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