Whamcloud - gitweb
LU-9121 lnet: Add the kernel level De-Marshalling API
[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(ni->ni_nid,
216                 ni_match->ud_net_id.udn_net_type,
217                 &ni_match->ud_net_id.udn_net_num_range,
218                 &ni_match->ud_addr_range);
219         if (!rc)
220                 return 0;
221
222         CDEBUG(D_NET, "apply udsp on ni %s\n",
223                libcfs_nid2str(ni->ni_nid));
224
225         /* Detected match. Set NIDs priority */
226         lnet_ni_set_sel_priority_locked(ni, priority);
227
228         return 0;
229 }
230
231 static int
232 lnet_udsp_apply_rte_list_on_net(struct lnet_net *net,
233                                 struct lnet_ud_nid_descr *rte_action,
234                                 bool revert)
235 {
236         struct lnet_remotenet *rnet;
237         struct list_head *rn_list;
238         struct lnet_route *route;
239         struct lnet_peer_ni *lpni;
240         bool cleared = false;
241         lnet_nid_t gw_nid, gw_prim_nid;
242         int rc = 0;
243         int i;
244
245         for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
246                 rn_list = &the_lnet.ln_remote_nets_hash[i];
247                 list_for_each_entry(rnet, rn_list, lrn_list) {
248                         list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
249                                 /* look if gw nid on the same net matches */
250                                 gw_prim_nid = route->lr_gateway->lp_primary_nid;
251                                 lpni = NULL;
252                                 while ((lpni = lnet_get_next_peer_ni_locked(route->lr_gateway,
253                                                                             NULL,
254                                                                             lpni)) != NULL) {
255                                         if (!lnet_get_net_locked(lpni->lpni_peer_net->lpn_net_id))
256                                                 continue;
257                                         gw_nid = lpni->lpni_nid;
258                                         rc = cfs_match_nid_net(gw_nid,
259                                                 rte_action->ud_net_id.udn_net_type,
260                                                 &rte_action->ud_net_id.udn_net_num_range,
261                                                 &rte_action->ud_addr_range);
262                                         if (rc)
263                                                 break;
264                                 }
265                                 /* match gw primary nid on a remote network */
266                                 if (!rc) {
267                                         gw_nid = gw_prim_nid;
268                                         rc = cfs_match_nid_net(gw_nid,
269                                                 rte_action->ud_net_id.udn_net_type,
270                                                 &rte_action->ud_net_id.udn_net_num_range,
271                                                 &rte_action->ud_addr_range);
272                                 }
273                                 if (!rc)
274                                         continue;
275                                 lnet_net_unlock(LNET_LOCK_EX);
276                                 if (!cleared || revert) {
277                                         lnet_net_clr_pref_rtrs(net);
278                                         cleared = true;
279                                         if (revert) {
280                                                 lnet_net_lock(LNET_LOCK_EX);
281                                                 continue;
282                                         }
283                                 }
284                                 /* match. Add to pref NIDs */
285                                 CDEBUG(D_NET, "udsp net->gw: %s->%s\n",
286                                        libcfs_net2str(net->net_id),
287                                        libcfs_nid2str(gw_prim_nid));
288                                 rc = lnet_net_add_pref_rtr(net, gw_prim_nid);
289                                 lnet_net_lock(LNET_LOCK_EX);
290                                 /* success if EEXIST return */
291                                 if (rc && rc != -EEXIST) {
292                                         CERROR("Failed to add %s to %s pref rtr list\n",
293                                                libcfs_nid2str(gw_prim_nid),
294                                                libcfs_net2str(net->net_id));
295                                         return rc;
296                                 }
297                         }
298                 }
299         }
300
301         return rc;
302 }
303
304 static int
305 lnet_udsp_apply_rte_rule_on_nets(struct udsp_info *udi)
306 {
307         int rc = 0;
308         int last_failure = 0;
309         struct lnet_net *net;
310         struct lnet_ud_nid_descr *match = udi->udi_match;
311         struct lnet_ud_nid_descr *rte_action = udi->udi_action;
312
313         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
314                 if (LNET_NETTYP(net->net_id) != match->ud_net_id.udn_net_type)
315                         continue;
316
317                 rc = cfs_match_net(net->net_id,
318                                    match->ud_net_id.udn_net_type,
319                                    &match->ud_net_id.udn_net_num_range);
320                 if (!rc)
321                         continue;
322
323                 CDEBUG(D_NET, "apply rule on %s\n",
324                        libcfs_net2str(net->net_id));
325                 rc = lnet_udsp_apply_rte_list_on_net(net, rte_action,
326                                                      udi->udi_revert);
327                 if (rc)
328                         last_failure = rc;
329         }
330
331         return last_failure;
332 }
333
334 static int
335 lnet_udsp_apply_rte_rule_on_net(struct udsp_info *udi)
336 {
337         int rc = 0;
338         struct lnet_net *net = udi->udi_net;
339         struct lnet_ud_nid_descr *match = udi->udi_match;
340         struct lnet_ud_nid_descr *rte_action = udi->udi_action;
341
342         rc = cfs_match_net(net->net_id,
343                            match->ud_net_id.udn_net_type,
344                            &match->ud_net_id.udn_net_num_range);
345         if (!rc)
346                 return 0;
347
348         CDEBUG(D_NET, "apply rule on %s\n",
349                 libcfs_net2str(net->net_id));
350         rc = lnet_udsp_apply_rte_list_on_net(net, rte_action,
351                                              udi->udi_revert);
352
353         return rc;
354 }
355
356 static int
357 lnet_udsp_apply_prio_rule_on_net(struct udsp_info *udi)
358 {
359         int rc;
360         struct lnet_ud_nid_descr *match = udi->udi_match;
361         struct lnet_net *net = udi->udi_net;
362         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
363
364         if (!lnet_udsp_is_net_rule(match))
365                 return RULE_NOT_APPLICABLE;
366
367         rc = cfs_match_net(net->net_id,
368                            match->ud_net_id.udn_net_type,
369                            &match->ud_net_id.udn_net_num_range);
370         if (!rc)
371                 return 0;
372
373         CDEBUG(D_NET, "apply rule on %s\n",
374                libcfs_net2str(net->net_id));
375
376         lnet_net_set_sel_priority_locked(net, priority);
377
378         return 0;
379 }
380
381 static int
382 lnet_udsp_apply_rule_on_nis(struct udsp_info *udi)
383 {
384         int rc = 0;
385         struct lnet_ni *ni;
386         struct lnet_net *net;
387         struct lnet_ud_nid_descr *ni_match = udi->udi_match;
388         int last_failure = 0;
389
390         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
391                 if (LNET_NETTYP(net->net_id) != ni_match->ud_net_id.udn_net_type)
392                         continue;
393
394                 udi->udi_net = net;
395                 if (!lnet_udsp_apply_prio_rule_on_net(udi))
396                         continue;
397
398                 list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
399                         udi->udi_ni = ni;
400                         rc = lnet_udsp_apply_rule_on_ni(udi);
401                         if (rc)
402                                 last_failure = rc;
403                 }
404         }
405
406         return last_failure;
407 }
408
409 static int
410 lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
411                                  struct lnet_ud_nid_descr *rte_action,
412                                  bool revert)
413 {
414         struct lnet_remotenet *rnet;
415         struct list_head *rn_list;
416         struct lnet_route *route;
417         bool cleared = false;
418         lnet_nid_t gw_nid;
419         int rc = 0;
420         int i;
421
422         for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
423                 rn_list = &the_lnet.ln_remote_nets_hash[i];
424                 list_for_each_entry(rnet, rn_list, lrn_list) {
425                         list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
426                                 gw_nid = route->lr_gateway->lp_primary_nid;
427                                 rc = cfs_match_nid_net(gw_nid,
428                                         rte_action->ud_net_id.udn_net_type,
429                                         &rte_action->ud_net_id.udn_net_num_range,
430                                         &rte_action->ud_addr_range);
431                                 if (!rc)
432                                         continue;
433                                 lnet_net_unlock(LNET_LOCK_EX);
434                                 if (!cleared || revert) {
435                                         CDEBUG(D_NET, "%spref rtr nids from lpni %s\n",
436                                                (revert) ? "revert " : "clear ",
437                                                libcfs_nid2str(lpni->lpni_nid));
438                                         lnet_peer_clr_pref_rtrs(lpni);
439                                         cleared = true;
440                                         if (revert) {
441                                                 lnet_net_lock(LNET_LOCK_EX);
442                                                 continue;
443                                         }
444                                 }
445                                 CDEBUG(D_NET, "add gw nid %s as preferred for peer %s\n",
446                                        libcfs_nid2str(gw_nid),
447                                        libcfs_nid2str(lpni->lpni_nid));
448                                 /* match. Add to pref NIDs */
449                                 rc = lnet_peer_add_pref_rtr(lpni, gw_nid);
450                                 lnet_net_lock(LNET_LOCK_EX);
451                                 /* success if EEXIST return */
452                                 if (rc && rc != -EEXIST) {
453                                         CERROR("Failed to add %s to %s pref rtr list\n",
454                                                libcfs_nid2str(gw_nid),
455                                                libcfs_nid2str(lpni->lpni_nid));
456                                         return rc;
457                                 }
458                         }
459                 }
460         }
461
462         return rc;
463 }
464
465 static int
466 lnet_udsp_apply_ni_list(struct lnet_peer_ni *lpni,
467                         struct lnet_ud_nid_descr *ni_action,
468                         bool revert)
469 {
470         int rc = 0;
471         struct lnet_ni *ni;
472         struct lnet_net *net;
473         bool cleared = false;
474
475         list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
476                 if (LNET_NETTYP(net->net_id) != ni_action->ud_net_id.udn_net_type)
477                         continue;
478                 list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
479                         rc = cfs_match_nid_net(ni->ni_nid,
480                                 ni_action->ud_net_id.udn_net_type,
481                                 &ni_action->ud_net_id.udn_net_num_range,
482                                 &ni_action->ud_addr_range);
483                         if (!rc)
484                                 continue;
485                         lnet_net_unlock(LNET_LOCK_EX);
486                         if (!cleared || revert) {
487                                 lnet_peer_clr_pref_nids(lpni);
488                                 CDEBUG(D_NET, "%spref nids from lpni %s\n",
489                                         (revert) ? "revert " : "clear ",
490                                         libcfs_nid2str(lpni->lpni_nid));
491                                 cleared = true;
492                                 if (revert) {
493                                         lnet_net_lock(LNET_LOCK_EX);
494                                         continue;
495                                 }
496                         }
497                         CDEBUG(D_NET, "add nid %s as preferred for peer %s\n",
498                                 libcfs_nid2str(ni->ni_nid),
499                                 libcfs_nid2str(lpni->lpni_nid));
500                         /* match. Add to pref NIDs */
501                         rc = lnet_peer_add_pref_nid(lpni, ni->ni_nid);
502                         lnet_net_lock(LNET_LOCK_EX);
503                         /* success if EEXIST return */
504                         if (rc && rc != -EEXIST) {
505                                 CERROR("Failed to add %s to %s pref nid list\n",
506                                         libcfs_nid2str(ni->ni_nid),
507                                         libcfs_nid2str(lpni->lpni_nid));
508                                 return rc;
509                         }
510                 }
511         }
512
513         return rc;
514 }
515
516 static int
517 lnet_udsp_apply_rule_on_lpni(struct udsp_info *udi)
518 {
519         int rc;
520         struct lnet_peer_ni *lpni = udi->udi_lpni;
521         struct lnet_ud_nid_descr *lp_match = udi->udi_match;
522         struct lnet_ud_nid_descr *action = udi->udi_action;
523         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
524         bool local = udi->udi_local;
525         enum lnet_udsp_action_type type = udi->udi_type;
526
527         rc = cfs_match_nid_net(lpni->lpni_nid,
528                 lp_match->ud_net_id.udn_net_type,
529                 &lp_match->ud_net_id.udn_net_num_range,
530                 &lp_match->ud_addr_range);
531
532         /* check if looking for a net match */
533         if (!rc &&
534             (lnet_get_list_len(&lp_match->ud_addr_range) ||
535              !cfs_match_net(udi->udi_lpn->lpn_net_id,
536                            lp_match->ud_net_id.udn_net_type,
537                            &lp_match->ud_net_id.udn_net_num_range))) {
538                 return 0;
539         }
540
541         if (type == EN_LNET_UDSP_ACTION_PREFERRED_LIST && local) {
542                 rc = lnet_udsp_apply_ni_list(lpni, action,
543                                              udi->udi_revert);
544                 if (rc)
545                         return rc;
546         } else if (type == EN_LNET_UDSP_ACTION_PREFERRED_LIST &&
547                         !local) {
548                 rc = lnet_udsp_apply_rte_list_on_lpni(lpni, action,
549                                                       udi->udi_revert);
550                 if (rc)
551                         return rc;
552         } else {
553                 lnet_peer_ni_set_selection_priority(lpni, priority);
554         }
555
556         return 0;
557 }
558
559 static int
560 lnet_udsp_apply_rule_on_lpn(struct udsp_info *udi)
561 {
562         int rc;
563         struct lnet_ud_nid_descr *match = udi->udi_match;
564         struct lnet_peer_net *lpn = udi->udi_lpn;
565         __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
566
567         if (udi->udi_type == EN_LNET_UDSP_ACTION_PREFERRED_LIST ||
568             !lnet_udsp_is_net_rule(match))
569                 return RULE_NOT_APPLICABLE;
570
571         rc = cfs_match_net(lpn->lpn_net_id,
572                         match->ud_net_id.udn_net_type,
573                         &match->ud_net_id.udn_net_num_range);
574         if (!rc)
575                 return 0;
576
577         CDEBUG(D_NET, "apply rule on lpn %s\n",
578                libcfs_net2str(lpn->lpn_net_id));
579         lnet_peer_net_set_sel_priority_locked(lpn, priority);
580
581         return 0;
582 }
583
584 static int
585 lnet_udsp_apply_rule_on_lpnis(struct udsp_info *udi)
586 {
587         /* iterate over all the peers in the system and find if any of the
588          * peers match the criteria. If they do, clear the preferred list
589          * and add the new list
590          */
591         int lncpt = cfs_percpt_number(the_lnet.ln_peer_tables);
592         struct lnet_ud_nid_descr *lp_match = udi->udi_match;
593         struct lnet_peer_table *ptable;
594         struct lnet_peer_net *lpn;
595         struct lnet_peer_ni *lpni;
596         struct lnet_peer *lp;
597         int last_failure = 0;
598         int cpt;
599         int rc;
600
601         for (cpt = 0; cpt < lncpt; cpt++) {
602                 ptable = the_lnet.ln_peer_tables[cpt];
603                 list_for_each_entry(lp, &ptable->pt_peer_list, lp_peer_list) {
604                         CDEBUG(D_NET, "udsp examining lp %s\n",
605                                libcfs_nid2str(lp->lp_primary_nid));
606                         list_for_each_entry(lpn,
607                                             &lp->lp_peer_nets,
608                                             lpn_peer_nets) {
609                                 CDEBUG(D_NET, "udsp examining lpn %s\n",
610                                        libcfs_net2str(lpn->lpn_net_id));
611
612                                 if (LNET_NETTYP(lpn->lpn_net_id) !=
613                                     lp_match->ud_net_id.udn_net_type)
614                                         continue;
615
616                                 udi->udi_lpn = lpn;
617
618                                 if (!lnet_udsp_apply_rule_on_lpn(udi))
619                                         continue;
620
621                                 list_for_each_entry(lpni,
622                                                     &lpn->lpn_peer_nis,
623                                                     lpni_peer_nis) {
624                                         CDEBUG(D_NET, "udsp examining lpni %s\n",
625                                                libcfs_nid2str(lpni->lpni_nid));
626                                         udi->udi_lpni = lpni;
627                                         rc = lnet_udsp_apply_rule_on_lpni(udi);
628                                         if (rc)
629                                                 last_failure = rc;
630                                 }
631                         }
632                 }
633         }
634
635         return last_failure;
636 }
637
638 static int
639 lnet_udsp_apply_single_policy(struct lnet_udsp *udsp, struct udsp_info *udi,
640                               udsp_apply_rule *cbs)
641 {
642         int rc;
643
644         if (lnet_udsp_criteria_present(&udsp->udsp_dst) &&
645             lnet_udsp_criteria_present(&udsp->udsp_src)) {
646                 /* NID Pair rule */
647                 if (!cbs[UDSP_APPLY_ON_PEERS])
648                         return 0;
649
650                 if (udsp->udsp_action_type !=
651                         EN_LNET_UDSP_ACTION_PREFERRED_LIST) {
652                         CERROR("Bad action type. Expected %d got %d\n",
653                                 EN_LNET_UDSP_ACTION_PREFERRED_LIST,
654                                 udsp->udsp_action_type);
655                         return 0;
656                 }
657                 udi->udi_match = &udsp->udsp_dst;
658                 udi->udi_action = &udsp->udsp_src;
659                 udi->udi_type = EN_LNET_UDSP_ACTION_PREFERRED_LIST;
660                 udi->udi_local = true;
661
662                 CDEBUG(D_NET, "applying udsp (%p) dst->src\n",
663                         udsp);
664                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
665                 if (rc)
666                         return rc;
667         } else if (lnet_udsp_criteria_present(&udsp->udsp_dst) &&
668                    lnet_udsp_criteria_present(&udsp->udsp_rte)) {
669                 /* Router rule */
670                 if (!cbs[UDSP_APPLY_ON_PEERS])
671                         return 0;
672
673                 if (udsp->udsp_action_type !=
674                         EN_LNET_UDSP_ACTION_PREFERRED_LIST) {
675                         CERROR("Bad action type. Expected %d got %d\n",
676                                 EN_LNET_UDSP_ACTION_PREFERRED_LIST,
677                                 udsp->udsp_action_type);
678                         return 0;
679                 }
680
681                 if (lnet_udsp_criteria_present(&udsp->udsp_src)) {
682                         CERROR("only one of src or dst can be specified\n");
683                         return 0;
684                 }
685                 udi->udi_match = &udsp->udsp_dst;
686                 udi->udi_action = &udsp->udsp_rte;
687                 udi->udi_type = EN_LNET_UDSP_ACTION_PREFERRED_LIST;
688                 udi->udi_local = false;
689
690                 CDEBUG(D_NET, "applying udsp (%p) dst->rte\n",
691                         udsp);
692                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
693                 if (rc)
694                         return rc;
695         } else if (lnet_udsp_criteria_present(&udsp->udsp_dst)) {
696                 /* destination priority rule */
697                 if (!cbs[UDSP_APPLY_ON_PEERS])
698                         return 0;
699
700                 if (udsp->udsp_action_type !=
701                         EN_LNET_UDSP_ACTION_PRIORITY) {
702                         CERROR("Bad action type. Expected %d got %d\n",
703                                 EN_LNET_UDSP_ACTION_PRIORITY,
704                                 udsp->udsp_action_type);
705                         return 0;
706                 }
707                 udi->udi_match = &udsp->udsp_dst;
708                 udi->udi_type = EN_LNET_UDSP_ACTION_PRIORITY;
709                 if (udsp->udsp_action_type !=
710                     EN_LNET_UDSP_ACTION_PRIORITY) {
711                         udi->udi_priority = 0;
712                 } else {
713                         udi->udi_priority = udsp->udsp_action.udsp_priority;
714                 }
715                 udi->udi_local = true;
716
717                 CDEBUG(D_NET, "applying udsp (%p) on destination\n",
718                         udsp);
719                 rc = cbs[UDSP_APPLY_ON_PEERS](udi);
720                 if (rc)
721                         return rc;
722         } else if (lnet_udsp_criteria_present(&udsp->udsp_src)) {
723                 /* source priority rule */
724                 if (!cbs[UDSP_APPLY_PRIO_ON_NIS])
725                         return 0;
726
727                 if (udsp->udsp_action_type !=
728                         EN_LNET_UDSP_ACTION_PRIORITY) {
729                         CERROR("Bad action type. Expected %d got %d\n",
730                                 EN_LNET_UDSP_ACTION_PRIORITY,
731                                 udsp->udsp_action_type);
732                         return 0;
733                 }
734                 udi->udi_match = &udsp->udsp_src;
735                 udi->udi_type = EN_LNET_UDSP_ACTION_PRIORITY;
736                 if (udsp->udsp_action_type !=
737                     EN_LNET_UDSP_ACTION_PRIORITY) {
738                         udi->udi_priority = 0;
739                 } else {
740                         udi->udi_priority = udsp->udsp_action.udsp_priority;
741                 }
742                 udi->udi_local = true;
743
744                 CDEBUG(D_NET, "applying udsp (%p) on source\n",
745                         udsp);
746                 rc = cbs[UDSP_APPLY_PRIO_ON_NIS](udi);
747         } else {
748                 CERROR("Bad UDSP policy\n");
749                 return 0;
750         }
751
752         return 0;
753 }
754
755 static int
756 lnet_udsp_apply_policies_helper(struct lnet_udsp *udsp, struct udsp_info *udi,
757                                 udsp_apply_rule *cbs)
758 {
759         int rc;
760         int last_failure = 0;
761
762         if (udsp)
763                 return lnet_udsp_apply_single_policy(udsp, udi, cbs);
764
765         list_for_each_entry_reverse(udsp,
766                                     &the_lnet.ln_udsp_list,
767                                     udsp_on_list) {
768                 rc = lnet_udsp_apply_single_policy(udsp, udi, cbs);
769                 if (rc)
770                         last_failure = rc;
771         }
772
773         return last_failure;
774 }
775
776 int
777 lnet_udsp_apply_policies_on_ni(struct lnet_ni *ni)
778 {
779         struct udsp_info udi;
780         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
781
782         memset(&udi, 0, sizeof(udi));
783
784         udi.udi_ni = ni;
785
786         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_rule_on_ni;
787
788         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
789 }
790
791 int
792 lnet_udsp_apply_policies_on_net(struct lnet_net *net)
793 {
794         struct udsp_info udi;
795         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
796
797         memset(&udi, 0, sizeof(udi));
798
799         udi.udi_net = net;
800
801         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_prio_rule_on_net;
802         cbs[UDSP_APPLY_RTE_ON_NETS] = lnet_udsp_apply_rte_rule_on_net;
803
804         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
805 }
806
807 int
808 lnet_udsp_apply_policies_on_lpni(struct lnet_peer_ni *lpni)
809 {
810         struct udsp_info udi;
811         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
812
813         memset(&udi, 0, sizeof(udi));
814
815         udi.udi_lpni = lpni;
816
817         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpni;
818
819         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
820 }
821
822 int
823 lnet_udsp_apply_policies_on_lpn(struct lnet_peer_net *lpn)
824 {
825         struct udsp_info udi;
826         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
827
828         memset(&udi, 0, sizeof(udi));
829
830         udi.udi_lpn = lpn;
831
832         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpn;
833
834         return lnet_udsp_apply_policies_helper(NULL, &udi, cbs);
835 }
836
837 int
838 lnet_udsp_apply_policies(struct lnet_udsp *udsp, bool revert)
839 {
840         int rc;
841         struct udsp_info udi;
842         udsp_apply_rule cbs[UDSP_APPLY_MAX_ENUM] = {NULL};
843
844         memset(&udi, 0, sizeof(udi));
845
846         cbs[UDSP_APPLY_ON_PEERS] = lnet_udsp_apply_rule_on_lpnis;
847         cbs[UDSP_APPLY_PRIO_ON_NIS] = lnet_udsp_apply_rule_on_nis;
848         cbs[UDSP_APPLY_RTE_ON_NETS] = lnet_udsp_apply_rte_rule_on_nets;
849
850         udi.udi_revert = revert;
851
852         lnet_net_lock(LNET_LOCK_EX);
853         rc = lnet_udsp_apply_policies_helper(udsp, &udi, cbs);
854         lnet_net_unlock(LNET_LOCK_EX);
855
856         return rc;
857 }
858
859 struct lnet_udsp *
860 lnet_udsp_get_policy(int idx)
861 {
862         int i = 0;
863         struct lnet_udsp *udsp = NULL;
864         bool found = false;
865
866         CDEBUG(D_NET, "Get UDSP at idx = %d\n", idx);
867
868         if (idx < 0)
869                 return NULL;
870
871         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list) {
872                 CDEBUG(D_NET, "iterating over upsp %d:%d:%d\n",
873                        udsp->udsp_idx, i, idx);
874                 if (i == idx) {
875                         found = true;
876                         break;
877                 }
878                 i++;
879         }
880
881         CDEBUG(D_NET, "Found UDSP (%p)\n", udsp);
882
883         if (!found)
884                 return NULL;
885
886         return udsp;
887 }
888
889 int
890 lnet_udsp_add_policy(struct lnet_udsp *new, int idx)
891 {
892         struct lnet_udsp *udsp;
893         struct lnet_udsp *insert = NULL;
894         int i = 0;
895
896         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list) {
897                 CDEBUG(D_NET, "found udsp i = %d:%d, idx = %d\n",
898                        i, udsp->udsp_idx, idx);
899                 if (i == idx) {
900                         insert = udsp;
901                         new->udsp_idx = idx;
902                 }
903                 i++;
904                 if (lnet_udsp_equal(udsp, new)) {
905                         if (!lnet_udsp_action_equal(udsp, new) &&
906                             udsp->udsp_action_type == EN_LNET_UDSP_ACTION_PRIORITY &&
907                             new->udsp_action_type == EN_LNET_UDSP_ACTION_PRIORITY) {
908                                 udsp->udsp_action.udsp_priority = new->udsp_action.udsp_priority;
909                                 CDEBUG(D_NET, "udsp: %p index %d updated priority to %d\n",
910                                        udsp,
911                                        udsp->udsp_idx,
912                                        udsp->udsp_action.udsp_priority);
913                                 return 0;
914                         }
915                         return -EALREADY;
916                 }
917         }
918
919         if (insert) {
920                 list_add(&new->udsp_on_list, insert->udsp_on_list.prev);
921                 i = 0;
922                 list_for_each_entry(udsp,
923                                     &the_lnet.ln_udsp_list,
924                                     udsp_on_list) {
925                         if (i <= idx) {
926                                 i++;
927                                 continue;
928                         }
929                         udsp->udsp_idx++;
930                 }
931         } else {
932                 list_add_tail(&new->udsp_on_list, &the_lnet.ln_udsp_list);
933                 new->udsp_idx = i;
934         }
935
936         CDEBUG(D_NET, "udsp: %p added at index %d\n", new, new->udsp_idx);
937
938         CDEBUG(D_NET, "udsp list:\n");
939         list_for_each_entry(udsp, &the_lnet.ln_udsp_list, udsp_on_list)
940                 CDEBUG(D_NET, "udsp %p:%d\n", udsp, udsp->udsp_idx);
941
942         return 0;
943 }
944
945 int
946 lnet_udsp_del_policy(int idx)
947 {
948         struct lnet_udsp *udsp;
949         struct lnet_udsp *tmp;
950         bool removed = false;
951
952         if (idx < 0) {
953                 lnet_udsp_destroy(false);
954                 return 0;
955         }
956
957         CDEBUG(D_NET, "del udsp at idx = %d\n", idx);
958
959         list_for_each_entry_safe(udsp,
960                                  tmp,
961                                  &the_lnet.ln_udsp_list,
962                                  udsp_on_list) {
963                 if (removed)
964                         udsp->udsp_idx--;
965                 if (udsp->udsp_idx == idx && !removed) {
966                         list_del_init(&udsp->udsp_on_list);
967                         lnet_udsp_apply_policies(udsp, true);
968                         lnet_udsp_free(udsp);
969                         removed = true;
970                 }
971         }
972
973         return 0;
974 }
975
976 struct lnet_udsp *
977 lnet_udsp_alloc(void)
978 {
979         struct lnet_udsp *udsp;
980
981         udsp = kmem_cache_alloc(lnet_udsp_cachep, GFP_NOFS | __GFP_ZERO);
982
983         if (!udsp)
984                 return NULL;
985
986         INIT_LIST_HEAD(&udsp->udsp_on_list);
987         INIT_LIST_HEAD(&udsp->udsp_src.ud_addr_range);
988         INIT_LIST_HEAD(&udsp->udsp_src.ud_net_id.udn_net_num_range);
989         INIT_LIST_HEAD(&udsp->udsp_dst.ud_addr_range);
990         INIT_LIST_HEAD(&udsp->udsp_dst.ud_net_id.udn_net_num_range);
991         INIT_LIST_HEAD(&udsp->udsp_rte.ud_addr_range);
992         INIT_LIST_HEAD(&udsp->udsp_rte.ud_net_id.udn_net_num_range);
993
994         CDEBUG(D_MALLOC, "udsp alloc %p\n", udsp);
995         return udsp;
996 }
997
998 static void
999 lnet_udsp_nid_descr_free(struct lnet_ud_nid_descr *nid_descr)
1000 {
1001         struct list_head *net_range = &nid_descr->ud_net_id.udn_net_num_range;
1002
1003         if (!lnet_udsp_criteria_present(nid_descr))
1004                 return;
1005
1006         /* memory management is a bit tricky here. When we allocate the
1007          * memory to store the NID descriptor we allocate a large buffer
1008          * for all the data, so we need to free the entire buffer at
1009          * once. If the net is present the net_range->next points to that
1010          * buffer otherwise if the ud_addr_range is present then it's the
1011          * ud_addr_range.next
1012          */
1013         if (!list_empty(net_range))
1014                 LIBCFS_FREE(net_range->next, nid_descr->ud_mem_size);
1015         else if (!list_empty(&nid_descr->ud_addr_range))
1016                 LIBCFS_FREE(nid_descr->ud_addr_range.next,
1017                             nid_descr->ud_mem_size);
1018 }
1019
1020 void
1021 lnet_udsp_free(struct lnet_udsp *udsp)
1022 {
1023         lnet_udsp_nid_descr_free(&udsp->udsp_src);
1024         lnet_udsp_nid_descr_free(&udsp->udsp_dst);
1025         lnet_udsp_nid_descr_free(&udsp->udsp_rte);
1026
1027         CDEBUG(D_MALLOC, "udsp free %p\n", udsp);
1028         kmem_cache_free(lnet_udsp_cachep, udsp);
1029 }
1030
1031 void
1032 lnet_udsp_destroy(bool shutdown)
1033 {
1034         struct lnet_udsp *udsp, *tmp;
1035
1036         CDEBUG(D_NET, "Destroying UDSPs in the system\n");
1037
1038         list_for_each_entry_safe(udsp, tmp, &the_lnet.ln_udsp_list,
1039                                  udsp_on_list) {
1040                 list_del(&udsp->udsp_on_list);
1041                 if (!shutdown)
1042                         lnet_udsp_apply_policies(udsp, true);
1043                 lnet_udsp_free(udsp);
1044         }
1045 }
1046
1047 static size_t
1048 lnet_size_marshaled_nid_descr(struct lnet_ud_nid_descr *descr)
1049 {
1050         struct cfs_expr_list *expr;
1051         int expr_count = 0;
1052         int range_count = 0;
1053         size_t size = sizeof(struct lnet_ioctl_udsp_descr);
1054
1055         if (!lnet_udsp_criteria_present(descr))
1056                 return size;
1057
1058         /* we always have one net expression */
1059         if (!list_empty(&descr->ud_net_id.udn_net_num_range)) {
1060                 expr = list_first_entry(&descr->ud_net_id.udn_net_num_range,
1061                                         struct cfs_expr_list, el_link);
1062
1063                 /* count the number of cfs_range_expr in the net expression */
1064                 range_count = lnet_get_list_len(&expr->el_exprs);
1065         }
1066
1067         /* count the number of cfs_range_expr in the address expressions */
1068         list_for_each_entry(expr, &descr->ud_addr_range, el_link) {
1069                 expr_count++;
1070                 range_count += lnet_get_list_len(&expr->el_exprs);
1071         }
1072
1073         size += (sizeof(struct lnet_expressions) * expr_count);
1074         size += (sizeof(struct lnet_range_expr) * range_count);
1075
1076         return size;
1077 }
1078
1079 size_t
1080 lnet_get_udsp_size(struct lnet_udsp *udsp)
1081 {
1082         size_t size = sizeof(struct lnet_ioctl_udsp);
1083
1084         size += lnet_size_marshaled_nid_descr(&udsp->udsp_src);
1085         size += lnet_size_marshaled_nid_descr(&udsp->udsp_dst);
1086         size += lnet_size_marshaled_nid_descr(&udsp->udsp_rte);
1087
1088         CDEBUG(D_NET, "get udsp (%p) size: %d\n", udsp, (int)size);
1089
1090         return size;
1091 }
1092
1093 static int
1094 copy_exprs(struct cfs_expr_list *expr, void __user **bulk,
1095            __u32 *bulk_size)
1096 {
1097         struct cfs_range_expr *range;
1098         struct lnet_range_expr range_expr;
1099
1100         /* copy over the net range expressions to the bulk */
1101         list_for_each_entry(range, &expr->el_exprs, re_link) {
1102                 range_expr.re_lo = range->re_lo;
1103                 range_expr.re_hi = range->re_hi;
1104                 range_expr.re_stride = range->re_stride;
1105                 CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
1106                        range_expr.re_lo, range_expr.re_hi,
1107                        range_expr.re_stride);
1108                 if (copy_to_user(*bulk, &range_expr, sizeof(range_expr))) {
1109                         CDEBUG(D_NET, "Failed to copy range_expr\n");
1110                         return -EFAULT;
1111                 }
1112                 *bulk += sizeof(range_expr);
1113                 *bulk_size -= sizeof(range_expr);
1114         }
1115
1116         return 0;
1117 }
1118
1119 static int
1120 copy_nid_range(struct lnet_ud_nid_descr *nid_descr, char *type,
1121                 void __user **bulk, __u32 *bulk_size)
1122 {
1123         struct lnet_ioctl_udsp_descr ioc_udsp_descr;
1124         struct cfs_expr_list *expr;
1125         struct lnet_expressions ioc_expr;
1126         int expr_count;
1127         int net_expr_count;
1128         int rc;
1129
1130         memset(&ioc_udsp_descr, 0, sizeof(ioc_udsp_descr));
1131         ioc_udsp_descr.iud_src_hdr.ud_descr_type = *(__u32 *)type;
1132
1133         /* if criteria not present, copy over the static part of the NID
1134          * descriptor
1135          */
1136         if (!lnet_udsp_criteria_present(nid_descr)) {
1137                 CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
1138                        ioc_udsp_descr.iud_src_hdr.ud_descr_type,
1139                        ioc_udsp_descr.iud_src_hdr.ud_descr_count,
1140                        ioc_udsp_descr.iud_net.ud_net_type,
1141                        ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
1142                 if (copy_to_user(*bulk, &ioc_udsp_descr,
1143                                  sizeof(ioc_udsp_descr))) {
1144                         CDEBUG(D_NET, "failed to copy ioc_udsp_descr\n");
1145                         return -EFAULT;
1146                 }
1147                 *bulk += sizeof(ioc_udsp_descr);
1148                 *bulk_size -= sizeof(ioc_udsp_descr);
1149                 return 0;
1150         }
1151
1152         expr_count = lnet_get_list_len(&nid_descr->ud_addr_range);
1153
1154         /* copy the net information */
1155         if (!list_empty(&nid_descr->ud_net_id.udn_net_num_range)) {
1156                 expr = list_first_entry(&nid_descr->ud_net_id.udn_net_num_range,
1157                                         struct cfs_expr_list, el_link);
1158                 net_expr_count = lnet_get_list_len(&expr->el_exprs);
1159         } else {
1160                 net_expr_count = 0;
1161         }
1162
1163         /* set the total expression count */
1164         ioc_udsp_descr.iud_src_hdr.ud_descr_count = expr_count;
1165         ioc_udsp_descr.iud_net.ud_net_type =
1166                 nid_descr->ud_net_id.udn_net_type;
1167         ioc_udsp_descr.iud_net.ud_net_num_expr.le_count = net_expr_count;
1168
1169         CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
1170                 ioc_udsp_descr.iud_src_hdr.ud_descr_type,
1171                 ioc_udsp_descr.iud_src_hdr.ud_descr_count,
1172                 ioc_udsp_descr.iud_net.ud_net_type,
1173                 ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
1174
1175         /* copy over the header info to the bulk */
1176         if (copy_to_user(*bulk, &ioc_udsp_descr, sizeof(ioc_udsp_descr))) {
1177                 CDEBUG(D_NET, "Failed to copy data\n");
1178                 return -EFAULT;
1179         }
1180         *bulk += sizeof(ioc_udsp_descr);
1181         *bulk_size -= sizeof(ioc_udsp_descr);
1182
1183         /* copy over the net num expression if it exists */
1184         if (net_expr_count) {
1185                 rc = copy_exprs(expr, bulk, bulk_size);
1186                 if (rc)
1187                         return rc;
1188         }
1189
1190         /* copy the address range */
1191         list_for_each_entry(expr, &nid_descr->ud_addr_range, el_link) {
1192                 ioc_expr.le_count = lnet_get_list_len(&expr->el_exprs);
1193                 if (copy_to_user(*bulk, &ioc_expr, sizeof(ioc_expr))) {
1194                         CDEBUG(D_NET, "failex to copy ioc_expr\n");
1195                         return -EFAULT;
1196                 }
1197                 *bulk += sizeof(ioc_expr);
1198                 *bulk_size -= sizeof(ioc_expr);
1199
1200                 rc = copy_exprs(expr, bulk, bulk_size);
1201                 if (rc)
1202                         return rc;
1203         }
1204
1205         return 0;
1206 }
1207
1208 int
1209 lnet_udsp_marshal(struct lnet_udsp *udsp, struct lnet_ioctl_udsp *ioc_udsp)
1210 {
1211         int rc = -ENOMEM;
1212         void __user *bulk;
1213         __u32 bulk_size;
1214
1215         if (!ioc_udsp)
1216                 return -EINVAL;
1217
1218         bulk = ioc_udsp->iou_bulk;
1219         bulk_size = ioc_udsp->iou_hdr.ioc_len +
1220           ioc_udsp->iou_bulk_size;
1221
1222         CDEBUG(D_NET, "marshal udsp (%p)\n", udsp);
1223         CDEBUG(D_NET, "MEM -----> bulk: %p:0x%x\n", bulk, bulk_size);
1224         /* make sure user space allocated enough buffer to marshal the
1225          * udsp
1226          */
1227         if (bulk_size != lnet_get_udsp_size(udsp)) {
1228                 rc = -ENOSPC;
1229                 goto fail;
1230         }
1231
1232         ioc_udsp->iou_idx = udsp->udsp_idx;
1233         ioc_udsp->iou_action_type = udsp->udsp_action_type;
1234         ioc_udsp->iou_action.priority = udsp->udsp_action.udsp_priority;
1235
1236         bulk_size -= sizeof(*ioc_udsp);
1237
1238         rc = copy_nid_range(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
1239         if (rc)
1240                 goto fail;
1241
1242         rc = copy_nid_range(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
1243         if (rc)
1244                 goto fail;
1245
1246         rc = copy_nid_range(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
1247         if (rc)
1248                 goto fail;
1249
1250         CDEBUG(D_NET, "MEM <----- bulk: %p\n", bulk);
1251
1252         /* we should've consumed the entire buffer */
1253         LASSERT(bulk_size == 0);
1254         return 0;
1255
1256 fail:
1257         CERROR("Failed to marshal udsp: %d\n", rc);
1258         return rc;
1259 }
1260
1261 static void
1262 copy_range_info(void **bulk, void **buf, struct list_head *list,
1263                 int count)
1264 {
1265         struct lnet_range_expr *range_expr;
1266         struct cfs_range_expr *range;
1267         struct cfs_expr_list *exprs;
1268         int range_count = count;
1269         int i;
1270
1271         if (range_count == 0)
1272                 return;
1273
1274         if (range_count == -1) {
1275                 struct lnet_expressions *e;
1276
1277                 e = *bulk;
1278                 range_count = e->le_count;
1279                 *bulk += sizeof(*e);
1280         }
1281
1282         exprs = *buf;
1283         INIT_LIST_HEAD(&exprs->el_link);
1284         INIT_LIST_HEAD(&exprs->el_exprs);
1285         list_add_tail(&exprs->el_link, list);
1286         *buf += sizeof(*exprs);
1287
1288         for (i = 0; i < range_count; i++) {
1289                 range_expr = *bulk;
1290                 range = *buf;
1291                 INIT_LIST_HEAD(&range->re_link);
1292                 range->re_lo = range_expr->re_lo;
1293                 range->re_hi = range_expr->re_hi;
1294                 range->re_stride = range_expr->re_stride;
1295                 CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
1296                        range->re_lo,
1297                        range->re_hi,
1298                        range->re_stride);
1299                 list_add_tail(&range->re_link, &exprs->el_exprs);
1300                 *bulk += sizeof(*range_expr);
1301                 *buf += sizeof(*range);
1302         }
1303 }
1304
1305 static int
1306 copy_ioc_udsp_descr(struct lnet_ud_nid_descr *nid_descr, char *type,
1307                     void **bulk, __u32 *bulk_size)
1308 {
1309         struct lnet_ioctl_udsp_descr *ioc_nid = *bulk;
1310         struct lnet_expressions *exprs;
1311         __u32 descr_type;
1312         int expr_count = 0;
1313         int range_count = 0;
1314         int i;
1315         __u32 size;
1316         int remaining_size = *bulk_size;
1317         void *tmp = *bulk;
1318         __u32 alloc_size;
1319         void *buf;
1320         size_t range_expr_s = sizeof(struct lnet_range_expr);
1321         size_t lnet_exprs_s = sizeof(struct lnet_expressions);
1322
1323         CDEBUG(D_NET, "%s: bulk = %p:%u\n", type, *bulk, *bulk_size);
1324
1325         /* criteria not present, skip over the static part of the
1326          * bulk, which is included for each NID descriptor
1327          */
1328         if (ioc_nid->iud_net.ud_net_type == 0) {
1329                 remaining_size -= sizeof(*ioc_nid);
1330                 if (remaining_size < 0) {
1331                         CERROR("Truncated userspace udsp buffer given\n");
1332                         return -EINVAL;
1333                 }
1334                 *bulk += sizeof(*ioc_nid);
1335                 *bulk_size = remaining_size;
1336                 return 0;
1337         }
1338
1339         descr_type = ioc_nid->iud_src_hdr.ud_descr_type;
1340         if (descr_type != *(__u32 *)type) {
1341                 CERROR("Bad NID descriptor type. Expected %s, given %c%c%c\n",
1342                         type, (__u8)descr_type, (__u8)(descr_type << 4),
1343                         (__u8)(descr_type << 8));
1344                 return -EINVAL;
1345         }
1346
1347         /* calculate the total size to verify we have enough buffer.
1348          * Start of by finding how many ranges there are for the net
1349          * expression.
1350          */
1351         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
1352         size = sizeof(*ioc_nid) + (range_count * range_expr_s);
1353         remaining_size -= size;
1354         if (remaining_size < 0) {
1355                 CERROR("Truncated userspace udsp buffer given\n");
1356                 return -EINVAL;
1357         }
1358
1359         CDEBUG(D_NET, "Total net num ranges in %s: %d:%u\n", type,
1360                range_count, size);
1361         /* the number of expressions for the NID. IE 4 for IP, 1 for GNI */
1362         expr_count = ioc_nid->iud_src_hdr.ud_descr_count;
1363         CDEBUG(D_NET, "addr as %d exprs\n", expr_count);
1364         /* point tmp to the beginning of the NID expressions */
1365         tmp += size;
1366         for (i = 0; i < expr_count; i++) {
1367                 /* get the number of ranges per expression */
1368                 exprs = tmp;
1369                 range_count += exprs->le_count;
1370                 size = (range_expr_s * exprs->le_count) + lnet_exprs_s;
1371                 remaining_size -= size;
1372                 CDEBUG(D_NET, "expr %d:%d:%u:%d:%d\n", i, exprs->le_count,
1373                        size, remaining_size, range_count);
1374                 if (remaining_size < 0) {
1375                         CERROR("Truncated userspace udsp buffer given\n");
1376                         return -EINVAL;
1377                 }
1378                 tmp += size;
1379         }
1380
1381         *bulk_size = remaining_size;
1382
1383         /* copy over the net type */
1384         nid_descr->ud_net_id.udn_net_type = ioc_nid->iud_net.ud_net_type;
1385
1386         CDEBUG(D_NET, "%u\n", nid_descr->ud_net_id.udn_net_type);
1387
1388         /* allocate the total memory required to copy this NID descriptor */
1389         alloc_size = (sizeof(struct cfs_expr_list) * (expr_count + 1)) +
1390                      (sizeof(struct cfs_range_expr) * (range_count));
1391         LIBCFS_ALLOC(buf, alloc_size);
1392         if (!buf)
1393                 return -ENOMEM;
1394
1395         /* store the amount of memory allocated so we can free it later on */
1396         nid_descr->ud_mem_size = alloc_size;
1397
1398         /* copy over the net number range */
1399         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
1400         *bulk += sizeof(*ioc_nid);
1401         CDEBUG(D_NET, "bulk = %p\n", *bulk);
1402         copy_range_info(bulk, &buf, &nid_descr->ud_net_id.udn_net_num_range,
1403                         range_count);
1404         CDEBUG(D_NET, "bulk = %p\n", *bulk);
1405
1406         /* copy over the NID descriptor */
1407         for (i = 0; i < expr_count; i++) {
1408                 copy_range_info(bulk, &buf, &nid_descr->ud_addr_range, -1);
1409                 CDEBUG(D_NET, "bulk = %p\n", *bulk);
1410         }
1411
1412         return 0;
1413 }
1414
1415 int
1416 lnet_udsp_demarshal_add(void *bulk, __u32 bulk_size)
1417 {
1418         struct lnet_ioctl_udsp *ioc_udsp;
1419         struct lnet_udsp *udsp;
1420         int rc = -ENOMEM;
1421         int idx;
1422
1423         if (bulk_size < sizeof(*ioc_udsp))
1424                 return -ENOSPC;
1425
1426         udsp = lnet_udsp_alloc();
1427         if (!udsp)
1428                 return rc;
1429
1430         ioc_udsp = bulk;
1431
1432         udsp->udsp_action_type = ioc_udsp->iou_action_type;
1433         udsp->udsp_action.udsp_priority = ioc_udsp->iou_action.priority;
1434         idx = ioc_udsp->iou_idx;
1435
1436         CDEBUG(D_NET, "demarshal descr %u:%u:%d:%u\n", udsp->udsp_action_type,
1437                udsp->udsp_action.udsp_priority, idx, bulk_size);
1438
1439         bulk += sizeof(*ioc_udsp);
1440         bulk_size -= sizeof(*ioc_udsp);
1441
1442         rc = copy_ioc_udsp_descr(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
1443         if (rc < 0)
1444                 goto free_udsp;
1445
1446         rc = copy_ioc_udsp_descr(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
1447         if (rc < 0)
1448                 goto free_udsp;
1449
1450         rc = copy_ioc_udsp_descr(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
1451         if (rc < 0)
1452                 goto free_udsp;
1453
1454         return lnet_udsp_add_policy(udsp, idx);
1455
1456 free_udsp:
1457         lnet_udsp_free(udsp);
1458         return rc;
1459 }