Whamcloud - gitweb
LU-10391 lnet: discard some peer_ni lookup functions
[fs/lustre-release.git] / lnet / lnet / udsp.c
1 /*
2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Copyright (c) 2011, 2017, Intel Corporation.
5  *
6  * Copyright (c) 2018-2020 Data Direct Networks.
7  *
8  *   This file is part of Lustre, https://wiki.whamcloud.com/
9  *
10  *   Portals is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Portals is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   version 2 along with this program; If not, see
21  *   http://www.gnu.org/licenses/gpl-2.0.html
22  *
23  *   lnet/lnet/udsp.c
24  *
25  *   User Defined Selection Policies (UDSP) are introduced to add
26  *   ability of fine traffic control. The policies are instantiated
27  *   on LNet constructs and allow preference of some constructs
28  *   over others as an extension of the selection algorithm.
29  *   The order of operation is defined by the selection algorithm logical flow:
30  *
31  *   1. Iterate over all the networks that a peer can be reached on
32  *      and select the best local network
33  *      - The remote network with the highest priority is examined
34  *        (Network Rule)
35  *      - The local network with the highest priority is selected
36  *        (Network Rule)
37  *      - The local NI with the highest priority is selected
38  *        (NID Rule)
39  *   2. If the peer is a remote peer and has no local networks,
40  *      - then select the remote peer network with the highest priority
41  *        (Network Rule)
42  *      - Select the highest priority remote peer_ni on the network selected
43  *        (NID Rule)
44  *      - Now that the peer's network and NI are decided, select the router
45  *        in round robin from the peer NI's preferred router list.
46  *        (Router Rule)
47  *      - Select the highest priority local NI on the local net of the
48  *        selected route.
49  *        (NID Rule)
50  *   3. Otherwise for local peers, select the peer_ni from the peer.
51  *      - highest priority peer NI is selected
52  *        (NID Rule)
53  *      - Select the peer NI which has the local NI selected on its
54  *        preferred list.
55  *        (NID Pair Rule)
56  *
57  *   Accordingly, the User Interface allows for the following:
58  *   - Adding a local network udsp: if multiple local networks are
59  *     available, each one can have a priority.
60  *   - Adding a local NID udsp: after a local network is chosen,
61  *     if there are multiple NIs, each one can have a priority.
62  *   - Adding a remote NID udsp: assign priority to a peer NID.
63  *   - Adding a NID pair udsp: allows to specify local NIDs
64  *     to be added on the list on the specified peer NIs
65  *     When selecting a peer NI, the one with the
66  *     local NID being used on its list is preferred.
67  *   - Adding a Router udsp: similar to the NID pair udsp.
68  *     Specified router NIDs are added on the list on the specified peer NIs.
69  *     When sending to a remote peer, remote net is selected and the peer NID
70  *     is selected. The router which has its nid on the peer NI list
71  *     is preferred.
72  *   - Deleting a udsp: use the specified policy index to remove it
73  *     from the policy list.
74  *
75  *   Generally, the syntax is as follows
76  *     lnetctl policy <add | del | show>
77  *      --src:      ip2nets syntax specifying the local NID to match
78  *      --dst:      ip2nets syntax specifying the remote NID to match
79  *      --rte:      ip2nets syntax specifying the router NID to match
80  *      --priority: Priority to apply to rule matches
81  *      --idx:      Index of where to insert or delete the rule
82  *                  By default add appends to the end of the rule list
83  *
84  * Author: Amir Shehata
85  */
86
87 #include <linux/uaccess.h>
88
89 #include <lnet/udsp.h>
90 #include <libcfs/libcfs.h>
91
92 struct udsp_info {
93         struct lnet_peer_ni *udi_lpni;
94         struct lnet_peer_net *udi_lpn;
95         struct lnet_ni *udi_ni;
96         struct lnet_net *udi_net;
97         struct lnet_ud_nid_descr *udi_match;
98         struct lnet_ud_nid_descr *udi_action;
99         __u32 udi_priority;
100         enum lnet_udsp_action_type udi_type;
101         bool udi_local;
102         bool udi_revert;
103 };
104
105 typedef int (*udsp_apply_rule)(struct udsp_info *);
106
107 enum udsp_apply {
108         UDSP_APPLY_ON_PEERS = 0,
109         UDSP_APPLY_PRIO_ON_NIS = 1,
110         UDSP_APPLY_RTE_ON_NETS = 2,
111         UDSP_APPLY_MAX_ENUM = 3,
112 };
113
114 #define RULE_NOT_APPLICABLE -1
115
116 static inline bool
117 lnet_udsp_is_net_rule(struct lnet_ud_nid_descr *match)
118 {
119         return list_empty(&match->ud_addr_range);
120 }
121
122 static bool
123 lnet_udsp_expr_list_equal(struct list_head *e1,
124                           struct list_head *e2)
125 {
126         struct cfs_expr_list *expr1;
127         struct cfs_expr_list *expr2;
128         struct cfs_range_expr *range1, *range2;
129
130         if (list_empty(e1) && list_empty(e2))
131                 return true;
132
133         if (lnet_get_list_len(e1) != lnet_get_list_len(e2))
134                 return false;
135
136         expr2 = list_first_entry(e2, struct cfs_expr_list, el_link);
137
138         list_for_each_entry(expr1, e1, el_link) {
139                 if (lnet_get_list_len(&expr1->el_exprs) !=
140                     lnet_get_list_len(&expr2->el_exprs))
141                         return false;
142
143                 range2 = list_first_entry(&expr2->el_exprs,
144                                           struct cfs_range_expr,
145                                           re_link);
146
147                 list_for_each_entry(range1, &expr1->el_exprs, re_link) {
148                         if (range1->re_lo != range2->re_lo ||
149                             range1->re_hi != range2->re_hi ||
150                             range1->re_stride != range2->re_stride)
151                                 return false;
152                         range2 = list_next_entry(range2, re_link);
153                 }
154                 expr2 = list_next_entry(expr2, el_link);
155         }
156
157         return true;
158 }
159
160 static bool
161 lnet_udsp_nid_descr_equal(struct lnet_ud_nid_descr *e1,
162                           struct lnet_ud_nid_descr *e2)
163 {
164         if (e1->ud_net_id.udn_net_type != e2->ud_net_id.udn_net_type ||
165             !lnet_udsp_expr_list_equal(&e1->ud_net_id.udn_net_num_range,
166                                        &e2->ud_net_id.udn_net_num_range) ||
167             !lnet_udsp_expr_list_equal(&e1->ud_addr_range, &e2->ud_addr_range))
168                 return false;
169
170         return true;
171 }
172
173 static bool
174 lnet_udsp_action_equal(struct lnet_udsp *e1, struct lnet_udsp *e2)
175 {
176         if (e1->udsp_action_type != e2->udsp_action_type)
177                 return false;
178
179         if (e1->udsp_action_type == EN_LNET_UDSP_ACTION_PRIORITY &&
180             e1->udsp_action.udsp_priority != e2->udsp_action.udsp_priority)
181                 return false;
182
183         return true;
184 }
185
186 static bool
187 lnet_udsp_equal(struct lnet_udsp *e1, struct lnet_udsp *e2)
188 {
189         /* check each NID descr */
190         if (!lnet_udsp_nid_descr_equal(&e1->udsp_src, &e2->udsp_src) ||
191             !lnet_udsp_nid_descr_equal(&e1->udsp_dst, &e2->udsp_dst) ||
192             !lnet_udsp_nid_descr_equal(&e1->udsp_rte, &e2->udsp_rte))
193                 return false;
194
195         return true;
196 }
197
198 /* it is enough to look at the net type of the descriptor. If the criteria
199  * is present the net must be specified
200  */
201 static inline bool
202 lnet_udsp_criteria_present(struct lnet_ud_nid_descr *descr)
203 {
204         return (descr->ud_net_id.udn_net_type != 0);
205 }
206
207 static int
208 lnet_udsp_apply_rule_on_ni(struct udsp_info *udi)
209 {
210         int rc;
211         struct lnet_ni *ni = udi->udi_ni;
212         struct lnet_ud_nid_descr *ni_match = udi->udi_match;
213         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
214
215         rc = cfs_match_nid_net(
216                 &ni->ni_nid,
217                 ni_match->ud_net_id.udn_net_type,
218                 &ni_match->ud_net_id.udn_net_num_range,
219                 &ni_match->ud_addr_range);
220         if (!rc)
221                 return 0;
222
223         CDEBUG(D_NET, "apply udsp on ni %s\n",
224                libcfs_nidstr(&ni->ni_nid));
225
226         /* Detected match. Set NIDs priority */
227         lnet_ni_set_sel_priority_locked(ni, priority);
228
229         return 0;
230 }
231
232 static int
233 lnet_udsp_apply_rte_list_on_net(struct lnet_net *net,
234                                 struct lnet_ud_nid_descr *rte_action,
235                                 bool revert)
236 {
237         struct lnet_remotenet *rnet;
238         struct list_head *rn_list;
239         struct lnet_route *route;
240         struct lnet_peer_ni *lpni;
241         bool cleared = false;
242         struct lnet_nid *gw_nid, *gw_prim_nid;
243         int rc = 0;
244         int i;
245
246         for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
247                 rn_list = &the_lnet.ln_remote_nets_hash[i];
248                 list_for_each_entry(rnet, rn_list, lrn_list) {
249                         list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
250                                 /* look if gw nid on the same net matches */
251                                 gw_prim_nid =
252                                         &route->lr_gateway->lp_primary_nid;
253                                 lpni = NULL;
254                                 while ((lpni = lnet_get_next_peer_ni_locked(route->lr_gateway,
255                                                                             NULL,
256                                                                             lpni)) != NULL) {
257                                         if (!lnet_get_net_locked(lpni->lpni_peer_net->lpn_net_id))
258                                                 continue;
259                                         gw_nid = &lpni->lpni_nid;
260                                         rc = cfs_match_nid_net(
261                                                 gw_nid,
262                                                 rte_action->ud_net_id.udn_net_type,
263                                                 &rte_action->ud_net_id.udn_net_num_range,
264                                                 &rte_action->ud_addr_range);
265                                         if (rc)
266                                                 break;
267                                 }
268                                 /* match gw primary nid on a remote network */
269                                 if (!rc) {
270                                         gw_nid = gw_prim_nid;
271                                         rc = cfs_match_nid_net(
272                                                 gw_nid,
273                                                 rte_action->ud_net_id.udn_net_type,
274                                                 &rte_action->ud_net_id.udn_net_num_range,
275                                                 &rte_action->ud_addr_range);
276                                 }
277                                 if (!rc)
278                                         continue;
279                                 lnet_net_unlock(LNET_LOCK_EX);
280                                 if (!cleared || revert) {
281                                         lnet_net_clr_pref_rtrs(net);
282                                         cleared = true;
283                                         if (revert) {
284                                                 lnet_net_lock(LNET_LOCK_EX);
285                                                 continue;
286                                         }
287                                 }
288                                 /* match. Add to pref NIDs */
289                                 CDEBUG(D_NET, "udsp net->gw: %s->%s\n",
290                                        libcfs_net2str(net->net_id),
291                                        libcfs_nidstr(gw_prim_nid));
292                                 rc = lnet_net_add_pref_rtr(net, gw_prim_nid);
293                                 lnet_net_lock(LNET_LOCK_EX);
294                                 /* success if EEXIST return */
295                                 if (rc && rc != -EEXIST) {
296                                         CERROR("Failed to add %s to %s pref rtr list\n",
297                                                libcfs_nidstr(gw_prim_nid),
298                                                libcfs_net2str(net->net_id));
299                                         return rc;
300                                 }
301                         }
302                 }
303         }
304
305         return rc;
306 }
307
308 static int
309 lnet_udsp_apply_rte_rule_on_nets(struct udsp_info *udi)
310 {
311         int rc = 0;
312         int last_failure = 0;
313         struct lnet_net *net;
314         struct lnet_ud_nid_descr *match = udi->udi_match;
315         struct lnet_ud_nid_descr *rte_action = udi->udi_action;
316
317         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
318                 if (LNET_NETTYP(net->net_id) != match->ud_net_id.udn_net_type)
319                         continue;
320
321                 rc = cfs_match_net(net->net_id,
322                                    match->ud_net_id.udn_net_type,
323                                    &match->ud_net_id.udn_net_num_range);
324                 if (!rc)
325                         continue;
326
327                 CDEBUG(D_NET, "apply rule on %s\n",
328                        libcfs_net2str(net->net_id));
329                 rc = lnet_udsp_apply_rte_list_on_net(net, rte_action,
330                                                      udi->udi_revert);
331                 if (rc)
332                         last_failure = rc;
333         }
334
335         return last_failure;
336 }
337
338 static int
339 lnet_udsp_apply_rte_rule_on_net(struct udsp_info *udi)
340 {
341         int rc = 0;
342         struct lnet_net *net = udi->udi_net;
343         struct lnet_ud_nid_descr *match = udi->udi_match;
344         struct lnet_ud_nid_descr *rte_action = udi->udi_action;
345
346         rc = cfs_match_net(net->net_id,
347                            match->ud_net_id.udn_net_type,
348                            &match->ud_net_id.udn_net_num_range);
349         if (!rc)
350                 return 0;
351
352         CDEBUG(D_NET, "apply rule on %s\n",
353                 libcfs_net2str(net->net_id));
354         rc = lnet_udsp_apply_rte_list_on_net(net, rte_action,
355                                              udi->udi_revert);
356
357         return rc;
358 }
359
360 static int
361 lnet_udsp_apply_prio_rule_on_net(struct udsp_info *udi)
362 {
363         int rc;
364         struct lnet_ud_nid_descr *match = udi->udi_match;
365         struct lnet_net *net = udi->udi_net;
366         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
367
368         if (!lnet_udsp_is_net_rule(match))
369                 return RULE_NOT_APPLICABLE;
370
371         rc = cfs_match_net(net->net_id,
372                            match->ud_net_id.udn_net_type,
373                            &match->ud_net_id.udn_net_num_range);
374         if (!rc)
375                 return 0;
376
377         CDEBUG(D_NET, "apply rule on %s\n",
378                libcfs_net2str(net->net_id));
379
380         lnet_net_set_sel_priority_locked(net, priority);
381
382         return 0;
383 }
384
385 static int
386 lnet_udsp_apply_rule_on_nis(struct udsp_info *udi)
387 {
388         int rc = 0;
389         struct lnet_ni *ni;
390         struct lnet_net *net;
391         struct lnet_ud_nid_descr *ni_match = udi->udi_match;
392         int last_failure = 0;
393
394         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
395                 if (LNET_NETTYP(net->net_id) != ni_match->ud_net_id.udn_net_type)
396                         continue;
397
398                 udi->udi_net = net;
399                 if (!lnet_udsp_apply_prio_rule_on_net(udi))
400                         continue;
401
402                 list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
403                         udi->udi_ni = ni;
404                         rc = lnet_udsp_apply_rule_on_ni(udi);
405                         if (rc)
406                                 last_failure = rc;
407                 }
408         }
409
410         return last_failure;
411 }
412
413 static int
414 lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
415                                  struct lnet_ud_nid_descr *rte_action,
416                                  bool revert)
417 {
418         struct lnet_remotenet *rnet;
419         struct list_head *rn_list;
420         struct lnet_route *route;
421         bool cleared = false;
422         struct lnet_nid *gw_nid;
423         int rc = 0;
424         int i;
425
426         for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
427                 rn_list = &the_lnet.ln_remote_nets_hash[i];
428                 list_for_each_entry(rnet, rn_list, lrn_list) {
429                         list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
430                                 gw_nid = &route->lr_gateway->lp_primary_nid;
431                                 rc = cfs_match_nid_net(
432                                         gw_nid,
433                                         rte_action->ud_net_id.udn_net_type,
434                                         &rte_action->ud_net_id.udn_net_num_range,
435                                         &rte_action->ud_addr_range);
436                                 if (!rc)
437                                         continue;
438                                 lnet_net_unlock(LNET_LOCK_EX);
439                                 if (!cleared || revert) {
440                                         CDEBUG(D_NET, "%spref rtr nids from lpni %s\n",
441                                                (revert) ? "revert " : "clear ",
442                                                libcfs_nidstr(&lpni->lpni_nid));
443                                         lnet_peer_clr_pref_rtrs(lpni);
444                                         cleared = true;
445                                         if (revert) {
446                                                 lnet_net_lock(LNET_LOCK_EX);
447                                                 continue;
448                                         }
449                                 }
450                                 CDEBUG(D_NET, "add gw nid %s as preferred for peer %s\n",
451                                        libcfs_nidstr(gw_nid),
452                                        libcfs_nidstr(&lpni->lpni_nid));
453                                 /* match. Add to pref NIDs */
454                                 rc = lnet_peer_add_pref_rtr(lpni, gw_nid);
455                                 lnet_net_lock(LNET_LOCK_EX);
456                                 /* success if EEXIST return */
457                                 if (rc && rc != -EEXIST) {
458                                         CERROR("Failed to add %s to %s pref rtr list\n",
459                                                libcfs_nidstr(gw_nid),
460                                                libcfs_nidstr(&lpni->lpni_nid));
461                                         return rc;
462                                 }
463                         }
464                 }
465         }
466
467         return rc;
468 }
469
470 static int
471 lnet_udsp_apply_ni_list(struct lnet_peer_ni *lpni,
472                         struct lnet_ud_nid_descr *ni_action,
473                         bool revert)
474 {
475         int rc = 0;
476         struct lnet_ni *ni;
477         struct lnet_net *net;
478         bool cleared = false;
479
480         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
481                 if (LNET_NETTYP(net->net_id) != ni_action->ud_net_id.udn_net_type)
482                         continue;
483                 list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
484                         rc = cfs_match_nid_net(
485                                 &ni->ni_nid,
486                                 ni_action->ud_net_id.udn_net_type,
487                                 &ni_action->ud_net_id.udn_net_num_range,
488                                 &ni_action->ud_addr_range);
489                         if (!rc)
490                                 continue;
491                         lnet_net_unlock(LNET_LOCK_EX);
492                         if (!cleared || revert) {
493                                 lnet_peer_clr_pref_nids(lpni);
494                                 CDEBUG(D_NET, "%spref nids from lpni %s\n",
495                                         (revert) ? "revert " : "clear ",
496                                         libcfs_nidstr(&lpni->lpni_nid));
497                                 cleared = true;
498                                 if (revert) {
499                                         lnet_net_lock(LNET_LOCK_EX);
500                                         continue;
501                                 }
502                         }
503                         CDEBUG(D_NET, "add nid %s as preferred for peer %s\n",
504                                 libcfs_nidstr(&ni->ni_nid),
505                                 libcfs_nidstr(&lpni->lpni_nid));
506                         /* match. Add to pref NIDs */
507                         rc = lnet_peer_add_pref_nid(lpni, &ni->ni_nid);
508                         lnet_net_lock(LNET_LOCK_EX);
509                         /* success if EEXIST return */
510                         if (rc && rc != -EEXIST) {
511                                 CERROR("Failed to add %s to %s pref nid list\n",
512                                         libcfs_nidstr(&ni->ni_nid),
513                                         libcfs_nidstr(&lpni->lpni_nid));
514                                 return rc;
515                         }
516                 }
517         }
518
519         return rc;
520 }
521
522 static int
523 lnet_udsp_apply_rule_on_lpni(struct udsp_info *udi)
524 {
525         int rc;
526         struct lnet_peer_ni *lpni = udi->udi_lpni;
527         struct lnet_ud_nid_descr *lp_match = udi->udi_match;
528         struct lnet_ud_nid_descr *action = udi->udi_action;
529         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
530         bool local = udi->udi_local;
531         enum lnet_udsp_action_type type = udi->udi_type;
532
533         rc = cfs_match_nid_net(
534                 &lpni->lpni_nid,
535                 lp_match->ud_net_id.udn_net_type,
536                 &lp_match->ud_net_id.udn_net_num_range,
537                 &lp_match->ud_addr_range);
538
539         /* check if looking for a net match */
540         if (!rc &&
541             (lnet_get_list_len(&lp_match->ud_addr_range) ||
542              !cfs_match_net(udi->udi_lpn->lpn_net_id,
543                            lp_match->ud_net_id.udn_net_type,
544                            &lp_match->ud_net_id.udn_net_num_range))) {
545                 return 0;
546         }
547
548         if (type == EN_LNET_UDSP_ACTION_PREFERRED_LIST && local) {
549                 rc = lnet_udsp_apply_ni_list(lpni, action,
550                                              udi->udi_revert);
551                 if (rc)
552                         return rc;
553         } else if (type == EN_LNET_UDSP_ACTION_PREFERRED_LIST &&
554                         !local) {
555                 rc = lnet_udsp_apply_rte_list_on_lpni(lpni, action,
556                                                       udi->udi_revert);
557                 if (rc)
558                         return rc;
559         } else {
560                 lnet_peer_ni_set_selection_priority(lpni, priority);
561         }
562
563         return 0;
564 }
565
566 static int
567 lnet_udsp_apply_rule_on_lpn(struct udsp_info *udi)
568 {
569         int rc;
570         struct lnet_ud_nid_descr *match = udi->udi_match;
571         struct lnet_peer_net *lpn = udi->udi_lpn;
572         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
573
574         if (udi->udi_type == EN_LNET_UDSP_ACTION_PREFERRED_LIST ||
575             !lnet_udsp_is_net_rule(match))
576                 return RULE_NOT_APPLICABLE;
577
578         rc = cfs_match_net(lpn->lpn_net_id,
579                         match->ud_net_id.udn_net_type,
580                         &match->ud_net_id.udn_net_num_range);
581         if (!rc)
582                 return 0;
583
584         CDEBUG(D_NET, "apply rule on lpn %s\n",
585                libcfs_net2str(lpn->lpn_net_id));
586         lnet_peer_net_set_sel_priority_locked(lpn, priority);
587
588         return 0;
589 }
590
591 static int
592 lnet_udsp_apply_rule_on_lpnis(struct udsp_info *udi)
593 {
594         /* iterate over all the peers in the system and find if any of the
595          * peers match the criteria. If they do, clear the preferred list
596          * and add the new list
597          */
598         int lncpt = cfs_percpt_number(the_lnet.ln_peer_tables);
599         struct lnet_ud_nid_descr *lp_match = udi->udi_match;
600         struct lnet_peer_table *ptable;
601         struct lnet_peer_net *lpn;
602         struct lnet_peer_ni *lpni;
603         struct lnet_peer *lp;
604         int last_failure = 0;
605         int cpt;
606         int rc;
607
608         for (cpt = 0; cpt < lncpt; cpt++) {
609                 ptable = the_lnet.ln_peer_tables[cpt];
610                 list_for_each_entry(lp, &ptable->pt_peer_list, lp_peer_list) {
611                         CDEBUG(D_NET, "udsp examining lp %s\n",
612                                libcfs_nidstr(&lp->lp_primary_nid));
613                         list_for_each_entry(lpn,
614                                             &lp->lp_peer_nets,
615                                             lpn_peer_nets) {
616                                 CDEBUG(D_NET, "udsp examining lpn %s\n",
617                                        libcfs_net2str(lpn->lpn_net_id));
618
619                                 if (LNET_NETTYP(lpn->lpn_net_id) !=
620                                     lp_match->ud_net_id.udn_net_type)
621                                         continue;
622
623                                 udi->udi_lpn = lpn;
624
625                                 if (!lnet_udsp_apply_rule_on_lpn(udi))
626                                         continue;
627
628                                 list_for_each_entry(lpni,
629                                                     &lpn->lpn_peer_nis,
630                                                     lpni_peer_nis) {
631                                         CDEBUG(D_NET, "udsp examining lpni %s\n",
632                                                libcfs_nidstr(&lpni->lpni_nid));
633                                         udi->udi_lpni = lpni;
634                                         rc = lnet_udsp_apply_rule_on_lpni(udi);
635                                         if (rc)
636                                                 last_failure = rc;
637                                 }
638                         }
639                 }
640         }
641
642         return last_failure;
643 }
644
645 static int
646 lnet_udsp_apply_single_policy(struct lnet_udsp *udsp, struct udsp_info *udi,
647                               udsp_apply_rule *cbs)
648 {
649         int rc;
650
651         if (lnet_udsp_criteria_present(&udsp->udsp_dst) &&
652             lnet_udsp_criteria_present(&udsp->udsp_src)) {
653                 /* NID Pair rule */
654                 if (!cbs[UDSP_APPLY_ON_PEERS])
655                         return 0;
656
657                 if (udsp->udsp_action_type !=
658                         EN_LNET_UDSP_ACTION_PREFERRED_LIST) {
659                         CERROR("Bad action type. Expected %d got %d\n",
660                                 EN_LNET_UDSP_ACTION_PREFERRED_LIST,
661                                 udsp->udsp_action_type);
662                         return 0;
663                 }
664                 udi->udi_match = &udsp->udsp_dst;
665                 udi->udi_action = &udsp->udsp_src;
666                 udi->udi_type = EN_LNET_UDSP_ACTION_PREFERRED_LIST;
667                 udi->udi_local = true;
668
669                 CDEBUG(D_NET, "applying udsp (%p) dst->src\n",
670                         udsp);
671                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
672                 if (rc)
673                         return rc;
674         } else if (lnet_udsp_criteria_present(&udsp->udsp_dst) &&
675                    lnet_udsp_criteria_present(&udsp->udsp_rte)) {
676                 /* Router rule */
677                 if (!cbs[UDSP_APPLY_ON_PEERS])
678                         return 0;
679
680                 if (udsp->udsp_action_type !=
681                         EN_LNET_UDSP_ACTION_PREFERRED_LIST) {
682                         CERROR("Bad action type. Expected %d got %d\n",
683                                 EN_LNET_UDSP_ACTION_PREFERRED_LIST,
684                                 udsp->udsp_action_type);
685                         return 0;
686                 }
687
688                 if (lnet_udsp_criteria_present(&udsp->udsp_src)) {
689                         CERROR("only one of src or dst can be specified\n");
690                         return 0;
691                 }
692                 udi->udi_match = &udsp->udsp_dst;
693                 udi->udi_action = &udsp->udsp_rte;
694                 udi->udi_type = EN_LNET_UDSP_ACTION_PREFERRED_LIST;
695                 udi->udi_local = false;
696
697                 CDEBUG(D_NET, "applying udsp (%p) dst->rte\n",
698                         udsp);
699                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
700                 if (rc)
701                         return rc;
702         } else if (lnet_udsp_criteria_present(&udsp->udsp_dst)) {
703                 /* destination priority rule */
704                 if (!cbs[UDSP_APPLY_ON_PEERS])
705                         return 0;
706
707                 if (udsp->udsp_action_type !=
708                         EN_LNET_UDSP_ACTION_PRIORITY) {
709                         CERROR("Bad action type. Expected %d got %d\n",
710                                 EN_LNET_UDSP_ACTION_PRIORITY,
711                                 udsp->udsp_action_type);
712                         return 0;
713                 }
714                 udi->udi_match = &udsp->udsp_dst;
715                 udi->udi_type = EN_LNET_UDSP_ACTION_PRIORITY;
716                 if (udsp->udsp_action_type !=
717                     EN_LNET_UDSP_ACTION_PRIORITY) {
718                         udi->udi_priority = 0;
719                 } else {
720                         udi->udi_priority = udsp->udsp_action.udsp_priority;
721                 }
722                 udi->udi_local = true;
723
724                 CDEBUG(D_NET, "applying udsp (%p) on destination\n",
725                         udsp);
726                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
727                 if (rc)
728                         return rc;
729         } else if (lnet_udsp_criteria_present(&udsp->udsp_src)) {
730                 /* source priority rule */
731                 if (!cbs[UDSP_APPLY_PRIO_ON_NIS])
732                         return 0;
733
734                 if (udsp->udsp_action_type !=
735                         EN_LNET_UDSP_ACTION_PRIORITY) {
736                         CERROR("Bad action type. Expected %d got %d\n",
737                                 EN_LNET_UDSP_ACTION_PRIORITY,
738                                 udsp->udsp_action_type);
739                         return 0;
740                 }
741                 udi->udi_match = &udsp->udsp_src;
742                 udi->udi_type = EN_LNET_UDSP_ACTION_PRIORITY;
743                 if (udsp->udsp_action_type !=
744                     EN_LNET_UDSP_ACTION_PRIORITY) {
745                         udi->udi_priority = 0;
746                 } else {
747                         udi->udi_priority = udsp->udsp_action.udsp_priority;
748                 }
749                 udi->udi_local = true;
750
751                 CDEBUG(D_NET, "applying udsp (%p) on source\n",
752                         udsp);
753                 rc = cbs[UDSP_APPLY_PRIO_ON_NIS](udi);
754         } else {
755                 CERROR("Bad UDSP policy\n");
756                 return 0;
757         }
758
759         return 0;
760 }
761
762 static int
763 lnet_udsp_apply_policies_helper(struct lnet_udsp *udsp, struct udsp_info *udi,
764                                 udsp_apply_rule *cbs)
765 {
766         int rc;
767         int last_failure = 0;
768
769         if (udsp)
770                 return lnet_udsp_apply_single_policy(udsp, udi, cbs);
771
772         list_for_each_entry_reverse(udsp,
773                                     &the_lnet.ln_udsp_list,
774                                     udsp_on_list) {
775                 rc = lnet_udsp_apply_single_policy(udsp, udi, cbs);
776                 if (rc)
777                         last_failure = rc;
778         }
779
780         return last_failure;
781 }
782
783 int
784 lnet_udsp_apply_policies_on_ni(struct lnet_ni *ni)
785 {
786         struct udsp_info udi;
787         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
788
789         memset(&udi, 0, sizeof(udi));
790
791         udi.udi_ni = ni;
792
793         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_rule_on_ni;
794
795         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
796 }
797
798 int
799 lnet_udsp_apply_policies_on_net(struct lnet_net *net)
800 {
801         struct udsp_info udi;
802         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
803
804         memset(&udi, 0, sizeof(udi));
805
806         udi.udi_net = net;
807
808         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_prio_rule_on_net;
809         cbs[UDSP_APPLY_RTE_ON_NETS] = lnet_udsp_apply_rte_rule_on_net;
810
811         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
812 }
813
814 int
815 lnet_udsp_apply_policies_on_lpni(struct lnet_peer_ni *lpni)
816 {
817         struct udsp_info udi;
818         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
819
820         memset(&udi, 0, sizeof(udi));
821
822         udi.udi_lpni = lpni;
823
824         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpni;
825
826         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
827 }
828
829 int
830 lnet_udsp_apply_policies_on_lpn(struct lnet_peer_net *lpn)
831 {
832         struct udsp_info udi;
833         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
834
835         memset(&udi, 0, sizeof(udi));
836
837         udi.udi_lpn = lpn;
838
839         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpn;
840
841         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
842 }
843
844 int
845 lnet_udsp_apply_policies(struct lnet_udsp *udsp, bool revert)
846 {
847         int rc;
848         struct udsp_info udi;
849         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
850
851         memset(&udi, 0, sizeof(udi));
852
853         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpnis;
854         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_rule_on_nis;
855         cbs[UDSP_APPLY_RTE_ON_NETS] = lnet_udsp_apply_rte_rule_on_nets;
856
857         udi.udi_revert = revert;
858
859         lnet_net_lock(LNET_LOCK_EX);
860         rc = lnet_udsp_apply_policies_helper(udsp, &udi, cbs);
861         lnet_net_unlock(LNET_LOCK_EX);
862
863         return rc;
864 }
865
866 struct lnet_udsp *
867 lnet_udsp_get_policy(int idx)
868 {
869         int i = 0;
870         struct lnet_udsp *udsp = NULL;
871         bool found = false;
872
873         CDEBUG(D_NET, "Get UDSP at idx = %d\n", idx);
874
875         if (idx < 0)
876                 return NULL;
877
878         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list) {
879                 CDEBUG(D_NET, "iterating over upsp %d:%d:%d\n",
880                        udsp->udsp_idx, i, idx);
881                 if (i == idx) {
882                         found = true;
883                         break;
884                 }
885                 i++;
886         }
887
888         CDEBUG(D_NET, "Found UDSP (%p)\n", udsp);
889
890         if (!found)
891                 return NULL;
892
893         return udsp;
894 }
895
896 int
897 lnet_udsp_add_policy(struct lnet_udsp *new, int idx)
898 {
899         struct lnet_udsp *udsp;
900         struct lnet_udsp *insert = NULL;
901         int i = 0;
902
903         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list) {
904                 CDEBUG(D_NET, "found udsp i = %d:%d, idx = %d\n",
905                        i, udsp->udsp_idx, idx);
906                 if (i == idx) {
907                         insert = udsp;
908                         new->udsp_idx = idx;
909                 }
910                 i++;
911                 if (lnet_udsp_equal(udsp, new)) {
912                         if (!lnet_udsp_action_equal(udsp, new) &&
913                             udsp->udsp_action_type == EN_LNET_UDSP_ACTION_PRIORITY &&
914                             new->udsp_action_type == EN_LNET_UDSP_ACTION_PRIORITY) {
915                                 udsp->udsp_action.udsp_priority = new->udsp_action.udsp_priority;
916                                 CDEBUG(D_NET, "udsp: %p index %d updated priority to %d\n",
917                                        udsp,
918                                        udsp->udsp_idx,
919                                        udsp->udsp_action.udsp_priority);
920                                 return 0;
921                         }
922                         return -EALREADY;
923                 }
924         }
925
926         if (insert) {
927                 list_add(&new->udsp_on_list, insert->udsp_on_list.prev);
928                 i = 0;
929                 list_for_each_entry(udsp,
930                                     &the_lnet.ln_udsp_list,
931                                     udsp_on_list) {
932                         if (i <= idx) {
933                                 i++;
934                                 continue;
935                         }
936                         udsp->udsp_idx++;
937                 }
938         } else {
939                 list_add_tail(&new->udsp_on_list, &the_lnet.ln_udsp_list);
940                 new->udsp_idx = i;
941         }
942
943         CDEBUG(D_NET, "udsp: %p added at index %d\n", new, new->udsp_idx);
944
945         CDEBUG(D_NET, "udsp list:\n");
946         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list)
947                 CDEBUG(D_NET, "udsp %p:%d\n", udsp, udsp->udsp_idx);
948
949         return 0;
950 }
951
952 int
953 lnet_udsp_del_policy(int idx)
954 {
955         struct lnet_udsp *udsp;
956         struct lnet_udsp *tmp;
957         bool removed = false;
958
959         if (idx < 0) {
960                 lnet_udsp_destroy(false);
961                 return 0;
962         }
963
964         CDEBUG(D_NET, "del udsp at idx = %d\n", idx);
965
966         list_for_each_entry_safe(udsp,
967                                  tmp,
968                                  &the_lnet.ln_udsp_list,
969                                  udsp_on_list) {
970                 if (removed)
971                         udsp->udsp_idx--;
972                 if (udsp->udsp_idx == idx && !removed) {
973                         list_del_init(&udsp->udsp_on_list);
974                         lnet_udsp_apply_policies(udsp, true);
975                         lnet_udsp_free(udsp);
976                         removed = true;
977                 }
978         }
979
980         return 0;
981 }
982
983 static void
984 lnet_udsp_get_ni_info(struct lnet_ioctl_construct_udsp_info *info,
985                       struct lnet_ni *ni)
986 {
987         struct lnet_nid_list *ne;
988         struct lnet_net *net = ni->ni_net;
989         int i = 0;
990
991         LASSERT(ni);
992
993         info->cud_nid_priority = ni->ni_sel_priority;
994         if (net) {
995                 info->cud_net_priority = ni->ni_net->net_sel_priority;
996                 list_for_each_entry(ne, &net->net_rtr_pref_nids, nl_list) {
997                         if (i < LNET_MAX_SHOW_NUM_NID)
998                                 info->cud_pref_rtr_nid[i] =
999                                         lnet_nid_to_nid4(&ne->nl_nid);
1000                         else
1001                                 break;
1002                         i++;
1003                 }
1004         }
1005 }
1006
1007 static void
1008 lnet_udsp_get_peer_info(struct lnet_ioctl_construct_udsp_info *info,
1009                         struct lnet_peer_ni *lpni)
1010 {
1011         struct lnet_nid_list *ne;
1012         int i = 0;
1013
1014         /* peer tree structure needs to be in existence */
1015         LASSERT(lpni && lpni->lpni_peer_net &&
1016                 lpni->lpni_peer_net->lpn_peer);
1017
1018         info->cud_nid_priority = lpni->lpni_sel_priority;
1019         CDEBUG(D_NET, "lpni %s has %d pref nids\n",
1020                libcfs_nidstr(&lpni->lpni_nid),
1021                lpni->lpni_pref_nnids);
1022         if (lpni->lpni_pref_nnids == 1) {
1023                 info->cud_pref_nid[0] = lnet_nid_to_nid4(&lpni->lpni_pref.nid);
1024         } else if (lpni->lpni_pref_nnids > 1) {
1025                 struct list_head *list = &lpni->lpni_pref.nids;
1026
1027                 list_for_each_entry(ne, list, nl_list) {
1028                         if (i < LNET_MAX_SHOW_NUM_NID)
1029                                 info->cud_pref_nid[i] =
1030                                         lnet_nid_to_nid4(&ne->nl_nid);
1031                         else
1032                                 break;
1033                         i++;
1034                 }
1035         }
1036
1037         i = 0;
1038         list_for_each_entry(ne, &lpni->lpni_rtr_pref_nids, nl_list) {
1039                 if (i < LNET_MAX_SHOW_NUM_NID)
1040                         info->cud_pref_rtr_nid[i] =
1041                                 lnet_nid_to_nid4(&ne->nl_nid);
1042                 else
1043                         break;
1044                 i++;
1045         }
1046
1047         info->cud_net_priority = lpni->lpni_peer_net->lpn_sel_priority;
1048 }
1049
1050 void
1051 lnet_udsp_get_construct_info(struct lnet_ioctl_construct_udsp_info *info)
1052 {
1053         struct lnet_ni *ni;
1054         struct lnet_peer_ni *lpni;
1055         struct lnet_nid nid;
1056
1057         lnet_net_lock(0);
1058         lnet_nid4_to_nid(info->cud_nid, &nid);
1059         if (!info->cud_peer) {
1060                 ni = lnet_nid_to_ni_locked(&nid, 0);
1061                 if (ni)
1062                         lnet_udsp_get_ni_info(info, ni);
1063         } else {
1064                 lpni = lnet_peer_ni_find_locked(&nid);
1065                 if (!lpni) {
1066                         CDEBUG(D_NET, "nid %s is not found\n",
1067                                libcfs_nidstr(&nid));
1068                 } else {
1069                         lnet_udsp_get_peer_info(info, lpni);
1070                         lnet_peer_ni_decref_locked(lpni);
1071                 }
1072         }
1073         lnet_net_unlock(0);
1074 }
1075
1076 struct lnet_udsp *
1077 lnet_udsp_alloc(void)
1078 {
1079         struct lnet_udsp *udsp;
1080
1081         udsp = kmem_cache_alloc(lnet_udsp_cachep, GFP_NOFS | __GFP_ZERO);
1082
1083         if (!udsp)
1084                 return NULL;
1085
1086         INIT_LIST_HEAD(&udsp->udsp_on_list);
1087         INIT_LIST_HEAD(&udsp->udsp_src.ud_addr_range);
1088         INIT_LIST_HEAD(&udsp->udsp_src.ud_net_id.udn_net_num_range);
1089         INIT_LIST_HEAD(&udsp->udsp_dst.ud_addr_range);
1090         INIT_LIST_HEAD(&udsp->udsp_dst.ud_net_id.udn_net_num_range);
1091         INIT_LIST_HEAD(&udsp->udsp_rte.ud_addr_range);
1092         INIT_LIST_HEAD(&udsp->udsp_rte.ud_net_id.udn_net_num_range);
1093
1094         CDEBUG(D_MALLOC, "udsp alloc %p\n", udsp);
1095         return udsp;
1096 }
1097
1098 static void
1099 lnet_udsp_nid_descr_free(struct lnet_ud_nid_descr *nid_descr)
1100 {
1101         struct list_head *net_range = &nid_descr->ud_net_id.udn_net_num_range;
1102
1103         if (!lnet_udsp_criteria_present(nid_descr))
1104                 return;
1105
1106         /* memory management is a bit tricky here. When we allocate the
1107          * memory to store the NID descriptor we allocate a large buffer
1108          * for all the data, so we need to free the entire buffer at
1109          * once. If the net is present the net_range->next points to that
1110          * buffer otherwise if the ud_addr_range is present then it's the
1111          * ud_addr_range.next
1112          */
1113         if (!list_empty(net_range))
1114                 LIBCFS_FREE(net_range->next, nid_descr->ud_mem_size);
1115         else if (!list_empty(&nid_descr->ud_addr_range))
1116                 LIBCFS_FREE(nid_descr->ud_addr_range.next,
1117                             nid_descr->ud_mem_size);
1118 }
1119
1120 void
1121 lnet_udsp_free(struct lnet_udsp *udsp)
1122 {
1123         lnet_udsp_nid_descr_free(&udsp->udsp_src);
1124         lnet_udsp_nid_descr_free(&udsp->udsp_dst);
1125         lnet_udsp_nid_descr_free(&udsp->udsp_rte);
1126
1127         CDEBUG(D_MALLOC, "udsp free %p\n", udsp);
1128         kmem_cache_free(lnet_udsp_cachep, udsp);
1129 }
1130
1131 void
1132 lnet_udsp_destroy(bool shutdown)
1133 {
1134         struct lnet_udsp *udsp, *tmp;
1135
1136         CDEBUG(D_NET, "Destroying UDSPs in the system\n");
1137
1138         list_for_each_entry_safe(udsp, tmp, &the_lnet.ln_udsp_list,
1139                                  udsp_on_list) {
1140                 list_del(&udsp->udsp_on_list);
1141                 if (!shutdown)
1142                         lnet_udsp_apply_policies(udsp, true);
1143                 lnet_udsp_free(udsp);
1144         }
1145 }
1146
1147 static size_t
1148 lnet_size_marshaled_nid_descr(struct lnet_ud_nid_descr *descr)
1149 {
1150         struct cfs_expr_list *expr;
1151         int expr_count = 0;
1152         int range_count = 0;
1153         size_t size = sizeof(struct lnet_ioctl_udsp_descr);
1154
1155         if (!lnet_udsp_criteria_present(descr))
1156                 return size;
1157
1158         /* we always have one net expression */
1159         if (!list_empty(&descr->ud_net_id.udn_net_num_range)) {
1160                 expr = list_first_entry(&descr->ud_net_id.udn_net_num_range,
1161                                         struct cfs_expr_list, el_link);
1162
1163                 /* count the number of cfs_range_expr in the net expression */
1164                 range_count = lnet_get_list_len(&expr->el_exprs);
1165         }
1166
1167         /* count the number of cfs_range_expr in the address expressions */
1168         list_for_each_entry(expr, &descr->ud_addr_range, el_link) {
1169                 expr_count++;
1170                 range_count += lnet_get_list_len(&expr->el_exprs);
1171         }
1172
1173         size += (sizeof(struct lnet_expressions) * expr_count);
1174         size += (sizeof(struct lnet_range_expr) * range_count);
1175
1176         return size;
1177 }
1178
1179 size_t
1180 lnet_get_udsp_size(struct lnet_udsp *udsp)
1181 {
1182         size_t size = sizeof(struct lnet_ioctl_udsp);
1183
1184         size += lnet_size_marshaled_nid_descr(&udsp->udsp_src);
1185         size += lnet_size_marshaled_nid_descr(&udsp->udsp_dst);
1186         size += lnet_size_marshaled_nid_descr(&udsp->udsp_rte);
1187
1188         CDEBUG(D_NET, "get udsp (%p) size: %d\n", udsp, (int)size);
1189
1190         return size;
1191 }
1192
1193 static int
1194 copy_exprs(struct cfs_expr_list *expr, void __user **bulk,
1195            __u32 *bulk_size)
1196 {
1197         struct cfs_range_expr *range;
1198         struct lnet_range_expr range_expr;
1199
1200         /* copy over the net range expressions to the bulk */
1201         list_for_each_entry(range, &expr->el_exprs, re_link) {
1202                 range_expr.re_lo = range->re_lo;
1203                 range_expr.re_hi = range->re_hi;
1204                 range_expr.re_stride = range->re_stride;
1205                 CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
1206                        range_expr.re_lo, range_expr.re_hi,
1207                        range_expr.re_stride);
1208                 if (copy_to_user(*bulk, &range_expr, sizeof(range_expr))) {
1209                         CDEBUG(D_NET, "Failed to copy range_expr\n");
1210                         return -EFAULT;
1211                 }
1212                 *bulk += sizeof(range_expr);
1213                 *bulk_size -= sizeof(range_expr);
1214         }
1215
1216         return 0;
1217 }
1218
1219 static int
1220 copy_nid_range(struct lnet_ud_nid_descr *nid_descr, char *type,
1221                 void __user **bulk, __u32 *bulk_size)
1222 {
1223         struct lnet_ioctl_udsp_descr ioc_udsp_descr;
1224         struct cfs_expr_list *expr;
1225         struct lnet_expressions ioc_expr;
1226         int expr_count;
1227         int net_expr_count;
1228         int rc;
1229
1230         memset(&ioc_udsp_descr, 0, sizeof(ioc_udsp_descr));
1231         ioc_udsp_descr.iud_src_hdr.ud_descr_type = *(__u32 *)type;
1232
1233         /* if criteria not present, copy over the static part of the NID
1234          * descriptor
1235          */
1236         if (!lnet_udsp_criteria_present(nid_descr)) {
1237                 CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
1238                        ioc_udsp_descr.iud_src_hdr.ud_descr_type,
1239                        ioc_udsp_descr.iud_src_hdr.ud_descr_count,
1240                        ioc_udsp_descr.iud_net.ud_net_type,
1241                        ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
1242                 if (copy_to_user(*bulk, &ioc_udsp_descr,
1243                                  sizeof(ioc_udsp_descr))) {
1244                         CDEBUG(D_NET, "failed to copy ioc_udsp_descr\n");
1245                         return -EFAULT;
1246                 }
1247                 *bulk += sizeof(ioc_udsp_descr);
1248                 *bulk_size -= sizeof(ioc_udsp_descr);
1249                 return 0;
1250         }
1251
1252         expr_count = lnet_get_list_len(&nid_descr->ud_addr_range);
1253
1254         /* copy the net information */
1255         if (!list_empty(&nid_descr->ud_net_id.udn_net_num_range)) {
1256                 expr = list_first_entry(&nid_descr->ud_net_id.udn_net_num_range,
1257                                         struct cfs_expr_list, el_link);
1258                 net_expr_count = lnet_get_list_len(&expr->el_exprs);
1259         } else {
1260                 net_expr_count = 0;
1261         }
1262
1263         /* set the total expression count */
1264         ioc_udsp_descr.iud_src_hdr.ud_descr_count = expr_count;
1265         ioc_udsp_descr.iud_net.ud_net_type =
1266                 nid_descr->ud_net_id.udn_net_type;
1267         ioc_udsp_descr.iud_net.ud_net_num_expr.le_count = net_expr_count;
1268
1269         CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
1270                 ioc_udsp_descr.iud_src_hdr.ud_descr_type,
1271                 ioc_udsp_descr.iud_src_hdr.ud_descr_count,
1272                 ioc_udsp_descr.iud_net.ud_net_type,
1273                 ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
1274
1275         /* copy over the header info to the bulk */
1276         if (copy_to_user(*bulk, &ioc_udsp_descr, sizeof(ioc_udsp_descr))) {
1277                 CDEBUG(D_NET, "Failed to copy data\n");
1278                 return -EFAULT;
1279         }
1280         *bulk += sizeof(ioc_udsp_descr);
1281         *bulk_size -= sizeof(ioc_udsp_descr);
1282
1283         /* copy over the net num expression if it exists */
1284         if (net_expr_count) {
1285                 rc = copy_exprs(expr, bulk, bulk_size);
1286                 if (rc)
1287                         return rc;
1288         }
1289
1290         /* copy the address range */
1291         list_for_each_entry(expr, &nid_descr->ud_addr_range, el_link) {
1292                 ioc_expr.le_count = lnet_get_list_len(&expr->el_exprs);
1293                 if (copy_to_user(*bulk, &ioc_expr, sizeof(ioc_expr))) {
1294                         CDEBUG(D_NET, "failex to copy ioc_expr\n");
1295                         return -EFAULT;
1296                 }
1297                 *bulk += sizeof(ioc_expr);
1298                 *bulk_size -= sizeof(ioc_expr);
1299
1300                 rc = copy_exprs(expr, bulk, bulk_size);
1301                 if (rc)
1302                         return rc;
1303         }
1304
1305         return 0;
1306 }
1307
1308 int
1309 lnet_udsp_marshal(struct lnet_udsp *udsp, struct lnet_ioctl_udsp *ioc_udsp)
1310 {
1311         int rc = -ENOMEM;
1312         void __user *bulk;
1313         __u32 bulk_size;
1314
1315         if (!ioc_udsp)
1316                 return -EINVAL;
1317
1318         bulk = ioc_udsp->iou_bulk;
1319         bulk_size = ioc_udsp->iou_hdr.ioc_len +
1320           ioc_udsp->iou_bulk_size;
1321
1322         CDEBUG(D_NET, "marshal udsp (%p)\n", udsp);
1323         CDEBUG(D_NET, "MEM -----> bulk: %p:0x%x\n", bulk, bulk_size);
1324         /* make sure user space allocated enough buffer to marshal the
1325          * udsp
1326          */
1327         if (bulk_size != lnet_get_udsp_size(udsp)) {
1328                 rc = -ENOSPC;
1329                 goto fail;
1330         }
1331
1332         ioc_udsp->iou_idx = udsp->udsp_idx;
1333         ioc_udsp->iou_action_type = udsp->udsp_action_type;
1334         ioc_udsp->iou_action.priority = udsp->udsp_action.udsp_priority;
1335
1336         bulk_size -= sizeof(*ioc_udsp);
1337
1338         rc = copy_nid_range(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
1339         if (rc)
1340                 goto fail;
1341
1342         rc = copy_nid_range(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
1343         if (rc)
1344                 goto fail;
1345
1346         rc = copy_nid_range(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
1347         if (rc)
1348                 goto fail;
1349
1350         CDEBUG(D_NET, "MEM <----- bulk: %p\n", bulk);
1351
1352         /* we should've consumed the entire buffer */
1353         LASSERT(bulk_size == 0);
1354         return 0;
1355
1356 fail:
1357         CERROR("Failed to marshal udsp: %d\n", rc);
1358         return rc;
1359 }
1360
1361 static void
1362 copy_range_info(void **bulk, void **buf, struct list_head *list,
1363                 int count)
1364 {
1365         struct lnet_range_expr *range_expr;
1366         struct cfs_range_expr *range;
1367         struct cfs_expr_list *exprs;
1368         int range_count = count;
1369         int i;
1370
1371         if (range_count == 0)
1372                 return;
1373
1374         if (range_count == -1) {
1375                 struct lnet_expressions *e;
1376
1377                 e = *bulk;
1378                 range_count = e->le_count;
1379                 *bulk += sizeof(*e);
1380         }
1381
1382         exprs = *buf;
1383         INIT_LIST_HEAD(&exprs->el_link);
1384         INIT_LIST_HEAD(&exprs->el_exprs);
1385         list_add_tail(&exprs->el_link, list);
1386         *buf += sizeof(*exprs);
1387
1388         for (i = 0; i < range_count; i++) {
1389                 range_expr = *bulk;
1390                 range = *buf;
1391                 INIT_LIST_HEAD(&range->re_link);
1392                 range->re_lo = range_expr->re_lo;
1393                 range->re_hi = range_expr->re_hi;
1394                 range->re_stride = range_expr->re_stride;
1395                 CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
1396                        range->re_lo,
1397                        range->re_hi,
1398                        range->re_stride);
1399                 list_add_tail(&range->re_link, &exprs->el_exprs);
1400                 *bulk += sizeof(*range_expr);
1401                 *buf += sizeof(*range);
1402         }
1403 }
1404
1405 static int
1406 copy_ioc_udsp_descr(struct lnet_ud_nid_descr *nid_descr, char *type,
1407                     void **bulk, __u32 *bulk_size)
1408 {
1409         struct lnet_ioctl_udsp_descr *ioc_nid = *bulk;
1410         struct lnet_expressions *exprs;
1411         __u32 descr_type;
1412         int expr_count = 0;
1413         int range_count = 0;
1414         int i;
1415         __u32 size;
1416         int remaining_size = *bulk_size;
1417         void *tmp = *bulk;
1418         __u32 alloc_size;
1419         void *buf;
1420         size_t range_expr_s = sizeof(struct lnet_range_expr);
1421         size_t lnet_exprs_s = sizeof(struct lnet_expressions);
1422
1423         CDEBUG(D_NET, "%s: bulk = %p:%u\n", type, *bulk, *bulk_size);
1424
1425         /* criteria not present, skip over the static part of the
1426          * bulk, which is included for each NID descriptor
1427          */
1428         if (ioc_nid->iud_net.ud_net_type == 0) {
1429                 remaining_size -= sizeof(*ioc_nid);
1430                 if (remaining_size < 0) {
1431                         CERROR("Truncated userspace udsp buffer given\n");
1432                         return -EINVAL;
1433                 }
1434                 *bulk += sizeof(*ioc_nid);
1435                 *bulk_size = remaining_size;
1436                 return 0;
1437         }
1438
1439         descr_type = ioc_nid->iud_src_hdr.ud_descr_type;
1440         if (descr_type != *(__u32 *)type) {
1441                 CERROR("Bad NID descriptor type. Expected %s, given %c%c%c\n",
1442                         type, (__u8)descr_type, (__u8)(descr_type << 4),
1443                         (__u8)(descr_type << 8));
1444                 return -EINVAL;
1445         }
1446
1447         /* calculate the total size to verify we have enough buffer.
1448          * Start of by finding how many ranges there are for the net
1449          * expression.
1450          */
1451         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
1452         size = sizeof(*ioc_nid) + (range_count * range_expr_s);
1453         remaining_size -= size;
1454         if (remaining_size < 0) {
1455                 CERROR("Truncated userspace udsp buffer given\n");
1456                 return -EINVAL;
1457         }
1458
1459         CDEBUG(D_NET, "Total net num ranges in %s: %d:%u\n", type,
1460                range_count, size);
1461         /* the number of expressions for the NID. IE 4 for IP, 1 for GNI */
1462         expr_count = ioc_nid->iud_src_hdr.ud_descr_count;
1463         CDEBUG(D_NET, "addr as %d exprs\n", expr_count);
1464         /* point tmp to the beginning of the NID expressions */
1465         tmp += size;
1466         for (i = 0; i < expr_count; i++) {
1467                 /* get the number of ranges per expression */
1468                 exprs = tmp;
1469                 range_count += exprs->le_count;
1470                 size = (range_expr_s * exprs->le_count) + lnet_exprs_s;
1471                 remaining_size -= size;
1472                 CDEBUG(D_NET, "expr %d:%d:%u:%d:%d\n", i, exprs->le_count,
1473                        size, remaining_size, range_count);
1474                 if (remaining_size < 0) {
1475                         CERROR("Truncated userspace udsp buffer given\n");
1476                         return -EINVAL;
1477                 }
1478                 tmp += size;
1479         }
1480
1481         *bulk_size = remaining_size;
1482
1483         /* copy over the net type */
1484         nid_descr->ud_net_id.udn_net_type = ioc_nid->iud_net.ud_net_type;
1485
1486         CDEBUG(D_NET, "%u\n", nid_descr->ud_net_id.udn_net_type);
1487
1488         /* allocate the total memory required to copy this NID descriptor */
1489         alloc_size = (sizeof(struct cfs_expr_list) * (expr_count + 1)) +
1490                      (sizeof(struct cfs_range_expr) * (range_count));
1491         LIBCFS_ALLOC(buf, alloc_size);
1492         if (!buf)
1493                 return -ENOMEM;
1494
1495         /* store the amount of memory allocated so we can free it later on */
1496         nid_descr->ud_mem_size = alloc_size;
1497
1498         /* copy over the net number range */
1499         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
1500         *bulk += sizeof(*ioc_nid);
1501         CDEBUG(D_NET, "bulk = %p\n", *bulk);
1502         copy_range_info(bulk, &buf, &nid_descr->ud_net_id.udn_net_num_range,
1503                         range_count);
1504         CDEBUG(D_NET, "bulk = %p\n", *bulk);
1505
1506         /* copy over the NID descriptor */
1507         for (i = 0; i < expr_count; i++) {
1508                 copy_range_info(bulk, &buf, &nid_descr->ud_addr_range, -1);
1509                 CDEBUG(D_NET, "bulk = %p\n", *bulk);
1510         }
1511
1512         return 0;
1513 }
1514
1515 int
1516 lnet_udsp_demarshal_add(void *bulk, __u32 bulk_size)
1517 {
1518         struct lnet_ioctl_udsp *ioc_udsp;
1519         struct lnet_udsp *udsp;
1520         int rc = -ENOMEM;
1521         int idx;
1522
1523         if (bulk_size < sizeof(*ioc_udsp))
1524                 return -ENOSPC;
1525
1526         udsp = lnet_udsp_alloc();
1527         if (!udsp)
1528                 return rc;
1529
1530         ioc_udsp = bulk;
1531
1532         udsp->udsp_action_type = ioc_udsp->iou_action_type;
1533         udsp->udsp_action.udsp_priority = ioc_udsp->iou_action.priority;
1534         idx = ioc_udsp->iou_idx;
1535
1536         CDEBUG(D_NET, "demarshal descr %u:%u:%d:%u\n", udsp->udsp_action_type,
1537                udsp->udsp_action.udsp_priority, idx, bulk_size);
1538
1539         bulk += sizeof(*ioc_udsp);
1540         bulk_size -= sizeof(*ioc_udsp);
1541
1542         rc = copy_ioc_udsp_descr(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
1543         if (rc < 0)
1544                 goto free_udsp;
1545
1546         rc = copy_ioc_udsp_descr(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
1547         if (rc < 0)
1548                 goto free_udsp;
1549
1550         rc = copy_ioc_udsp_descr(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
1551         if (rc < 0)
1552                 goto free_udsp;
1553
1554         return lnet_udsp_add_policy(udsp, idx);
1555
1556 free_udsp:
1557         lnet_udsp_free(udsp);
1558         return rc;
1559 }