Whamcloud - gitweb
LU-10391 lnet: change lpni_nid in lnet_peer_ni to lnet_nid
[fs/lustre-release.git] / lnet / lnet / router_proc.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  *   This file is part of Lustre, https://wiki.whamcloud.com/
7  *
8  *   Portals is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Portals is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Portals; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #define DEBUG_SUBSYSTEM S_LNET
24
25 #include <linux/uaccess.h>
26
27 #include <libcfs/libcfs.h>
28 #include <lnet/lib-lnet.h>
29
30 /* This is really lnet_proc.c. You might need to update sanity test 215
31  * if any file format is changed. */
32
33 #define LNET_LOFFT_BITS         (sizeof(loff_t) * 8)
34 /*
35  * NB: max allowed LNET_CPT_BITS is 8 on 64-bit system and 2 on 32-bit system
36  */
37 #define LNET_PROC_CPT_BITS      (LNET_CPT_BITS + 1)
38 /* change version, 16 bits or 8 bits */
39 #define LNET_PROC_VER_BITS              \
40         clamp_t(int, LNET_LOFFT_BITS / 4, 8, 16)
41
42 #define LNET_PROC_HASH_BITS     LNET_PEER_HASH_BITS
43 /*
44  * bits for peer hash offset
45  * NB: we don't use the highest bit of *ppos because it's signed
46  */
47 #define LNET_PROC_HOFF_BITS     (LNET_LOFFT_BITS -       \
48                                  LNET_PROC_CPT_BITS -    \
49                                  LNET_PROC_VER_BITS -    \
50                                  LNET_PROC_HASH_BITS - 1)
51 /* bits for hash index + position */
52 #define LNET_PROC_HPOS_BITS     (LNET_PROC_HASH_BITS + LNET_PROC_HOFF_BITS)
53 /* bits for peer hash table + hash version */
54 #define LNET_PROC_VPOS_BITS     (LNET_PROC_HPOS_BITS + LNET_PROC_VER_BITS)
55
56 #define LNET_PROC_CPT_MASK      ((1ULL << LNET_PROC_CPT_BITS) - 1)
57 #define LNET_PROC_VER_MASK      ((1ULL << LNET_PROC_VER_BITS) - 1)
58 #define LNET_PROC_HASH_MASK     ((1ULL << LNET_PROC_HASH_BITS) - 1)
59 #define LNET_PROC_HOFF_MASK     ((1ULL << LNET_PROC_HOFF_BITS) - 1)
60
61 #define LNET_PROC_CPT_GET(pos)                          \
62         (int)(((pos) >> LNET_PROC_VPOS_BITS) & LNET_PROC_CPT_MASK)
63
64 #define LNET_PROC_VER_GET(pos)                          \
65         (int)(((pos) >> LNET_PROC_HPOS_BITS) & LNET_PROC_VER_MASK)
66
67 #define LNET_PROC_HASH_GET(pos)                         \
68         (int)(((pos) >> LNET_PROC_HOFF_BITS) & LNET_PROC_HASH_MASK)
69
70 #define LNET_PROC_HOFF_GET(pos)                         \
71         (int)((pos) & LNET_PROC_HOFF_MASK)
72
73 #define LNET_PROC_POS_MAKE(cpt, ver, hash, off)         \
74         (((((loff_t)(cpt)) & LNET_PROC_CPT_MASK) << LNET_PROC_VPOS_BITS) |   \
75         ((((loff_t)(ver)) & LNET_PROC_VER_MASK) << LNET_PROC_HPOS_BITS) |   \
76         ((((loff_t)(hash)) & LNET_PROC_HASH_MASK) << LNET_PROC_HOFF_BITS) | \
77         ((off) & LNET_PROC_HOFF_MASK))
78
79 #define LNET_PROC_VERSION(v)    ((unsigned int)((v) & LNET_PROC_VER_MASK))
80
81 static int proc_lnet_stats(struct ctl_table *table, int write,
82                            void __user *buffer, size_t *lenp, loff_t *ppos)
83 {
84         int rc;
85         struct lnet_counters *ctrs;
86         struct lnet_counters_common common;
87         size_t nob = *lenp;
88         loff_t pos = *ppos;
89         int len;
90         char tmpstr[256]; /* 7 %u and 4 u64 */
91
92         if (write) {
93                 lnet_counters_reset();
94                 return 0;
95         }
96
97         /* read */
98
99         LIBCFS_ALLOC(ctrs, sizeof(*ctrs));
100         if (ctrs == NULL)
101                 return -ENOMEM;
102
103         rc = lnet_counters_get(ctrs);
104         if (rc)
105                 goto out_no_ctrs;
106
107         common = ctrs->lct_common;
108
109         len = scnprintf(tmpstr, sizeof(tmpstr),
110                         "%u %u %u %u %u %u %u %llu %llu "
111                         "%llu %llu",
112                         common.lcc_msgs_alloc, common.lcc_msgs_max,
113                         common.lcc_errors,
114                         common.lcc_send_count, common.lcc_recv_count,
115                         common.lcc_route_count, common.lcc_drop_count,
116                         common.lcc_send_length, common.lcc_recv_length,
117                         common.lcc_route_length, common.lcc_drop_length);
118
119         if (pos >= len)
120                 rc = 0;
121         else
122                 rc = cfs_trace_copyout_string(buffer, nob,
123                                               tmpstr + pos, "\n");
124 out_no_ctrs:
125         LIBCFS_FREE(ctrs, sizeof(*ctrs));
126         return rc;
127 }
128
129 static int
130 proc_lnet_routes(struct ctl_table *table, int write, void __user *buffer,
131                  size_t *lenp, loff_t *ppos)
132 {
133         const int       tmpsiz = 256;
134         char            *tmpstr;
135         char            *s;
136         int             rc = 0;
137         int             len;
138         int             ver;
139         int             off;
140
141         BUILD_BUG_ON(sizeof(loff_t) < 4);
142
143         off = LNET_PROC_HOFF_GET(*ppos);
144         ver = LNET_PROC_VER_GET(*ppos);
145
146         LASSERT(!write);
147
148         if (*lenp == 0)
149                 return 0;
150
151         LIBCFS_ALLOC(tmpstr, tmpsiz);
152         if (tmpstr == NULL)
153                 return -ENOMEM;
154
155         s = tmpstr; /* points to current position in tmpstr[] */
156
157         if (*ppos == 0) {
158                 s += scnprintf(s, tmpstr + tmpsiz - s, "Routing %s\n",
159                                the_lnet.ln_routing ? "enabled" : "disabled");
160                 LASSERT(tmpstr + tmpsiz - s > 0);
161
162                 s += scnprintf(s, tmpstr + tmpsiz - s, "%-8s %4s %8s %7s %s\n",
163                                "net", "hops", "priority", "state", "router");
164                 LASSERT(tmpstr + tmpsiz - s > 0);
165
166                 lnet_net_lock(0);
167                 ver = (unsigned int)the_lnet.ln_remote_nets_version;
168                 lnet_net_unlock(0);
169                 *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
170         } else {
171                 struct list_head        *n;
172                 struct list_head        *r;
173                 struct lnet_route               *route = NULL;
174                 struct lnet_remotenet   *rnet  = NULL;
175                 int                     skip  = off - 1;
176                 struct list_head        *rn_list;
177                 int                     i;
178
179                 lnet_net_lock(0);
180
181                 if (ver != LNET_PROC_VERSION(the_lnet.ln_remote_nets_version)) {
182                         lnet_net_unlock(0);
183                         LIBCFS_FREE(tmpstr, tmpsiz);
184                         return -ESTALE;
185                 }
186
187                 for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE && route == NULL;
188                      i++) {
189                         rn_list = &the_lnet.ln_remote_nets_hash[i];
190
191                         n = rn_list->next;
192
193                         while (n != rn_list && route == NULL) {
194                                 rnet = list_entry(n, struct lnet_remotenet,
195                                                   lrn_list);
196
197                                 r = rnet->lrn_routes.next;
198
199                                 while (r != &rnet->lrn_routes) {
200                                         struct lnet_route *re =
201                                                 list_entry(r, struct lnet_route,
202                                                            lr_list);
203                                         if (skip == 0) {
204                                                 route = re;
205                                                 break;
206                                         }
207
208                                         skip--;
209                                         r = r->next;
210                                 }
211
212                                 n = n->next;
213                         }
214                 }
215
216                 if (route != NULL) {
217                         __u32 net = rnet->lrn_net;
218                         __u32 hops = route->lr_hops;
219                         unsigned int priority = route->lr_priority;
220                         int alive = lnet_is_route_alive(route);
221
222                         s += scnprintf(s, tmpstr + tmpsiz - s,
223                                        "%-8s %4d %8u %7s %s\n",
224                                        libcfs_net2str(net), hops,
225                                        priority,
226                                        alive ? "up" : "down",
227                                        libcfs_nid2str(route->lr_nid));
228                         LASSERT(tmpstr + tmpsiz - s > 0);
229                 }
230
231                 lnet_net_unlock(0);
232         }
233
234         len = s - tmpstr;     /* how many bytes was written */
235
236         if (len > *lenp) {    /* linux-supplied buffer is too small */
237                 rc = -EINVAL;
238         } else if (len > 0) { /* wrote something */
239                 if (copy_to_user(buffer, tmpstr, len))
240                         rc = -EFAULT;
241                 else {
242                         off += 1;
243                         *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
244                 }
245         }
246
247         LIBCFS_FREE(tmpstr, tmpsiz);
248
249         if (rc == 0)
250                 *lenp = len;
251
252         return rc;
253 }
254
255 static int
256 proc_lnet_routers(struct ctl_table *table, int write, void __user *buffer,
257                   size_t *lenp, loff_t *ppos)
258 {
259         int        rc = 0;
260         char      *tmpstr;
261         char      *s;
262         const int  tmpsiz = 256;
263         int        len;
264         int        ver;
265         int        off;
266
267         off = LNET_PROC_HOFF_GET(*ppos);
268         ver = LNET_PROC_VER_GET(*ppos);
269
270         LASSERT(!write);
271
272         if (*lenp == 0)
273                 return 0;
274
275         LIBCFS_ALLOC(tmpstr, tmpsiz);
276         if (tmpstr == NULL)
277                 return -ENOMEM;
278
279         s = tmpstr; /* points to current position in tmpstr[] */
280
281         if (*ppos == 0) {
282                 s += scnprintf(s, tmpstr + tmpsiz - s,
283                                "%-4s %7s %5s %s\n",
284                                "ref", "rtr_ref", "alive", "router");
285                 LASSERT(tmpstr + tmpsiz - s > 0);
286
287                 lnet_net_lock(0);
288                 ver = (unsigned int)the_lnet.ln_routers_version;
289                 lnet_net_unlock(0);
290                 *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
291         } else {
292                 struct list_head *r;
293                 struct lnet_peer *peer = NULL;
294                 int               skip = off - 1;
295
296                 lnet_net_lock(0);
297
298                 if (ver != LNET_PROC_VERSION(the_lnet.ln_routers_version)) {
299                         lnet_net_unlock(0);
300
301                         LIBCFS_FREE(tmpstr, tmpsiz);
302                         return -ESTALE;
303                 }
304
305                 r = the_lnet.ln_routers.next;
306
307                 while (r != &the_lnet.ln_routers) {
308                         struct lnet_peer *lp =
309                           list_entry(r, struct lnet_peer,
310                                      lp_rtr_list);
311
312                         if (skip == 0) {
313                                 peer = lp;
314                                 break;
315                         }
316
317                         skip--;
318                         r = r->next;
319                 }
320
321                 if (peer != NULL) {
322                         lnet_nid_t nid = peer->lp_primary_nid;
323                         int nrefs     = atomic_read(&peer->lp_refcount);
324                         int nrtrrefs  = peer->lp_rtr_refcount;
325                         int alive     = lnet_is_gateway_alive(peer);
326
327                         s += scnprintf(s, tmpstr + tmpsiz - s,
328                                        "%-4d %7d %5s %s\n",
329                                        nrefs, nrtrrefs,
330                                        alive ? "up" : "down",
331                                        libcfs_nid2str(nid));
332                 }
333
334                 lnet_net_unlock(0);
335         }
336
337         len = s - tmpstr;     /* how many bytes was written */
338
339         if (len > *lenp) {    /* linux-supplied buffer is too small */
340                 rc = -EINVAL;
341         } else if (len > 0) { /* wrote something */
342                 if (copy_to_user(buffer, tmpstr, len))
343                         rc = -EFAULT;
344                 else {
345                         off += 1;
346                         *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
347                 }
348         }
349
350         LIBCFS_FREE(tmpstr, tmpsiz);
351
352         if (rc == 0)
353                 *lenp = len;
354
355         return rc;
356 }
357
358 /* TODO: there should be no direct access to ptable. We should add a set
359  * of APIs that give access to the ptable and its members */
360 static int
361 proc_lnet_peers(struct ctl_table *table, int write, void __user *buffer,
362                 size_t *lenp, loff_t *ppos)
363 {
364         const int               tmpsiz  = 256;
365         struct lnet_peer_table  *ptable;
366         char                    *tmpstr = NULL;
367         char                    *s;
368         int                     cpt  = LNET_PROC_CPT_GET(*ppos);
369         int                     ver  = LNET_PROC_VER_GET(*ppos);
370         int                     hash = LNET_PROC_HASH_GET(*ppos);
371         int                     hoff = LNET_PROC_HOFF_GET(*ppos);
372         int                     rc = 0;
373         int                     len;
374
375         if (write) {
376                 int i;
377                 struct lnet_peer_ni *peer;
378
379                 cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) {
380                         lnet_net_lock(i);
381                         for (hash = 0; hash < LNET_PEER_HASH_SIZE; hash++) {
382                                 list_for_each_entry(peer,
383                                                     &ptable->pt_hash[hash],
384                                                     lpni_hashlist) {
385                                         peer->lpni_mintxcredits =
386                                                 peer->lpni_txcredits;
387                                         peer->lpni_minrtrcredits =
388                                                 peer->lpni_rtrcredits;
389                                 }
390                         }
391                         lnet_net_unlock(i);
392                 }
393                 *ppos += *lenp;
394                 return 0;
395         }
396
397         if (*lenp == 0)
398                 return 0;
399
400         BUILD_BUG_ON(LNET_PROC_HASH_BITS < LNET_PEER_HASH_BITS);
401
402         if (cpt >= LNET_CPT_NUMBER) {
403                 *lenp = 0;
404                 return 0;
405         }
406
407         LIBCFS_ALLOC(tmpstr, tmpsiz);
408         if (tmpstr == NULL)
409                 return -ENOMEM;
410
411         s = tmpstr; /* points to current position in tmpstr[] */
412
413         if (*ppos == 0) {
414                 s += scnprintf(s, tmpstr + tmpsiz - s,
415                                "%-24s %4s %5s %5s %5s %5s %5s %5s %5s %s\n",
416                                "nid", "refs", "state", "last", "max",
417                                "rtr", "min", "tx", "min", "queue");
418                 LASSERT(tmpstr + tmpsiz - s > 0);
419
420                 hoff++;
421         } else {
422                 struct lnet_peer_ni     *peer;
423                 struct list_head        *p;
424                 int                     skip;
425
426  again:
427                 p = NULL;
428                 peer = NULL;
429                 skip = hoff - 1;
430
431                 lnet_net_lock(cpt);
432                 ptable = the_lnet.ln_peer_tables[cpt];
433                 if (hoff == 1)
434                         ver = LNET_PROC_VERSION(ptable->pt_version);
435
436                 if (ver != LNET_PROC_VERSION(ptable->pt_version)) {
437                         lnet_net_unlock(cpt);
438                         LIBCFS_FREE(tmpstr, tmpsiz);
439                         return -ESTALE;
440                 }
441
442                 while (hash < LNET_PEER_HASH_SIZE) {
443                         if (p == NULL)
444                                 p = ptable->pt_hash[hash].next;
445
446                         while (p != &ptable->pt_hash[hash]) {
447                                 struct lnet_peer_ni *lp =
448                                   list_entry(p, struct lnet_peer_ni,
449                                              lpni_hashlist);
450                                 if (skip == 0) {
451                                         peer = lp;
452
453                                         /* minor optimization: start from idx+1
454                                          * on next iteration if we've just
455                                          * drained lpni_hashlist */
456                                         if (lp->lpni_hashlist.next ==
457                                             &ptable->pt_hash[hash]) {
458                                                 hoff = 1;
459                                                 hash++;
460                                         } else {
461                                                 hoff++;
462                                         }
463
464                                         break;
465                                 }
466
467                                 skip--;
468                                 p = lp->lpni_hashlist.next;
469                         }
470
471                         if (peer != NULL)
472                                 break;
473
474                         p = NULL;
475                         hoff = 1;
476                         hash++;
477                 }
478
479                 if (peer != NULL) {
480                         struct lnet_nid nid = peer->lpni_nid;
481                         int nrefs = kref_read(&peer->lpni_kref);
482                         time64_t lastalive = -1;
483                         char *aliveness = "NA";
484                         int maxcr = (peer->lpni_net) ?
485                           peer->lpni_net->net_tunables.lct_peer_tx_credits : 0;
486                         int txcr = peer->lpni_txcredits;
487                         int mintxcr = peer->lpni_mintxcredits;
488                         int rtrcr = peer->lpni_rtrcredits;
489                         int minrtrcr = peer->lpni_minrtrcredits;
490                         int txqnob = peer->lpni_txqnob;
491
492                         if (lnet_isrouter(peer) ||
493                             lnet_peer_aliveness_enabled(peer))
494                                 aliveness = lnet_is_peer_ni_alive(peer) ?
495                                         "up" : "down";
496
497                         lnet_net_unlock(cpt);
498
499                         s += scnprintf(s, tmpstr + tmpsiz - s,
500                                        "%-24s %4d %5s %5lld %5d %5d %5d %5d %5d %d\n",
501                                        libcfs_nidstr(&nid), nrefs, aliveness,
502                                        lastalive, maxcr, rtrcr, minrtrcr, txcr,
503                                        mintxcr, txqnob);
504                         LASSERT(tmpstr + tmpsiz - s > 0);
505
506                 } else { /* peer is NULL */
507                         lnet_net_unlock(cpt);
508                 }
509
510                 if (hash == LNET_PEER_HASH_SIZE) {
511                         cpt++;
512                         hash = 0;
513                         hoff = 1;
514                         if (peer == NULL && cpt < LNET_CPT_NUMBER)
515                                 goto again;
516                 }
517         }
518
519         len = s - tmpstr;     /* how many bytes was written */
520
521         if (len > *lenp) {    /* linux-supplied buffer is too small */
522                 rc = -EINVAL;
523         } else if (len > 0) { /* wrote something */
524                 if (copy_to_user(buffer, tmpstr, len))
525                         rc = -EFAULT;
526                 else
527                         *ppos = LNET_PROC_POS_MAKE(cpt, ver, hash, hoff);
528         }
529
530         LIBCFS_FREE(tmpstr, tmpsiz);
531
532         if (rc == 0)
533                 *lenp = len;
534
535         return rc;
536 }
537
538 static int proc_lnet_buffers(struct ctl_table *table, int write,
539                              void __user *buffer, size_t *lenp, loff_t *ppos)
540 {
541         size_t nob = *lenp;
542         loff_t pos = *ppos;
543         char            *s;
544         char            *tmpstr;
545         int             tmpsiz;
546         int             idx;
547         int             len;
548         int             rc;
549         int             i;
550
551         LASSERT(!write);
552
553         /* (4 %d) * 4 * LNET_CPT_NUMBER */
554         tmpsiz = 64 * (LNET_NRBPOOLS + 1) * LNET_CPT_NUMBER;
555         LIBCFS_ALLOC(tmpstr, tmpsiz);
556         if (tmpstr == NULL)
557                 return -ENOMEM;
558
559         s = tmpstr; /* points to current position in tmpstr[] */
560
561         s += scnprintf(s, tmpstr + tmpsiz - s,
562                        "%5s %5s %7s %7s\n",
563                        "pages", "count", "credits", "min");
564         LASSERT(tmpstr + tmpsiz - s > 0);
565
566         if (the_lnet.ln_rtrpools == NULL)
567                 goto out; /* I'm not a router */
568
569         for (idx = 0; idx < LNET_NRBPOOLS; idx++) {
570                 struct lnet_rtrbufpool *rbp;
571
572                 lnet_net_lock(LNET_LOCK_EX);
573                 cfs_percpt_for_each(rbp, i, the_lnet.ln_rtrpools) {
574                         s += scnprintf(s, tmpstr + tmpsiz - s,
575                                        "%5d %5d %7d %7d\n",
576                                        rbp[idx].rbp_npages,
577                                        rbp[idx].rbp_nbuffers,
578                                        rbp[idx].rbp_credits,
579                                        rbp[idx].rbp_mincredits);
580                         LASSERT(tmpstr + tmpsiz - s > 0);
581                 }
582                 lnet_net_unlock(LNET_LOCK_EX);
583         }
584
585  out:
586         len = s - tmpstr;
587
588         if (pos >= min_t(int, len, strlen(tmpstr)))
589                 rc = 0;
590         else
591                 rc = cfs_trace_copyout_string(buffer, nob,
592                                               tmpstr + pos, NULL);
593
594         LIBCFS_FREE(tmpstr, tmpsiz);
595         return rc;
596 }
597
598 static int
599 proc_lnet_nis(struct ctl_table *table, int write, void __user *buffer,
600               size_t *lenp, loff_t *ppos)
601 {
602         int     tmpsiz = 128 * LNET_CPT_NUMBER;
603         int     rc = 0;
604         char    *tmpstr;
605         char    *s;
606         int     len;
607
608         if (*lenp == 0)
609                 return 0;
610
611         if (write) {
612                 /* Just reset the min stat. */
613                 struct lnet_ni  *ni;
614                 struct lnet_net *net;
615
616                 lnet_net_lock(0);
617
618                 list_for_each_entry(net, &the_lnet.ln_nets, net_list) {
619                         list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
620                                 struct lnet_tx_queue *tq;
621                                 int i;
622                                 int j;
623
624                                 cfs_percpt_for_each(tq, i, ni->ni_tx_queues) {
625                                         for (j = 0; ni->ni_cpts != NULL &&
626                                              j < ni->ni_ncpts; j++) {
627                                                 if (i == ni->ni_cpts[j])
628                                                         break;
629                                         }
630
631                                         if (j == ni->ni_ncpts)
632                                                 continue;
633
634                                         if (i != 0)
635                                                 lnet_net_lock(i);
636                                         tq->tq_credits_min = tq->tq_credits;
637                                         if (i != 0)
638                                                 lnet_net_unlock(i);
639                                 }
640                         }
641                 }
642                 lnet_net_unlock(0);
643                 *ppos += *lenp;
644                 return 0;
645         }
646
647         LIBCFS_ALLOC(tmpstr, tmpsiz);
648         if (tmpstr == NULL)
649                 return -ENOMEM;
650
651         s = tmpstr; /* points to current position in tmpstr[] */
652
653         if (*ppos == 0) {
654                 s += scnprintf(s, tmpstr + tmpsiz - s,
655                                "%-24s %6s %5s %4s %4s %4s %5s %5s %5s\n",
656                                "nid", "status", "alive", "refs", "peer",
657                                "rtr", "max", "tx", "min");
658                 LASSERT (tmpstr + tmpsiz - s > 0);
659         } else {
660                 struct lnet_ni *ni   = NULL;
661                 int skip = *ppos - 1;
662
663                 lnet_net_lock(0);
664
665                 ni = lnet_get_ni_idx_locked(skip);
666
667                 if (ni != NULL) {
668                         struct lnet_tx_queue *tq;
669                         char *stat;
670                         time64_t now = ktime_get_real_seconds();
671                         time64_t last_alive = -1;
672                         int i;
673                         int j;
674
675                         if (the_lnet.ln_routing)
676                                 last_alive = now - ni->ni_net->net_last_alive;
677
678                         lnet_ni_lock(ni);
679                         LASSERT(ni->ni_status != NULL);
680                         stat = (lnet_ni_get_status_locked(ni) ==
681                                 LNET_NI_STATUS_UP) ? "up" : "down";
682                         lnet_ni_unlock(ni);
683
684                         /* @lo forever alive */
685                         if (ni->ni_net->net_lnd->lnd_type == LOLND) {
686                                 last_alive = 0;
687                                 stat = "up";
688                         }
689
690                         /* we actually output credits information for
691                          * TX queue of each partition */
692                         cfs_percpt_for_each(tq, i, ni->ni_tx_queues) {
693                                 for (j = 0; ni->ni_cpts != NULL &&
694                                      j < ni->ni_ncpts; j++) {
695                                         if (i == ni->ni_cpts[j])
696                                                 break;
697                                 }
698
699                                 if (j == ni->ni_ncpts)
700                                         continue;
701
702                                 if (i != 0)
703                                         lnet_net_lock(i);
704
705                                 s += scnprintf(s, tmpstr + tmpsiz - s,
706                                        "%-24s %6s %5lld %4d %4d %4d %5d %5d %5d\n",
707                                        libcfs_nidstr(&ni->ni_nid), stat,
708                                        last_alive, *ni->ni_refs[i],
709                                        ni->ni_net->net_tunables.lct_peer_tx_credits,
710                                        ni->ni_net->net_tunables.lct_peer_rtr_credits,
711                                        tq->tq_credits_max,
712                                        tq->tq_credits, tq->tq_credits_min);
713                                 if (i != 0)
714                                         lnet_net_unlock(i);
715                         }
716                         LASSERT(tmpstr + tmpsiz - s > 0);
717                 }
718
719                 lnet_net_unlock(0);
720         }
721
722         len = s - tmpstr;     /* how many bytes was written */
723
724         if (len > *lenp) {    /* linux-supplied buffer is too small */
725                 rc = -EINVAL;
726         } else if (len > 0) { /* wrote something */
727                 if (copy_to_user(buffer, tmpstr, len))
728                         rc = -EFAULT;
729                 else
730                         *ppos += 1;
731         }
732
733         LIBCFS_FREE(tmpstr, tmpsiz);
734
735         if (rc == 0)
736                 *lenp = len;
737
738         return rc;
739 }
740
741 struct lnet_portal_rotors {
742         int             pr_value;
743         const char      *pr_name;
744         const char      *pr_desc;
745 };
746
747 static struct lnet_portal_rotors        portal_rotors[] = {
748         {
749                 .pr_value = LNET_PTL_ROTOR_OFF,
750                 .pr_name  = "OFF",
751                 .pr_desc  = "Turn off message rotor for wildcard portals"
752         },
753         {
754                 .pr_value = LNET_PTL_ROTOR_ON,
755                 .pr_name  = "ON",
756                 .pr_desc  = "round-robin dispatch all PUT messages for "
757                             "wildcard portals"
758         },
759         {
760                 .pr_value = LNET_PTL_ROTOR_RR_RT,
761                 .pr_name  = "RR_RT",
762                 .pr_desc  = "round-robin dispatch routed PUT message for "
763                             "wildcard portals"
764         },
765         {
766                 .pr_value = LNET_PTL_ROTOR_HASH_RT,
767                 .pr_name  = "HASH_RT",
768                 .pr_desc  = "dispatch routed PUT message by hashing source "
769                             "NID for wildcard portals"
770         },
771         {
772                 .pr_value = -1,
773                 .pr_name  = NULL,
774                 .pr_desc  = NULL
775         },
776 };
777
778 static int proc_lnet_portal_rotor(struct ctl_table *table, int write,
779                                   void __user *buffer, size_t *lenp,
780                                   loff_t *ppos)
781 {
782         const int       buf_len = 128;
783         size_t nob = *lenp;
784         loff_t pos = *ppos;
785         char            *buf;
786         char            *tmp;
787         int             rc;
788         int             i;
789
790         if (!write) {
791                 LIBCFS_ALLOC(buf, buf_len);
792                 if (buf == NULL)
793                         return -ENOMEM;
794
795                 lnet_res_lock(0);
796
797                 for (i = 0; portal_rotors[i].pr_value >= 0; i++) {
798                         if (portal_rotors[i].pr_value == portal_rotor)
799                                 break;
800                 }
801
802                 LASSERT(portal_rotors[i].pr_value == portal_rotor);
803                 lnet_res_unlock(0);
804
805                 rc = scnprintf(buf, buf_len,
806                                "{\n\tportals: all\n"
807                                "\trotor: %s\n\tdescription: %s\n}",
808                                portal_rotors[i].pr_name,
809                                portal_rotors[i].pr_desc);
810
811                 if (pos >= min_t(int, rc, buf_len)) {
812                         rc = 0;
813                 } else {
814                         rc = cfs_trace_copyout_string(buffer, nob,
815                                                       buf + pos, "\n");
816                 }
817                 LIBCFS_FREE(buf, buf_len);
818
819                 return rc;
820         }
821
822         buf = memdup_user_nul(buffer, nob);
823         if (IS_ERR(buf))
824                 return PTR_ERR(buf);
825
826         tmp = strim(buf);
827
828         rc = -EINVAL;
829         lnet_res_lock(0);
830         for (i = 0; portal_rotors[i].pr_name != NULL; i++) {
831                 if (strncasecmp(portal_rotors[i].pr_name, tmp,
832                                 strlen(portal_rotors[i].pr_name)) == 0) {
833                         portal_rotor = portal_rotors[i].pr_value;
834                         rc = 0;
835                         break;
836                 }
837         }
838         lnet_res_unlock(0);
839         kfree(buf);
840
841         return rc;
842 }
843
844 static struct ctl_table lnet_table[] = {
845         /*
846          * NB No .strategy entries have been provided since sysctl(8) prefers
847          * to go via /proc for portability.
848          */
849         {
850                 .procname       = "stats",
851                 .mode           = 0644,
852                 .proc_handler   = &proc_lnet_stats,
853         },
854         {
855                 .procname       = "routes",
856                 .mode           = 0444,
857                 .proc_handler   = &proc_lnet_routes,
858         },
859         {
860                 .procname       = "routers",
861                 .mode           = 0444,
862                 .proc_handler   = &proc_lnet_routers,
863         },
864         {
865                 .procname       = "peers",
866                 .mode           = 0644,
867                 .proc_handler   = &proc_lnet_peers,
868         },
869         {
870                 .procname       = "buffers",
871                 .mode           = 0444,
872                 .proc_handler   = &proc_lnet_buffers,
873         },
874         {
875                 .procname       = "nis",
876                 .mode           = 0644,
877                 .proc_handler   = &proc_lnet_nis,
878         },
879         {
880                 .procname       = "portal_rotor",
881                 .mode           = 0644,
882                 .proc_handler   = &proc_lnet_portal_rotor,
883         },
884         {
885                 .procname       = "lnet_lnd_timeout",
886                 .data           = &lnet_lnd_timeout,
887                 .maxlen         = sizeof(lnet_lnd_timeout),
888                 .mode           = 0444,
889                 .proc_handler   = &debugfs_doint,
890         },
891         { .procname = NULL }
892 };
893
894 void lnet_router_debugfs_init(void)
895 {
896         lnet_insert_debugfs(lnet_table);
897 }
898
899 void lnet_router_debugfs_fini(void)
900 {
901         lnet_remove_debugfs(lnet_table);
902 }