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