Merge branch 'ras-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / staging / lustre / lnet / lnet / config.c
CommitLineData
d7e09d03
PT
1/*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
6a5b99a4 18 * http://www.gnu.org/licenses/gpl-2.0.html
d7e09d03 19 *
d7e09d03
PT
20 * GPL HEADER END
21 */
22/*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
25 *
1dc563a6 26 * Copyright (c) 2012, 2015, Intel Corporation.
d7e09d03
PT
27 */
28/*
29 * This file is part of Lustre, http://www.lustre.org/
30 * Lustre is a trademark of Sun Microsystems, Inc.
31 */
32
33#define DEBUG_SUBSYSTEM S_LNET
9fdaf8c0 34#include "../../include/linux/lnet/lib-lnet.h"
d7e09d03 35
ddbc66a5 36struct lnet_text_buf { /* tmp struct for parsing routes */
7e7ab095
MS
37 struct list_head ltb_list; /* stash on lists */
38 int ltb_size; /* allocated size */
39 char ltb_text[0]; /* text buffer */
e416a893 40};
d7e09d03 41
ad0a75b4 42static int lnet_tbnob; /* track text buf allocation */
51078e25
JS
43#define LNET_MAX_TEXTBUF_NOB (64 << 10) /* bound allocation */
44#define LNET_SINGLE_TEXTBUF_NOB (4 << 10)
d7e09d03 45
714340db 46static void
d7e09d03
PT
47lnet_syntax(char *name, char *str, int offset, int width)
48{
49 static char dots[LNET_SINGLE_TEXTBUF_NOB];
50 static char dashes[LNET_SINGLE_TEXTBUF_NOB];
51
52 memset(dots, '.', sizeof(dots));
51078e25 53 dots[sizeof(dots) - 1] = 0;
d7e09d03 54 memset(dashes, '-', sizeof(dashes));
51078e25 55 dashes[sizeof(dashes) - 1] = 0;
d7e09d03
PT
56
57 LCONSOLE_ERROR_MSG(0x10f, "Error parsing '%s=\"%s\"'\n", name, str);
58 LCONSOLE_ERROR_MSG(0x110, "here...........%.*s..%.*s|%.*s|\n",
59 (int)strlen(name), dots, offset, dots,
60 (width < 1) ? 0 : width - 1, dashes);
61}
62
714340db 63static int
24edbe4f 64lnet_issep(char c)
d7e09d03
PT
65{
66 switch (c) {
67 case '\n':
68 case '\r':
69 case ';':
70 return 1;
71 default:
72 return 0;
73 }
74}
75
6c9e5a55 76int
d7e09d03
PT
77lnet_net_unique(__u32 net, struct list_head *nilist)
78{
7e7ab095
MS
79 struct list_head *tmp;
80 lnet_ni_t *ni;
d7e09d03 81
24edbe4f 82 list_for_each(tmp, nilist) {
d7e09d03
PT
83 ni = list_entry(tmp, lnet_ni_t, ni_list);
84
85 if (LNET_NIDNET(ni->ni_nid) == net)
86 return 0;
87 }
88
89 return 1;
90}
91
92void
93lnet_ni_free(struct lnet_ni *ni)
94{
21602c7d
AS
95 int i;
96
06ace26e 97 if (ni->ni_refs)
d7e09d03
PT
98 cfs_percpt_free(ni->ni_refs);
99
06ace26e 100 if (ni->ni_tx_queues)
d7e09d03
PT
101 cfs_percpt_free(ni->ni_tx_queues);
102
06ace26e 103 if (ni->ni_cpts)
d7e09d03
PT
104 cfs_expr_list_values_free(ni->ni_cpts, ni->ni_ncpts);
105
243a941c
AS
106 if (ni->ni_lnd_tunables)
107 LIBCFS_FREE(ni->ni_lnd_tunables, sizeof(*ni->ni_lnd_tunables));
108
21602c7d
AS
109 for (i = 0; i < LNET_MAX_INTERFACES && ni->ni_interfaces[i]; i++) {
110 LIBCFS_FREE(ni->ni_interfaces[i],
111 strlen(ni->ni_interfaces[i]) + 1);
112 }
d7e09d03
PT
113 LIBCFS_FREE(ni, sizeof(*ni));
114}
115
9c26b89d 116lnet_ni_t *
d7e09d03
PT
117lnet_ni_alloc(__u32 net, struct cfs_expr_list *el, struct list_head *nilist)
118{
7e7ab095
MS
119 struct lnet_tx_queue *tq;
120 struct lnet_ni *ni;
121 int rc;
122 int i;
d7e09d03
PT
123
124 if (!lnet_net_unique(net, nilist)) {
125 LCONSOLE_ERROR_MSG(0x111, "Duplicate network specified: %s\n",
126 libcfs_net2str(net));
127 return NULL;
128 }
129
130 LIBCFS_ALLOC(ni, sizeof(*ni));
06ace26e 131 if (!ni) {
d7e09d03
PT
132 CERROR("Out of memory creating network %s\n",
133 libcfs_net2str(net));
134 return NULL;
135 }
136
137 spin_lock_init(&ni->ni_lock);
138 INIT_LIST_HEAD(&ni->ni_cptlist);
139 ni->ni_refs = cfs_percpt_alloc(lnet_cpt_table(),
140 sizeof(*ni->ni_refs[0]));
06ace26e 141 if (!ni->ni_refs)
d7e09d03
PT
142 goto failed;
143
144 ni->ni_tx_queues = cfs_percpt_alloc(lnet_cpt_table(),
145 sizeof(*ni->ni_tx_queues[0]));
06ace26e 146 if (!ni->ni_tx_queues)
d7e09d03
PT
147 goto failed;
148
149 cfs_percpt_for_each(tq, i, ni->ni_tx_queues)
150 INIT_LIST_HEAD(&tq->tq_delayed);
151
06ace26e 152 if (!el) {
d7e09d03
PT
153 ni->ni_cpts = NULL;
154 ni->ni_ncpts = LNET_CPT_NUMBER;
155 } else {
156 rc = cfs_expr_list_values(el, LNET_CPT_NUMBER, &ni->ni_cpts);
157 if (rc <= 0) {
158 CERROR("Failed to set CPTs for NI %s: %d\n",
159 libcfs_net2str(net), rc);
160 goto failed;
161 }
162
163 LASSERT(rc <= LNET_CPT_NUMBER);
164 if (rc == LNET_CPT_NUMBER) {
165 LIBCFS_FREE(ni->ni_cpts, rc * sizeof(ni->ni_cpts[0]));
166 ni->ni_cpts = NULL;
167 }
168
169 ni->ni_ncpts = rc;
170 }
171
172 /* LND will fill in the address part of the NID */
173 ni->ni_nid = LNET_MKNID(net, 0);
ec0067d1 174 ni->ni_last_alive = ktime_get_real_seconds();
d7e09d03
PT
175 list_add_tail(&ni->ni_list, nilist);
176 return ni;
177 failed:
178 lnet_ni_free(ni);
179 return NULL;
180}
181
182int
183lnet_parse_networks(struct list_head *nilist, char *networks)
184{
185 struct cfs_expr_list *el = NULL;
8766cd12 186 int tokensize;
7e7ab095
MS
187 char *tokens;
188 char *str;
189 char *tmp;
190 struct lnet_ni *ni;
191 __u32 net;
192 int nnets = 0;
9c26b89d 193 struct list_head *temp_node;
d7e09d03 194
8766cd12
AS
195 if (!networks) {
196 CERROR("networks string is undefined\n");
197 return -EINVAL;
198 }
199
d7e09d03
PT
200 if (strlen(networks) > LNET_SINGLE_TEXTBUF_NOB) {
201 /* _WAY_ conservative */
e46b3a0b
RK
202 LCONSOLE_ERROR_MSG(0x112,
203 "Can't parse networks: string too long\n");
d7e09d03
PT
204 return -EINVAL;
205 }
206
8766cd12
AS
207 tokensize = strlen(networks) + 1;
208
d7e09d03 209 LIBCFS_ALLOC(tokens, tokensize);
06ace26e 210 if (!tokens) {
d7e09d03
PT
211 CERROR("Can't allocate net tokens\n");
212 return -ENOMEM;
213 }
214
24edbe4f 215 memcpy(tokens, networks, tokensize);
d3d3d37a
JS
216 tmp = tokens;
217 str = tokens;
d7e09d03 218
5fd88337 219 while (str && *str) {
7e7ab095
MS
220 char *comma = strchr(str, ',');
221 char *bracket = strchr(str, '(');
222 char *square = strchr(str, '[');
223 char *iface;
224 int niface;
225 int rc;
d7e09d03 226
587cb022
SS
227 /*
228 * NB we don't check interface conflicts here; it's the LNDs
229 * responsibility (if it cares at all)
230 */
06ace26e 231 if (square && (!comma || square < comma)) {
587cb022
SS
232 /*
233 * i.e: o2ib0(ib0)[1,2], number between square
234 * brackets are CPTs this NI needs to be bond
235 */
06ace26e 236 if (bracket && bracket > square) {
d7e09d03
PT
237 tmp = square;
238 goto failed_syntax;
239 }
240
241 tmp = strchr(square, ']');
06ace26e 242 if (!tmp) {
d7e09d03
PT
243 tmp = square;
244 goto failed_syntax;
245 }
246
247 rc = cfs_expr_list_parse(square, tmp - square + 1,
248 0, LNET_CPT_NUMBER - 1, &el);
5fd88337 249 if (rc) {
d7e09d03
PT
250 tmp = square;
251 goto failed_syntax;
252 }
253
254 while (square <= tmp)
255 *square++ = ' ';
256 }
257
06ace26e 258 if (!bracket || (comma && comma < bracket)) {
d7e09d03
PT
259 /* no interface list specified */
260
06ace26e 261 if (comma)
d7e09d03
PT
262 *comma++ = 0;
263 net = libcfs_str2net(cfs_trimwhite(str));
264
265 if (net == LNET_NIDNET(LNET_NID_ANY)) {
e46b3a0b
RK
266 LCONSOLE_ERROR_MSG(0x113,
267 "Unrecognised network type\n");
d7e09d03
PT
268 tmp = str;
269 goto failed_syntax;
270 }
271
272 if (LNET_NETTYP(net) != LOLND && /* LO is implicit */
06ace26e 273 !lnet_ni_alloc(net, el, nilist))
d7e09d03
PT
274 goto failed;
275
06ace26e 276 if (el) {
d7e09d03
PT
277 cfs_expr_list_free(el);
278 el = NULL;
279 }
280
281 str = comma;
282 continue;
283 }
284
285 *bracket = 0;
286 net = libcfs_str2net(cfs_trimwhite(str));
287 if (net == LNET_NIDNET(LNET_NID_ANY)) {
288 tmp = str;
289 goto failed_syntax;
290 }
291
d7e09d03 292 ni = lnet_ni_alloc(net, el, nilist);
06ace26e 293 if (!ni)
d7e09d03
PT
294 goto failed;
295
06ace26e 296 if (el) {
d7e09d03
PT
297 cfs_expr_list_free(el);
298 el = NULL;
299 }
300
301 niface = 0;
302 iface = bracket + 1;
303
304 bracket = strchr(iface, ')');
06ace26e 305 if (!bracket) {
d7e09d03
PT
306 tmp = iface;
307 goto failed_syntax;
308 }
309
310 *bracket = 0;
311 do {
312 comma = strchr(iface, ',');
06ace26e 313 if (comma)
d7e09d03
PT
314 *comma++ = 0;
315
316 iface = cfs_trimwhite(iface);
5fd88337 317 if (!*iface) {
d7e09d03
PT
318 tmp = iface;
319 goto failed_syntax;
320 }
321
322 if (niface == LNET_MAX_INTERFACES) {
e46b3a0b
RK
323 LCONSOLE_ERROR_MSG(0x115,
324 "Too many interfaces for net %s\n",
d7e09d03
PT
325 libcfs_net2str(net));
326 goto failed;
327 }
328
21602c7d
AS
329 /*
330 * Allocate a separate piece of memory and copy
331 * into it the string, so we don't have
332 * a depencency on the tokens string. This way we
333 * can free the tokens at the end of the function.
334 * The newly allocated ni_interfaces[] can be
335 * freed when freeing the NI
336 */
337 LIBCFS_ALLOC(ni->ni_interfaces[niface],
338 strlen(iface) + 1);
339 if (!ni->ni_interfaces[niface]) {
340 CERROR("Can't allocate net interface name\n");
341 goto failed;
342 }
343 strncpy(ni->ni_interfaces[niface], iface,
344 strlen(iface));
345 niface++;
d7e09d03 346 iface = comma;
06ace26e 347 } while (iface);
d7e09d03
PT
348
349 str = bracket + 1;
350 comma = strchr(bracket + 1, ',');
06ace26e 351 if (comma) {
d7e09d03
PT
352 *comma = 0;
353 str = cfs_trimwhite(str);
5fd88337 354 if (*str) {
d7e09d03
PT
355 tmp = str;
356 goto failed_syntax;
357 }
358 str = comma + 1;
359 continue;
360 }
361
362 str = cfs_trimwhite(str);
5fd88337 363 if (*str) {
d7e09d03
PT
364 tmp = str;
365 goto failed_syntax;
366 }
367 }
368
9c26b89d
AS
369 list_for_each(temp_node, nilist)
370 nnets++;
21602c7d
AS
371
372 LIBCFS_FREE(tokens, tokensize);
9c26b89d 373 return nnets;
d7e09d03
PT
374
375 failed_syntax:
376 lnet_syntax("networks", networks, (int)(tmp - tokens), strlen(tmp));
377 failed:
378 while (!list_empty(nilist)) {
379 ni = list_entry(nilist->next, lnet_ni_t, ni_list);
380
381 list_del(&ni->ni_list);
382 lnet_ni_free(ni);
383 }
384
06ace26e 385 if (el)
d7e09d03
PT
386 cfs_expr_list_free(el);
387
388 LIBCFS_FREE(tokens, tokensize);
d7e09d03
PT
389
390 return -EINVAL;
391}
392
ddbc66a5 393static struct lnet_text_buf *
24edbe4f 394lnet_new_text_buf(int str_len)
d7e09d03 395{
ddbc66a5 396 struct lnet_text_buf *ltb;
7e7ab095 397 int nob;
d7e09d03
PT
398
399 /* NB allocate space for the terminating 0 */
ddbc66a5 400 nob = offsetof(struct lnet_text_buf, ltb_text[str_len + 1]);
d7e09d03
PT
401 if (nob > LNET_SINGLE_TEXTBUF_NOB) {
402 /* _way_ conservative for "route net gateway..." */
403 CERROR("text buffer too big\n");
404 return NULL;
405 }
406
407 if (lnet_tbnob + nob > LNET_MAX_TEXTBUF_NOB) {
408 CERROR("Too many text buffers\n");
409 return NULL;
410 }
411
412 LIBCFS_ALLOC(ltb, nob);
06ace26e 413 if (!ltb)
d7e09d03
PT
414 return NULL;
415
416 ltb->ltb_size = nob;
417 ltb->ltb_text[0] = 0;
418 lnet_tbnob += nob;
419 return ltb;
420}
421
714340db 422static void
ddbc66a5 423lnet_free_text_buf(struct lnet_text_buf *ltb)
d7e09d03
PT
424{
425 lnet_tbnob -= ltb->ltb_size;
426 LIBCFS_FREE(ltb, ltb->ltb_size);
427}
428
714340db 429static void
d7e09d03
PT
430lnet_free_text_bufs(struct list_head *tbs)
431{
ddbc66a5 432 struct lnet_text_buf *ltb;
d7e09d03
PT
433
434 while (!list_empty(tbs)) {
ddbc66a5 435 ltb = list_entry(tbs->next, struct lnet_text_buf, ltb_list);
d7e09d03
PT
436
437 list_del(&ltb->ltb_list);
438 lnet_free_text_buf(ltb);
439 }
440}
441
714340db 442static int
24edbe4f 443lnet_str2tbs_sep(struct list_head *tbs, char *str)
d7e09d03 444{
7e7ab095
MS
445 struct list_head pending;
446 char *sep;
447 int nob;
448 int i;
ddbc66a5 449 struct lnet_text_buf *ltb;
d7e09d03
PT
450
451 INIT_LIST_HEAD(&pending);
452
453 /* Split 'str' into separate commands */
454 for (;;) {
455 /* skip leading whitespace */
e525a681 456 while (isspace(*str))
d7e09d03
PT
457 str++;
458
459 /* scan for separator or comment */
5fd88337 460 for (sep = str; *sep; sep++)
d7e09d03
PT
461 if (lnet_issep(*sep) || *sep == '#')
462 break;
463
464 nob = (int)(sep - str);
465 if (nob > 0) {
466 ltb = lnet_new_text_buf(nob);
06ace26e 467 if (!ltb) {
d7e09d03 468 lnet_free_text_bufs(&pending);
58cb2ad3 469 return -ENOMEM;
d7e09d03
PT
470 }
471
472 for (i = 0; i < nob; i++)
e525a681 473 if (isspace(str[i]))
d7e09d03
PT
474 ltb->ltb_text[i] = ' ';
475 else
476 ltb->ltb_text[i] = str[i];
477
478 ltb->ltb_text[nob] = 0;
479
480 list_add_tail(&ltb->ltb_list, &pending);
481 }
482
483 if (*sep == '#') {
484 /* scan for separator */
485 do {
486 sep++;
5fd88337 487 } while (*sep && !lnet_issep(*sep));
d7e09d03
PT
488 }
489
5fd88337 490 if (!*sep)
d7e09d03
PT
491 break;
492
493 str = sep + 1;
494 }
495
496 list_splice(&pending, tbs->prev);
497 return 0;
498}
499
714340db 500static int
24edbe4f 501lnet_expand1tb(struct list_head *list,
d7e09d03
PT
502 char *str, char *sep1, char *sep2,
503 char *item, int itemlen)
504{
7e7ab095
MS
505 int len1 = (int)(sep1 - str);
506 int len2 = strlen(sep2 + 1);
ddbc66a5 507 struct lnet_text_buf *ltb;
d7e09d03 508
24edbe4f
RK
509 LASSERT(*sep1 == '[');
510 LASSERT(*sep2 == ']');
d7e09d03
PT
511
512 ltb = lnet_new_text_buf(len1 + itemlen + len2);
06ace26e 513 if (!ltb)
d7e09d03
PT
514 return -ENOMEM;
515
516 memcpy(ltb->ltb_text, str, len1);
517 memcpy(&ltb->ltb_text[len1], item, itemlen);
51078e25 518 memcpy(&ltb->ltb_text[len1 + itemlen], sep2 + 1, len2);
d7e09d03
PT
519 ltb->ltb_text[len1 + itemlen + len2] = 0;
520
521 list_add_tail(&ltb->ltb_list, list);
522 return 0;
523}
524
714340db 525static int
24edbe4f 526lnet_str2tbs_expand(struct list_head *tbs, char *str)
d7e09d03 527{
7e7ab095
MS
528 char num[16];
529 struct list_head pending;
530 char *sep;
531 char *sep2;
532 char *parsed;
533 char *enditem;
534 int lo;
535 int hi;
536 int stride;
537 int i;
538 int nob;
539 int scanned;
d7e09d03
PT
540
541 INIT_LIST_HEAD(&pending);
542
543 sep = strchr(str, '[');
06ace26e 544 if (!sep) /* nothing to expand */
d7e09d03
PT
545 return 0;
546
547 sep2 = strchr(sep, ']');
06ace26e 548 if (!sep2)
d7e09d03
PT
549 goto failed;
550
551 for (parsed = sep; parsed < sep2; parsed = enditem) {
d7e09d03
PT
552 enditem = ++parsed;
553 while (enditem < sep2 && *enditem != ',')
554 enditem++;
555
556 if (enditem == parsed) /* no empty items */
557 goto failed;
558
3a338e20
RB
559 if (sscanf(parsed, "%d-%d/%d%n", &lo, &hi,
560 &stride, &scanned) < 3) {
d7e09d03 561 if (sscanf(parsed, "%d-%d%n", &lo, &hi, &scanned) < 2) {
d7e09d03 562 /* simple string enumeration */
c314c319
JS
563 if (lnet_expand1tb(&pending, str, sep, sep2,
564 parsed,
5fd88337 565 (int)(enditem - parsed))) {
d7e09d03 566 goto failed;
3a338e20 567 }
d7e09d03
PT
568 continue;
569 }
570
571 stride = 1;
572 }
573
574 /* range expansion */
575
576 if (enditem != parsed + scanned) /* no trailing junk */
577 goto failed;
578
579 if (hi < 0 || lo < 0 || stride < 0 || hi < lo ||
5fd88337 580 (hi - lo) % stride)
d7e09d03
PT
581 goto failed;
582
583 for (i = lo; i <= hi; i += stride) {
d7e09d03
PT
584 snprintf(num, sizeof(num), "%d", i);
585 nob = strlen(num);
586 if (nob + 1 == sizeof(num))
587 goto failed;
588
589 if (lnet_expand1tb(&pending, str, sep, sep2,
5fd88337 590 num, nob))
d7e09d03
PT
591 goto failed;
592 }
593 }
594
595 list_splice(&pending, tbs->prev);
596 return 1;
597
598 failed:
599 lnet_free_text_bufs(&pending);
58cb2ad3 600 return -EINVAL;
d7e09d03
PT
601}
602
714340db 603static int
24edbe4f 604lnet_parse_hops(char *str, unsigned int *hops)
d7e09d03 605{
7e7ab095
MS
606 int len = strlen(str);
607 int nob = len;
d7e09d03
PT
608
609 return (sscanf(str, "%u%n", hops, &nob) >= 1 &&
610 nob == len &&
611 *hops > 0 && *hops < 256);
612}
613
e75fb87f
DO
614#define LNET_PRIORITY_SEPARATOR (':')
615
714340db 616static int
e75fb87f
DO
617lnet_parse_priority(char *str, unsigned int *priority, char **token)
618{
7e7ab095 619 int nob;
e75fb87f 620 char *sep;
7e7ab095 621 int len;
e75fb87f
DO
622
623 sep = strchr(str, LNET_PRIORITY_SEPARATOR);
06ace26e 624 if (!sep) {
e75fb87f
DO
625 *priority = 0;
626 return 0;
627 }
628 len = strlen(sep + 1);
629
51078e25 630 if ((sscanf((sep + 1), "%u%n", priority, &nob) < 1) || (len != nob)) {
587cb022
SS
631 /*
632 * Update the caller's token pointer so it treats the found
633 * priority as the token to report in the error message.
634 */
e75fb87f 635 *token += sep - str + 1;
58cb2ad3 636 return -EINVAL;
e75fb87f
DO
637 }
638
639 CDEBUG(D_NET, "gateway %s, priority %d, nob %d\n", str, *priority, nob);
640
641 /*
642 * Change priority separator to \0 to be able to parse NID
643 */
644 *sep = '\0';
645 return 0;
646}
d7e09d03 647
714340db 648static int
24edbe4f 649lnet_parse_route(char *str, int *im_a_router)
d7e09d03
PT
650{
651 /* static scratch buffer OK (single threaded) */
7e7ab095
MS
652 static char cmd[LNET_SINGLE_TEXTBUF_NOB];
653
654 struct list_head nets;
655 struct list_head gateways;
656 struct list_head *tmp1;
657 struct list_head *tmp2;
658 __u32 net;
659 lnet_nid_t nid;
ddbc66a5 660 struct lnet_text_buf *ltb;
7e7ab095
MS
661 int rc;
662 char *sep;
663 char *token = str;
664 int ntokens = 0;
665 int myrc = -1;
b9bbb61c 666 __u32 hops;
7e7ab095
MS
667 int got_hops = 0;
668 unsigned int priority = 0;
d7e09d03
PT
669
670 INIT_LIST_HEAD(&gateways);
671 INIT_LIST_HEAD(&nets);
672
673 /* save a copy of the string for error messages */
9563fe8a
DE
674 strncpy(cmd, str, sizeof(cmd));
675 cmd[sizeof(cmd) - 1] = '\0';
d7e09d03
PT
676
677 sep = str;
678 for (;;) {
679 /* scan for token start */
e525a681 680 while (isspace(*sep))
d7e09d03 681 sep++;
5fd88337 682 if (!*sep) {
d7e09d03
PT
683 if (ntokens < (got_hops ? 3 : 2))
684 goto token_error;
685 break;
686 }
687
688 ntokens++;
689 token = sep++;
690
691 /* scan for token end */
5fd88337 692 while (*sep && !isspace(*sep))
d7e09d03 693 sep++;
5fd88337 694 if (*sep)
d7e09d03
PT
695 *sep++ = 0;
696
697 if (ntokens == 1) {
698 tmp2 = &nets; /* expanding nets */
699 } else if (ntokens == 2 &&
700 lnet_parse_hops(token, &hops)) {
701 got_hops = 1; /* got a hop count */
702 continue;
703 } else {
704 tmp2 = &gateways; /* expanding gateways */
705 }
706
707 ltb = lnet_new_text_buf(strlen(token));
06ace26e 708 if (!ltb)
d7e09d03
PT
709 goto out;
710
711 strcpy(ltb->ltb_text, token);
712 tmp1 = &ltb->ltb_list;
713 list_add_tail(tmp1, tmp2);
714
715 while (tmp1 != tmp2) {
ddbc66a5 716 ltb = list_entry(tmp1, struct lnet_text_buf, ltb_list);
d7e09d03
PT
717
718 rc = lnet_str2tbs_expand(tmp1->next, ltb->ltb_text);
719 if (rc < 0)
720 goto token_error;
721
722 tmp1 = tmp1->next;
723
724 if (rc > 0) { /* expanded! */
725 list_del(&ltb->ltb_list);
726 lnet_free_text_buf(ltb);
727 continue;
728 }
729
730 if (ntokens == 1) {
731 net = libcfs_str2net(ltb->ltb_text);
732 if (net == LNET_NIDNET(LNET_NID_ANY) ||
733 LNET_NETTYP(net) == LOLND)
734 goto token_error;
735 } else {
e75fb87f
DO
736 rc = lnet_parse_priority(ltb->ltb_text,
737 &priority, &token);
738 if (rc < 0)
739 goto token_error;
740
d7e09d03
PT
741 nid = libcfs_str2nid(ltb->ltb_text);
742 if (nid == LNET_NID_ANY ||
743 LNET_NETTYP(LNET_NIDNET(nid)) == LOLND)
744 goto token_error;
745 }
746 }
747 }
748
b9bbb61c
AS
749 /**
750 * if there are no hops set then we want to flag this value as
751 * unset since hops is an optional parameter
752 */
d7e09d03 753 if (!got_hops)
b9bbb61c 754 hops = LNET_UNDEFINED_HOPS;
d7e09d03 755
24edbe4f
RK
756 LASSERT(!list_empty(&nets));
757 LASSERT(!list_empty(&gateways));
d7e09d03 758
24edbe4f 759 list_for_each(tmp1, &nets) {
ddbc66a5 760 ltb = list_entry(tmp1, struct lnet_text_buf, ltb_list);
d7e09d03 761 net = libcfs_str2net(ltb->ltb_text);
24edbe4f 762 LASSERT(net != LNET_NIDNET(LNET_NID_ANY));
d7e09d03 763
24edbe4f 764 list_for_each(tmp2, &gateways) {
ddbc66a5 765 ltb = list_entry(tmp2, struct lnet_text_buf, ltb_list);
d7e09d03 766 nid = libcfs_str2nid(ltb->ltb_text);
24edbe4f 767 LASSERT(nid != LNET_NID_ANY);
d7e09d03
PT
768
769 if (lnet_islocalnid(nid)) {
770 *im_a_router = 1;
771 continue;
772 }
773
e75fb87f 774 rc = lnet_add_route(net, hops, nid, priority);
be8240ac 775 if (rc && rc != -EEXIST && rc != -EHOSTUNREACH) {
e46b3a0b 776 CERROR("Can't create route to %s via %s\n",
d7e09d03
PT
777 libcfs_net2str(net),
778 libcfs_nid2str(nid));
779 goto out;
780 }
781 }
782 }
783
784 myrc = 0;
785 goto out;
786
787 token_error:
788 lnet_syntax("routes", cmd, (int)(token - str), strlen(token));
789 out:
790 lnet_free_text_bufs(&nets);
791 lnet_free_text_bufs(&gateways);
792 return myrc;
793}
794
714340db 795static int
d7e09d03
PT
796lnet_parse_route_tbs(struct list_head *tbs, int *im_a_router)
797{
ddbc66a5 798 struct lnet_text_buf *ltb;
d7e09d03
PT
799
800 while (!list_empty(tbs)) {
ddbc66a5 801 ltb = list_entry(tbs->next, struct lnet_text_buf, ltb_list);
d7e09d03
PT
802
803 if (lnet_parse_route(ltb->ltb_text, im_a_router) < 0) {
804 lnet_free_text_bufs(tbs);
805 return -EINVAL;
806 }
807
808 list_del(&ltb->ltb_list);
809 lnet_free_text_buf(ltb);
810 }
811
812 return 0;
813}
814
815int
24edbe4f 816lnet_parse_routes(char *routes, int *im_a_router)
d7e09d03 817{
7e7ab095
MS
818 struct list_head tbs;
819 int rc = 0;
d7e09d03
PT
820
821 *im_a_router = 0;
822
823 INIT_LIST_HEAD(&tbs);
824
825 if (lnet_str2tbs_sep(&tbs, routes) < 0) {
826 CERROR("Error parsing routes\n");
827 rc = -EINVAL;
828 } else {
829 rc = lnet_parse_route_tbs(&tbs, im_a_router);
830 }
831
5fd88337 832 LASSERT(!lnet_tbnob);
d7e09d03
PT
833 return rc;
834}
835
714340db 836static int
d7e09d03
PT
837lnet_match_network_token(char *token, int len, __u32 *ipaddrs, int nip)
838{
24edbe4f 839 LIST_HEAD(list);
7e7ab095
MS
840 int rc;
841 int i;
d7e09d03
PT
842
843 rc = cfs_ip_addr_parse(token, len, &list);
5fd88337 844 if (rc)
d7e09d03
PT
845 return rc;
846
847 for (rc = i = 0; !rc && i < nip; i++)
848 rc = cfs_ip_addr_match(ipaddrs[i], &list);
849
a620ec63 850 cfs_expr_list_free_list(&list);
d7e09d03
PT
851
852 return rc;
853}
854
714340db 855static int
d7e09d03
PT
856lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
857{
858 static char tokens[LNET_SINGLE_TEXTBUF_NOB];
859
7e7ab095
MS
860 int matched = 0;
861 int ntokens = 0;
862 int len;
d7e09d03
PT
863 char *net = NULL;
864 char *sep;
865 char *token;
7e7ab095 866 int rc;
d7e09d03 867
24edbe4f 868 LASSERT(strlen(net_entry) < sizeof(tokens));
d7e09d03
PT
869
870 /* work on a copy of the string */
871 strcpy(tokens, net_entry);
872 sep = tokens;
873 for (;;) {
874 /* scan for token start */
e525a681 875 while (isspace(*sep))
d7e09d03 876 sep++;
5fd88337 877 if (!*sep)
d7e09d03
PT
878 break;
879
880 token = sep++;
881
882 /* scan for token end */
5fd88337 883 while (*sep && !isspace(*sep))
d7e09d03 884 sep++;
5fd88337 885 if (*sep)
d7e09d03
PT
886 *sep++ = 0;
887
5fd88337 888 if (!ntokens++) {
d7e09d03
PT
889 net = token;
890 continue;
891 }
892
893 len = strlen(token);
894
895 rc = lnet_match_network_token(token, len, ipaddrs, nip);
896 if (rc < 0) {
897 lnet_syntax("ip2nets", net_entry,
898 (int)(token - tokens), len);
899 return rc;
900 }
901
5fd88337
JS
902 if (rc)
903 matched |= 1;
d7e09d03
PT
904 }
905
906 if (!matched)
907 return 0;
908
909 strcpy(net_entry, net); /* replace with matched net */
910 return 1;
911}
912
714340db 913static __u32
d7e09d03
PT
914lnet_netspec2net(char *netspec)
915{
7e7ab095
MS
916 char *bracket = strchr(netspec, '(');
917 __u32 net;
d7e09d03 918
06ace26e 919 if (bracket)
d7e09d03
PT
920 *bracket = 0;
921
922 net = libcfs_str2net(netspec);
923
06ace26e 924 if (bracket)
d7e09d03
PT
925 *bracket = '(';
926
927 return net;
928}
929
714340db 930static int
d7e09d03
PT
931lnet_splitnets(char *source, struct list_head *nets)
932{
7e7ab095
MS
933 int offset = 0;
934 int offset2;
935 int len;
ddbc66a5
JS
936 struct lnet_text_buf *tb;
937 struct lnet_text_buf *tb2;
7e7ab095
MS
938 struct list_head *t;
939 char *sep;
940 char *bracket;
941 __u32 net;
d7e09d03 942
24edbe4f
RK
943 LASSERT(!list_empty(nets));
944 LASSERT(nets->next == nets->prev); /* single entry */
d7e09d03 945
ddbc66a5 946 tb = list_entry(nets->next, struct lnet_text_buf, ltb_list);
d7e09d03
PT
947
948 for (;;) {
949 sep = strchr(tb->ltb_text, ',');
950 bracket = strchr(tb->ltb_text, '(');
951
06ace26e 952 if (sep && bracket && bracket < sep) {
d7e09d03
PT
953 /* netspec lists interfaces... */
954
955 offset2 = offset + (int)(bracket - tb->ltb_text);
956 len = strlen(bracket);
957
958 bracket = strchr(bracket + 1, ')');
959
06ace26e 960 if (!bracket ||
5fd88337 961 !(bracket[1] == ',' || !bracket[1])) {
d7e09d03
PT
962 lnet_syntax("ip2nets", source, offset2, len);
963 return -EINVAL;
964 }
965
5fd88337 966 sep = !bracket[1] ? NULL : bracket + 1;
d7e09d03
PT
967 }
968
06ace26e 969 if (sep)
d7e09d03
PT
970 *sep++ = 0;
971
972 net = lnet_netspec2net(tb->ltb_text);
973 if (net == LNET_NIDNET(LNET_NID_ANY)) {
974 lnet_syntax("ip2nets", source, offset,
975 strlen(tb->ltb_text));
976 return -EINVAL;
977 }
978
979 list_for_each(t, nets) {
ddbc66a5 980 tb2 = list_entry(t, struct lnet_text_buf, ltb_list);
d7e09d03
PT
981
982 if (tb2 == tb)
983 continue;
984
985 if (net == lnet_netspec2net(tb2->ltb_text)) {
986 /* duplicate network */
987 lnet_syntax("ip2nets", source, offset,
988 strlen(tb->ltb_text));
989 return -EINVAL;
990 }
991 }
992
06ace26e 993 if (!sep)
d7e09d03
PT
994 return 0;
995
996 offset += (int)(sep - tb->ltb_text);
9563fe8a
DE
997 len = strlen(sep);
998 tb2 = lnet_new_text_buf(len);
06ace26e 999 if (!tb2)
d7e09d03
PT
1000 return -ENOMEM;
1001
9563fe8a
DE
1002 strncpy(tb2->ltb_text, sep, len);
1003 tb2->ltb_text[len] = '\0';
d7e09d03
PT
1004 list_add_tail(&tb2->ltb_list, nets);
1005
1006 tb = tb2;
1007 }
1008}
1009
714340db 1010static int
24edbe4f 1011lnet_match_networks(char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
d7e09d03 1012{
7e7ab095
MS
1013 static char networks[LNET_SINGLE_TEXTBUF_NOB];
1014 static char source[LNET_SINGLE_TEXTBUF_NOB];
1015
1016 struct list_head raw_entries;
1017 struct list_head matched_nets;
1018 struct list_head current_nets;
1019 struct list_head *t;
1020 struct list_head *t2;
ddbc66a5 1021 struct lnet_text_buf *tb;
cb734cf7 1022 struct lnet_text_buf *temp;
ddbc66a5 1023 struct lnet_text_buf *tb2;
7e7ab095
MS
1024 __u32 net1;
1025 __u32 net2;
1026 int len;
1027 int count;
1028 int dup;
1029 int rc;
d7e09d03
PT
1030
1031 INIT_LIST_HEAD(&raw_entries);
1032 if (lnet_str2tbs_sep(&raw_entries, ip2nets) < 0) {
1033 CERROR("Error parsing ip2nets\n");
5fd88337 1034 LASSERT(!lnet_tbnob);
d7e09d03
PT
1035 return -EINVAL;
1036 }
1037
1038 INIT_LIST_HEAD(&matched_nets);
1039 INIT_LIST_HEAD(&current_nets);
1040 networks[0] = 0;
1041 count = 0;
1042 len = 0;
1043 rc = 0;
1044
cb734cf7 1045 list_for_each_entry_safe(tb, temp, &raw_entries, ltb_list) {
9563fe8a 1046 strncpy(source, tb->ltb_text, sizeof(source));
51078e25 1047 source[sizeof(source) - 1] = '\0';
d7e09d03
PT
1048
1049 /* replace ltb_text with the network(s) add on match */
1050 rc = lnet_match_network_tokens(tb->ltb_text, ipaddrs, nip);
1051 if (rc < 0)
1052 break;
1053
1054 list_del(&tb->ltb_list);
1055
5fd88337 1056 if (!rc) { /* no match */
d7e09d03
PT
1057 lnet_free_text_buf(tb);
1058 continue;
1059 }
1060
1061 /* split into separate networks */
1062 INIT_LIST_HEAD(&current_nets);
1063 list_add(&tb->ltb_list, &current_nets);
1064 rc = lnet_splitnets(source, &current_nets);
1065 if (rc < 0)
1066 break;
1067
1068 dup = 0;
24edbe4f 1069 list_for_each(t, &current_nets) {
ddbc66a5 1070 tb = list_entry(t, struct lnet_text_buf, ltb_list);
d7e09d03 1071 net1 = lnet_netspec2net(tb->ltb_text);
24edbe4f 1072 LASSERT(net1 != LNET_NIDNET(LNET_NID_ANY));
d7e09d03
PT
1073
1074 list_for_each(t2, &matched_nets) {
ddbc66a5
JS
1075 tb2 = list_entry(t2, struct lnet_text_buf,
1076 ltb_list);
d7e09d03 1077 net2 = lnet_netspec2net(tb2->ltb_text);
24edbe4f 1078 LASSERT(net2 != LNET_NIDNET(LNET_NID_ANY));
d7e09d03
PT
1079
1080 if (net1 == net2) {
1081 dup = 1;
1082 break;
1083 }
1084 }
1085
1086 if (dup)
1087 break;
1088 }
1089
1090 if (dup) {
1091 lnet_free_text_bufs(&current_nets);
1092 continue;
1093 }
1094
1095 list_for_each_safe(t, t2, &current_nets) {
ddbc66a5 1096 tb = list_entry(t, struct lnet_text_buf, ltb_list);
d7e09d03
PT
1097
1098 list_del(&tb->ltb_list);
1099 list_add_tail(&tb->ltb_list, &matched_nets);
1100
1101 len += snprintf(networks + len, sizeof(networks) - len,
5fd88337 1102 "%s%s", !len ? "" : ",",
d7e09d03
PT
1103 tb->ltb_text);
1104
1105 if (len >= sizeof(networks)) {
1106 CERROR("Too many matched networks\n");
1107 rc = -E2BIG;
1108 goto out;
1109 }
1110 }
1111
1112 count++;
1113 }
1114
1115 out:
1116 lnet_free_text_bufs(&raw_entries);
1117 lnet_free_text_bufs(&matched_nets);
1118 lnet_free_text_bufs(&current_nets);
5fd88337 1119 LASSERT(!lnet_tbnob);
d7e09d03
PT
1120
1121 if (rc < 0)
1122 return rc;
1123
1124 *networksp = networks;
1125 return count;
1126}
1127
714340db 1128static int
24edbe4f 1129lnet_ipaddr_enumerate(__u32 **ipaddrsp)
d7e09d03 1130{
7e7ab095
MS
1131 int up;
1132 __u32 netmask;
1133 __u32 *ipaddrs;
1134 __u32 *ipaddrs2;
1135 int nip;
1136 char **ifnames;
1ad6a73e 1137 int nif = lnet_ipif_enumerate(&ifnames);
7e7ab095
MS
1138 int i;
1139 int rc;
d7e09d03
PT
1140
1141 if (nif <= 0)
1142 return nif;
1143
1144 LIBCFS_ALLOC(ipaddrs, nif * sizeof(*ipaddrs));
06ace26e 1145 if (!ipaddrs) {
d7e09d03 1146 CERROR("Can't allocate ipaddrs[%d]\n", nif);
1ad6a73e 1147 lnet_ipif_free_enumeration(ifnames, nif);
d7e09d03
PT
1148 return -ENOMEM;
1149 }
1150
1151 for (i = nip = 0; i < nif; i++) {
1152 if (!strcmp(ifnames[i], "lo"))
1153 continue;
1154
1ad6a73e 1155 rc = lnet_ipif_query(ifnames[i], &up, &ipaddrs[nip], &netmask);
5fd88337 1156 if (rc) {
d7e09d03
PT
1157 CWARN("Can't query interface %s: %d\n",
1158 ifnames[i], rc);
1159 continue;
1160 }
1161
1162 if (!up) {
1163 CWARN("Ignoring interface %s: it's down\n",
1164 ifnames[i]);
1165 continue;
1166 }
1167
1168 nip++;
1169 }
1170
1ad6a73e 1171 lnet_ipif_free_enumeration(ifnames, nif);
d7e09d03
PT
1172
1173 if (nip == nif) {
1174 *ipaddrsp = ipaddrs;
1175 } else {
1176 if (nip > 0) {
1177 LIBCFS_ALLOC(ipaddrs2, nip * sizeof(*ipaddrs2));
06ace26e 1178 if (!ipaddrs2) {
d7e09d03
PT
1179 CERROR("Can't allocate ipaddrs[%d]\n", nip);
1180 nip = -ENOMEM;
1181 } else {
1182 memcpy(ipaddrs2, ipaddrs,
1183 nip * sizeof(*ipaddrs));
1184 *ipaddrsp = ipaddrs2;
1185 rc = nip;
1186 }
1187 }
cfa38118 1188 LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
d7e09d03
PT
1189 }
1190 return nip;
1191}
1192
1193int
24edbe4f 1194lnet_parse_ip2nets(char **networksp, char *ip2nets)
d7e09d03 1195{
7e7ab095
MS
1196 __u32 *ipaddrs = NULL;
1197 int nip = lnet_ipaddr_enumerate(&ipaddrs);
1198 int rc;
d7e09d03
PT
1199
1200 if (nip < 0) {
e46b3a0b
RK
1201 LCONSOLE_ERROR_MSG(0x117,
1202 "Error %d enumerating local IP interfaces for ip2nets to match\n",
1203 nip);
d7e09d03
PT
1204 return nip;
1205 }
1206
5fd88337 1207 if (!nip) {
e46b3a0b
RK
1208 LCONSOLE_ERROR_MSG(0x118,
1209 "No local IP interfaces for ip2nets to match\n");
d7e09d03
PT
1210 return -ENOENT;
1211 }
1212
1213 rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
cfa38118 1214 LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
d7e09d03
PT
1215
1216 if (rc < 0) {
1217 LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
1218 return rc;
1219 }
1220
5fd88337 1221 if (!rc) {
e46b3a0b
RK
1222 LCONSOLE_ERROR_MSG(0x11a,
1223 "ip2nets does not match any local IP interfaces\n");
d7e09d03
PT
1224 return -ENOENT;
1225 }
1226
1227 return 0;
1228}