Whamcloud - gitweb
LU-5435 lnet: LNet drop rule implementation
[fs/lustre-release.git] / lnet / lnet / net_fault.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA
20  *
21  * GPL HEADER END
22  */
23 /*
24  * Copyright (c) 2014, Intel Corporation.
25  */
26 /*
27  * This file is part of Lustre, http://www.lustre.org/
28  * Lustre is a trademark of Sun Microsystems, Inc.
29  *
30  * lnet/lnet/net_fault.c
31  *
32  * Lustre network fault simulation
33  *
34  * Author: liang.zhen@intel.com
35  */
36
37 #define DEBUG_SUBSYSTEM S_LNET
38
39 #include <lnet/lib-lnet.h>
40 #include <lnet/lnetctl.h>
41
42 #define LNET_MSG_MASK           (LNET_PUT_BIT | LNET_ACK_BIT | \
43                                  LNET_GET_BIT | LNET_REPLY_BIT)
44
45 struct lnet_drop_rule {
46         /** link chain on the_lnet.ln_drop_rules */
47         struct list_head        dr_link;
48         /** attributes of this rule */
49         struct lnet_fault_attr  dr_attr;
50         /** lock to protect \a dr_drop_at and \a dr_stat */
51         spinlock_t              dr_lock;
52         /**
53          * the message sequence to drop, which means message is dropped when
54          * dr_stat.drs_count == dr_drop_at
55          */
56         unsigned long           dr_drop_at;
57         /**
58          * seconds to drop the next message, it's exclusive with dr_drop_at
59          */
60         cfs_time_t              dr_drop_time;
61         /** baseline to caculate dr_drop_time */
62         cfs_time_t              dr_time_base;
63         /** statistic of dropped messages */
64         struct lnet_fault_stat  dr_stat;
65 };
66
67 static bool
68 lnet_fault_nid_match(lnet_nid_t nid, lnet_nid_t msg_nid)
69 {
70         if (nid == msg_nid || nid == LNET_NID_ANY)
71                 return true;
72
73         if (LNET_NIDNET(nid) != LNET_NIDNET(msg_nid))
74                 return false;
75
76         /* 255.255.255.255@net is wildcard for all addresses in a network */
77         return LNET_NIDADDR(nid) == LNET_NIDADDR(LNET_NID_ANY);
78 }
79
80 static bool
81 lnet_fault_attr_match(struct lnet_fault_attr *attr, lnet_nid_t src,
82                       lnet_nid_t dst, unsigned int type, unsigned int portal)
83 {
84         if (!lnet_fault_nid_match(attr->fa_src, src) ||
85             !lnet_fault_nid_match(attr->fa_dst, dst))
86                 return false;
87
88         if (!(attr->fa_msg_mask & (1 << type)))
89                 return false;
90
91         /* NB: ACK and REPLY have no portal, but they should have been
92          * rejected by message mask */
93         if (attr->fa_ptl_mask != 0 && /* has portal filter */
94             !(attr->fa_ptl_mask & (1ULL << portal)))
95                 return false;
96
97         return true;
98 }
99
100 static int
101 lnet_fault_attr_validate(struct lnet_fault_attr *attr)
102 {
103         if (attr->fa_msg_mask == 0)
104                 attr->fa_msg_mask = LNET_MSG_MASK; /* all message types */
105
106         if (attr->fa_ptl_mask == 0) /* no portal filter */
107                 return 0;
108
109         /* NB: only PUT and GET can be filtered if portal filter has been set */
110         attr->fa_msg_mask &= LNET_GET_BIT | LNET_PUT_BIT;
111         if (attr->fa_msg_mask == 0) {
112                 CDEBUG(D_NET, "can't find valid message type bits %x\n",
113                        attr->fa_msg_mask);
114                 return -EINVAL;
115         }
116         return 0;
117 }
118
119 static void
120 lnet_fault_stat_inc(struct lnet_fault_stat *stat, unsigned int type)
121 {
122         /* NB: fs_counter is NOT updated by this function */
123         switch (type) {
124         case LNET_MSG_PUT:
125                 stat->fs_put++;
126                 return;
127         case LNET_MSG_ACK:
128                 stat->fs_ack++;
129                 return;
130         case LNET_MSG_GET:
131                 stat->fs_get++;
132                 return;
133         case LNET_MSG_REPLY:
134                 stat->fs_reply++;
135                 return;
136         }
137 }
138
139 /**
140  * Add a new drop rule to LNet
141  * There is no check for duplicated drop rule, all rules will be checked for
142  * incoming message.
143  */
144 static int
145 lnet_drop_rule_add(struct lnet_fault_attr *attr)
146 {
147         struct lnet_drop_rule *rule;
148         ENTRY;
149
150         if ((attr->u.drop.da_rate == 0) == (attr->u.drop.da_interval == 0)) {
151                 CDEBUG(D_NET, "invalid rate %d or interval %d\n",
152                        attr->u.drop.da_rate, attr->u.drop.da_interval);
153                 RETURN(-EINVAL);
154         }
155
156         if (lnet_fault_attr_validate(attr) != 0)
157                 RETURN(-EINVAL);
158
159         CFS_ALLOC_PTR(rule);
160         if (rule == NULL)
161                 RETURN(-ENOMEM);
162
163         spin_lock_init(&rule->dr_lock);
164
165         rule->dr_attr = *attr;
166         if (attr->u.drop.da_interval != 0) {
167                 rule->dr_time_base = cfs_time_shift(attr->u.drop.da_interval);
168                 rule->dr_drop_time = cfs_time_shift(cfs_rand() %
169                                                     attr->u.drop.da_interval);
170         } else {
171                 rule->dr_drop_at = cfs_rand() % attr->u.drop.da_rate;
172         }
173
174         lnet_net_lock(LNET_LOCK_EX);
175         list_add(&rule->dr_link, &the_lnet.ln_drop_rules);
176         lnet_net_unlock(LNET_LOCK_EX);
177
178         CDEBUG(D_NET, "Added drop rule: src %s, dst %s, rate %d, interval %d\n",
179                libcfs_nid2str(attr->fa_src), libcfs_nid2str(attr->fa_src),
180                attr->u.drop.da_rate, attr->u.drop.da_interval);
181         RETURN(0);
182 }
183
184 /**
185  * Remove matched drop rules from lnet, all rules that can match \a src and
186  * \a dst will be removed.
187  * If \a src is zero, then all rules have \a dst as destination will be remove
188  * If \a dst is zero, then all rules have \a src as source will be removed
189  * If both of them are zero, all rules will be removed
190  */
191 static int
192 lnet_drop_rule_del(lnet_nid_t src, lnet_nid_t dst)
193 {
194         struct lnet_drop_rule *rule;
195         struct lnet_drop_rule *tmp;
196         struct list_head       zombies;
197         int                    n = 0;
198         ENTRY;
199
200         INIT_LIST_HEAD(&zombies);
201
202         lnet_net_lock(LNET_LOCK_EX);
203         list_for_each_entry_safe(rule, tmp, &the_lnet.ln_drop_rules, dr_link) {
204                 if (rule->dr_attr.fa_src != src && src != 0)
205                         continue;
206
207                 if (rule->dr_attr.fa_dst != dst && dst != 0)
208                         continue;
209
210                 list_move(&rule->dr_link, &zombies);
211         }
212         lnet_net_unlock(LNET_LOCK_EX);
213
214         list_for_each_entry_safe(rule, tmp, &zombies, dr_link) {
215                 CDEBUG(D_NET, "Remove drop rule: src %s->dst: %s (1/%d, %d)\n",
216                        libcfs_nid2str(rule->dr_attr.fa_src),
217                        libcfs_nid2str(rule->dr_attr.fa_dst),
218                        rule->dr_attr.u.drop.da_rate,
219                        rule->dr_attr.u.drop.da_interval);
220
221                 list_del(&rule->dr_link);
222                 CFS_FREE_PTR(rule);
223                 n++;
224         }
225
226         RETURN(n);
227 }
228
229 /**
230  * List drop rule at position of \a pos
231  */
232 static int
233 lnet_drop_rule_list(int pos, struct lnet_fault_attr *attr,
234                     struct lnet_fault_stat *stat)
235 {
236         struct lnet_drop_rule *rule;
237         int                    cpt;
238         int                    i = 0;
239         int                    rc = -ENOENT;
240         ENTRY;
241
242         cpt = lnet_net_lock_current();
243         list_for_each_entry(rule, &the_lnet.ln_drop_rules, dr_link) {
244                 if (i++ < pos)
245                         continue;
246
247                 spin_lock(&rule->dr_lock);
248                 *attr = rule->dr_attr;
249                 *stat = rule->dr_stat;
250                 spin_unlock(&rule->dr_lock);
251                 rc = 0;
252                 break;
253         }
254
255         lnet_net_unlock(cpt);
256         RETURN(rc);
257 }
258
259 /**
260  * reset counters for all drop rules
261  */
262 static void
263 lnet_drop_rule_reset(void)
264 {
265         struct lnet_drop_rule *rule;
266         int                    cpt;
267         ENTRY;
268
269         cpt = lnet_net_lock_current();
270
271         list_for_each_entry(rule, &the_lnet.ln_drop_rules, dr_link) {
272                 struct lnet_fault_attr *attr = &rule->dr_attr;
273
274                 spin_lock(&rule->dr_lock);
275
276                 memset(&rule->dr_stat, 0, sizeof(rule->dr_stat));
277                 if (attr->u.drop.da_rate != 0) {
278                         rule->dr_drop_at = cfs_rand() % attr->u.drop.da_rate;
279                 } else {
280                         rule->dr_drop_time = cfs_time_shift(cfs_rand() %
281                                                 attr->u.drop.da_interval);
282                         rule->dr_time_base = cfs_time_shift(attr->u.drop.
283                                                                   da_interval);
284                 }
285                 spin_unlock(&rule->dr_lock);
286         }
287
288         lnet_net_unlock(cpt);
289         EXIT;
290 }
291
292 /**
293  * check source/destination NID, portal, message type and drop rate,
294  * decide whether should drop this message or not
295  */
296 static bool
297 drop_rule_match(struct lnet_drop_rule *rule, lnet_nid_t src,
298                 lnet_nid_t dst, unsigned int type, unsigned int portal)
299 {
300         struct lnet_fault_attr  *attr = &rule->dr_attr;
301         bool                     drop;
302
303         if (!lnet_fault_attr_match(attr, src, dst, type, portal))
304                 return false;
305
306         /* match this rule, check drop rate now */
307         spin_lock(&rule->dr_lock);
308         if (rule->dr_drop_time != 0) { /* time based drop */
309                 cfs_time_t now = cfs_time_current();
310
311                 rule->dr_stat.fs_count++;
312                 drop = cfs_time_aftereq(now, rule->dr_drop_time);
313                 if (drop) {
314                         if (cfs_time_after(now, rule->dr_time_base))
315                                 rule->dr_time_base = now;
316
317                         rule->dr_drop_time = rule->dr_time_base +
318                                              cfs_time_seconds(cfs_rand() %
319                                                 attr->u.drop.da_interval);
320                         rule->dr_time_base += cfs_time_seconds(attr->u.drop.
321                                                                da_interval);
322
323                         CDEBUG(D_NET, "Drop Rule %s->%s: next drop : "
324                                       CFS_TIME_T"\n",
325                                       libcfs_nid2str(attr->fa_src),
326                                       libcfs_nid2str(attr->fa_dst),
327                                       rule->dr_drop_time);
328                 }
329
330         } else { /* rate based drop */
331                 drop = rule->dr_stat.fs_count++ == rule->dr_drop_at;
332
333                 if (rule->dr_stat.fs_count % attr->u.drop.da_rate == 0) {
334                         rule->dr_drop_at = rule->dr_stat.fs_count +
335                                            cfs_rand() % attr->u.drop.da_rate;
336                         CDEBUG(D_NET, "Drop Rule %s->%s: next drop: %lu\n",
337                                libcfs_nid2str(attr->fa_src),
338                                libcfs_nid2str(attr->fa_dst), rule->dr_drop_at);
339                 }
340         }
341
342         if (drop) { /* drop this message, update counters */
343                 lnet_fault_stat_inc(&rule->dr_stat, type);
344                 rule->dr_stat.u.drop.ds_dropped++;
345         }
346
347         spin_unlock(&rule->dr_lock);
348         return drop;
349 }
350
351 /**
352  * Check if message from \a src to \a dst can match any existed drop rule
353  */
354 bool
355 lnet_drop_rule_match(lnet_hdr_t *hdr)
356 {
357         struct lnet_drop_rule   *rule;
358         lnet_nid_t               src = le64_to_cpu(hdr->src_nid);
359         lnet_nid_t               dst = le64_to_cpu(hdr->dest_nid);
360         unsigned int             typ = le32_to_cpu(hdr->type);
361         unsigned int             ptl = -1;
362         bool                     drop = false;
363         int                      cpt;
364
365         /* NB: if Portal is specified, then only PUT and GET will be
366          * filtered by drop rule */
367         if (typ == LNET_MSG_PUT)
368                 ptl = le32_to_cpu(hdr->msg.put.ptl_index);
369         else if (typ == LNET_MSG_GET)
370                 ptl = le32_to_cpu(hdr->msg.get.ptl_index);
371
372         cpt = lnet_net_lock_current();
373         list_for_each_entry(rule, &the_lnet.ln_drop_rules, dr_link) {
374                 drop = drop_rule_match(rule, src, dst, typ, ptl);
375                 if (drop)
376                         break;
377         }
378
379         lnet_net_unlock(cpt);
380         return drop;
381 }
382
383 int
384 lnet_fault_ctl(int opc, struct libcfs_ioctl_data *data)
385 {
386         struct lnet_fault_attr *attr;
387         struct lnet_fault_stat *stat;
388
389         attr = (struct lnet_fault_attr *)data->ioc_inlbuf1;
390
391         switch (opc) {
392         default:
393                 return -EINVAL;
394
395         case LNET_CTL_DROP_ADD:
396                 if (attr == NULL)
397                         return -EINVAL;
398
399                 return lnet_drop_rule_add(attr);
400
401         case LNET_CTL_DROP_DEL:
402                 if (attr == NULL)
403                         return -EINVAL;
404
405                 data->ioc_count = lnet_drop_rule_del(attr->fa_src,
406                                                      attr->fa_dst);
407                 return 0;
408
409         case LNET_CTL_DROP_RESET:
410                 lnet_drop_rule_reset();
411                 return 0;
412
413         case LNET_CTL_DROP_LIST:
414                 stat = (struct lnet_fault_stat *)data->ioc_inlbuf2;
415                 if (attr == NULL || stat == NULL)
416                         return -EINVAL;
417
418                 return lnet_drop_rule_list(data->ioc_count, attr, stat);
419         }
420 }
421
422 int
423 lnet_fault_init(void)
424 {
425         CLASSERT(LNET_PUT_BIT == 1 << LNET_MSG_PUT);
426         CLASSERT(LNET_ACK_BIT == 1 << LNET_MSG_ACK);
427         CLASSERT(LNET_GET_BIT == 1 << LNET_MSG_GET);
428         CLASSERT(LNET_REPLY_BIT == 1 << LNET_MSG_REPLY);
429
430         return 0;
431 }
432
433 void
434 lnet_fault_fini(void)
435 {
436         lnet_drop_rule_del(0, 0);
437
438         LASSERT(list_empty(&the_lnet.ln_drop_rules));
439 }