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