Whamcloud - gitweb
LU-56 lnet: Partitioned LNet resources (ME/MD/EQ)
[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, Whamcloud, Inc.
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 cfs_sysctl_table_header_t *lnet_table_header = NULL;
34
35 #ifndef HAVE_SYSCTL_UNNUMBERED
36 #define CTL_LNET         (0x100)
37 enum {
38         PSDEV_LNET_STATS = 100,
39         PSDEV_LNET_ROUTES,
40         PSDEV_LNET_ROUTERS,
41         PSDEV_LNET_PEERS,
42         PSDEV_LNET_BUFFERS,
43         PSDEV_LNET_NIS,
44 };
45 #else
46 #define CTL_LNET           CTL_UNNUMBERED
47 #define PSDEV_LNET_STATS   CTL_UNNUMBERED
48 #define PSDEV_LNET_ROUTES  CTL_UNNUMBERED
49 #define PSDEV_LNET_ROUTERS CTL_UNNUMBERED
50 #define PSDEV_LNET_PEERS   CTL_UNNUMBERED
51 #define PSDEV_LNET_BUFFERS CTL_UNNUMBERED
52 #define PSDEV_LNET_NIS     CTL_UNNUMBERED
53 #endif
54
55 /*
56  * NB: we don't use the highest bit of *ppos because it's signed;
57  *     next 9 bits is used to stash idx (assuming that
58  *     LNET_PEER_HASHSIZE < 512)
59  */
60 #define LNET_LOFFT_BITS        (sizeof(loff_t) * 8)
61 #define LNET_VERSION_BITS      MAX(((MIN(LNET_LOFFT_BITS, 64)) / 4), 8)
62 #define LNET_PHASH_IDX_BITS    9
63 #define LNET_PHASH_NUM_BITS    (LNET_LOFFT_BITS - 1 -\
64                                 LNET_VERSION_BITS - LNET_PHASH_IDX_BITS)
65 #define LNET_PHASH_BITS        (LNET_PHASH_IDX_BITS + LNET_PHASH_NUM_BITS)
66
67 #define LNET_VERSION_BITMASK   ((1ULL << LNET_VERSION_BITS) - 1)
68 #define LNET_PHASH_IDX_BITMASK ((1ULL << LNET_PHASH_IDX_BITS) - 1)
69 #define LNET_PHASH_NUM_BITMASK ((1ULL << LNET_PHASH_NUM_BITS) - 1)
70
71 #define LNET_VERSION_MASK      (LNET_VERSION_BITMASK << LNET_PHASH_BITS)
72 #define LNET_PHASH_IDX_MASK    (LNET_PHASH_IDX_BITMASK << LNET_PHASH_NUM_BITS)
73 #define LNET_PHASH_NUM_MASK    (LNET_PHASH_NUM_BITMASK)
74
75 #define LNET_VERSION_GET(pos)   (int)(((pos) & LNET_VERSION_MASK) >> \
76                                      LNET_PHASH_BITS)
77 #define LNET_PHASH_IDX_GET(pos) (int)(((pos) & LNET_PHASH_IDX_MASK) >> \
78                                       LNET_PHASH_NUM_BITS)
79 #define LNET_PHASH_NUM_GET(pos) (int)((pos) & LNET_PHASH_NUM_MASK)
80 #define LNET_VERSION_VALID_MASK(ver) \
81                                 (unsigned int)((ver) & \
82                                  LNET_VERSION_BITMASK)
83 #define LNET_PHASH_POS_MAKE(ver, idx, num)                                     \
84                                 (((((loff_t)(ver)) & LNET_VERSION_BITMASK) <<  \
85                                    LNET_PHASH_BITS) |                          \
86                                  ((((loff_t)(idx)) & LNET_PHASH_IDX_BITMASK) <<\
87                                    LNET_PHASH_NUM_BITS) |                      \
88                                  ((num) & LNET_PHASH_NUM_BITMASK))
89
90 static int __proc_lnet_stats(void *data, int write,
91                              loff_t pos, void *buffer, int nob)
92 {
93         int              rc;
94         lnet_counters_t *ctrs;
95         int              len;
96         char            *tmpstr;
97         const int        tmpsiz = 256; /* 7 %u and 4 LPU64 */
98
99         if (write) {
100                 LNET_LOCK();
101                 memset(&the_lnet.ln_counters, 0, sizeof(the_lnet.ln_counters));
102                 LNET_UNLOCK();
103                 return 0;
104         }
105
106         /* read */
107
108         LIBCFS_ALLOC(ctrs, sizeof(*ctrs));
109         if (ctrs == NULL)
110                 return -ENOMEM;
111
112         LIBCFS_ALLOC(tmpstr, tmpsiz);
113         if (tmpstr == NULL) {
114                 LIBCFS_FREE(ctrs, sizeof(*ctrs));
115                 return -ENOMEM;
116         }
117
118         LNET_LOCK();
119         *ctrs = the_lnet.ln_counters;
120         LNET_UNLOCK();
121
122         len = snprintf(tmpstr, tmpsiz,
123                        "%u %u %u %u %u %u %u "LPU64" "LPU64" "
124                        LPU64" "LPU64,
125                        ctrs->msgs_alloc, ctrs->msgs_max,
126                        ctrs->errors,
127                        ctrs->send_count, ctrs->recv_count,
128                        ctrs->route_count, ctrs->drop_count,
129                        ctrs->send_length, ctrs->recv_length,
130                        ctrs->route_length, ctrs->drop_length);
131
132         if (pos >= min_t(int, len, strlen(tmpstr)))
133                 rc = 0;
134         else
135                 rc = cfs_trace_copyout_string(buffer, nob,
136                                               tmpstr + pos, "\n");
137
138         LIBCFS_FREE(tmpstr, tmpsiz);
139         LIBCFS_FREE(ctrs, sizeof(*ctrs));
140         return rc;
141 }
142
143 DECLARE_PROC_HANDLER(proc_lnet_stats);
144
145 int LL_PROC_PROTO(proc_lnet_routes)
146 {
147         int        rc     = 0;
148         char      *tmpstr;
149         char      *s;
150         const int  tmpsiz = 256;
151         int        len;
152         int        ver;
153         int        num;
154
155         DECLARE_LL_PROC_PPOS_DECL;
156
157         num = LNET_PHASH_NUM_GET(*ppos);
158         ver = LNET_VERSION_GET(*ppos);
159
160         LASSERT (!write);
161
162         if (*lenp == 0)
163                 return 0;
164
165         LIBCFS_ALLOC(tmpstr, tmpsiz);
166         if (tmpstr == NULL)
167                 return -ENOMEM;
168
169         s = tmpstr; /* points to current position in tmpstr[] */
170
171         if (*ppos == 0) {
172                 s += snprintf(s, tmpstr + tmpsiz - s, "Routing %s\n",
173                               the_lnet.ln_routing ? "enabled" : "disabled");
174                 LASSERT (tmpstr + tmpsiz - s > 0);
175
176                 s += snprintf(s, tmpstr + tmpsiz - s, "%-8s %4s %7s %s\n",
177                               "net", "hops", "state", "router");
178                 LASSERT (tmpstr + tmpsiz - s > 0);
179
180                 LNET_LOCK();
181                 ver = (unsigned int)the_lnet.ln_remote_nets_version;
182                 LNET_UNLOCK();
183                 *ppos = LNET_PHASH_POS_MAKE(ver, 0, num);
184         } else {
185                 cfs_list_t        *n;
186                 cfs_list_t        *r;
187                 lnet_route_t      *route = NULL;
188                 lnet_remotenet_t  *rnet  = NULL;
189                 int                skip  = num - 1;
190
191                 LNET_LOCK();
192
193                 if (ver != LNET_VERSION_VALID_MASK(the_lnet.ln_remote_nets_version)) {
194                         LNET_UNLOCK();
195                         LIBCFS_FREE(tmpstr, tmpsiz);
196                         return -ESTALE;
197                 }
198
199                 n = the_lnet.ln_remote_nets.next;
200
201                 while (n != &the_lnet.ln_remote_nets && route == NULL) {
202                         rnet = cfs_list_entry(n, lnet_remotenet_t, lrn_list);
203
204                         r = rnet->lrn_routes.next;
205
206                         while (r != &rnet->lrn_routes) {
207                                 lnet_route_t *re =
208                                         cfs_list_entry(r, lnet_route_t,
209                                                        lr_list);
210                                 if (skip == 0) {
211                                         route = re;
212                                         break;
213                                 }
214
215                                 skip--;
216                                 r = r->next;
217                         }
218
219                         n = n->next;
220                 }
221
222                 if (route != NULL) {
223                         __u32        net   = rnet->lrn_net;
224                         unsigned int hops  = route->lr_hops;
225                         lnet_nid_t   nid   = route->lr_gateway->lp_nid;
226                         int          alive = route->lr_gateway->lp_alive;
227
228                         s += snprintf(s, tmpstr + tmpsiz - s, "%-8s %4u %7s %s\n",
229                                       libcfs_net2str(net), hops,
230                                       alive ? "up" : "down", libcfs_nid2str(nid));
231                         LASSERT (tmpstr + tmpsiz - s > 0);
232                 }
233
234                 LNET_UNLOCK();
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 (cfs_copy_to_user(buffer, tmpstr, len))
243                         rc = -EFAULT;
244                 else {
245                         num += 1;
246                         *ppos = LNET_PHASH_POS_MAKE(ver, 0, num);
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        num;
267
268         DECLARE_LL_PROC_PPOS_DECL;
269
270         num = LNET_PHASH_NUM_GET(*ppos);
271         ver = LNET_VERSION_GET(*ppos);
272
273         LASSERT (!write);
274
275         if (*lenp == 0)
276                 return 0;
277
278         LIBCFS_ALLOC(tmpstr, tmpsiz);
279         if (tmpstr == NULL)
280                 return -ENOMEM;
281
282         s = tmpstr; /* points to current position in tmpstr[] */
283
284         if (*ppos == 0) {
285                 s += snprintf(s, tmpstr + tmpsiz - s,
286                               "%-4s %7s %9s %6s %12s %9s %8s %7s %s\n",
287                               "ref", "rtr_ref", "alive_cnt", "state", "last_ping",
288                               "ping_sent", "deadline", "down_ni", "router");
289                 LASSERT (tmpstr + tmpsiz - s > 0);
290
291                 LNET_LOCK();
292                 ver = (unsigned int)the_lnet.ln_routers_version;
293                 LNET_UNLOCK();
294                 *ppos = LNET_PHASH_POS_MAKE(ver, 0, num);
295         } else {
296                 cfs_list_t        *r;
297                 lnet_peer_t       *peer = NULL;
298                 int                skip = num - 1;
299
300                 LNET_LOCK();
301
302                 if (ver != LNET_VERSION_VALID_MASK(the_lnet.ln_routers_version)) {
303                         LNET_UNLOCK();
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 = cfs_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_version == LNET_PROTO_PING_VERSION) {
338                                 cfs_list_for_each_entry(rtr, &peer->lp_routes,
339                                                         lr_gwlist) {
340                                         /* downis on any route should be the
341                                          * number of downis on the gateway */
342                                         if (rtr->lr_downis != 0) {
343                                                 down_ni = rtr->lr_downis;
344                                                 break;
345                                         }
346                                 }
347                         }
348
349                         if (deadline == 0)
350                                 s += snprintf(s, tmpstr + tmpsiz - s,
351                                               "%-4d %7d %9d %6s %12d %9d %8s %7d %s\n",
352                                               nrefs, nrtrrefs, alive_cnt,
353                                               alive ? "up" : "down", last_ping,
354                                               pingsent, "NA", down_ni,
355                                               libcfs_nid2str(nid));
356                         else
357                                 s += snprintf(s, tmpstr + tmpsiz - s,
358                                               "%-4d %7d %9d %6s %12d %9d %8lu %7d %s\n",
359                                               nrefs, nrtrrefs, alive_cnt,
360                                               alive ? "up" : "down", last_ping,
361                                               pingsent,
362                                               cfs_duration_sec(cfs_time_sub(deadline, now)),
363                                               down_ni, libcfs_nid2str(nid));
364                         LASSERT (tmpstr + tmpsiz - s > 0);
365                 }
366
367                 LNET_UNLOCK();
368         }
369
370         len = s - tmpstr;     /* how many bytes was written */
371
372         if (len > *lenp) {    /* linux-supplied buffer is too small */
373                 rc = -EINVAL;
374         } else if (len > 0) { /* wrote something */
375                 if (cfs_copy_to_user(buffer, tmpstr, len))
376                         rc = -EFAULT;
377                 else {
378                         num += 1;
379                         *ppos = LNET_PHASH_POS_MAKE(ver, 0, num);
380                 }
381         }
382
383         LIBCFS_FREE(tmpstr, tmpsiz);
384
385         if (rc == 0)
386                 *lenp = len;
387
388         return rc;
389 }
390
391 int LL_PROC_PROTO(proc_lnet_peers)
392 {
393         struct lnet_peer_table  *ptable = the_lnet.ln_peer_table;
394         int        rc = 0;
395         char      *tmpstr;
396         char      *s;
397         const int  tmpsiz      = 256;
398         int        len;
399         int        ver;
400         int        idx;
401         int        num;
402
403         DECLARE_LL_PROC_PPOS_DECL;
404
405         idx = LNET_PHASH_IDX_GET(*ppos);
406         num = LNET_PHASH_NUM_GET(*ppos);
407         ver = LNET_VERSION_GET(*ppos);
408
409         CLASSERT ((1ULL << LNET_PHASH_BITS) > LNET_PEER_HASHSIZE);
410
411         LASSERT (!write);
412
413         if (*lenp == 0)
414                 return 0;
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                 LNET_LOCK();
430                 ver = (unsigned int)ptable->pt_version;
431                 LNET_UNLOCK();
432                 *ppos = LNET_PHASH_POS_MAKE(ver, idx, num);
433
434                 num++;
435         } else {
436                 cfs_list_t        *p    = NULL;
437                 lnet_peer_t       *peer = NULL;
438                 int                skip = num - 1;
439
440                 LNET_LOCK();
441
442                 if (ver != LNET_VERSION_VALID_MASK(ptable->pt_version)) {
443                         LNET_UNLOCK();
444                         LIBCFS_FREE(tmpstr, tmpsiz);
445                         return -ESTALE;
446                 }
447
448                 while (idx < LNET_PEER_HASHSIZE) {
449                         if (p == NULL)
450                                 p = ptable->pt_hash[idx].next;
451
452                         while (p != &ptable->pt_hash[idx]) {
453                                 lnet_peer_t *lp = cfs_list_entry(p, lnet_peer_t,
454                                                                  lp_hashlist);
455                                 if (skip == 0) {
456                                         peer = lp;
457
458                                         /* minor optimization: start from idx+1
459                                          * on next iteration if we've just
460                                          * drained lp_hashlist */
461                                         if (lp->lp_hashlist.next ==
462                                             &ptable->pt_hash[idx]) {
463                                                 num = 1;
464                                                 idx++;
465                                         } else {
466                                                 num++;
467                                         }
468
469                                         break;
470                                 }
471
472                                 skip--;
473                                 p = lp->lp_hashlist.next;
474                         }
475
476                         if (peer != NULL)
477                                 break;
478
479                         p = NULL;
480                         num = 1;
481                         idx++;
482                 }
483
484                 if (peer != NULL) {
485                         lnet_nid_t nid       = peer->lp_nid;
486                         int        nrefs     = peer->lp_refcount;
487                         int        lastalive = -1;
488                         char      *aliveness = "NA";
489                         int        maxcr     = peer->lp_ni->ni_peertxcredits;
490                         int        txcr      = peer->lp_txcredits;
491                         int        mintxcr   = peer->lp_mintxcredits;
492                         int        rtrcr     = peer->lp_rtrcredits;
493                         int        minrtrcr  = peer->lp_minrtrcredits;
494                         int        txqnob    = peer->lp_txqnob;
495
496                         if (lnet_isrouter(peer) ||
497                             lnet_peer_aliveness_enabled(peer))
498                                 aliveness = peer->lp_alive ? "up" : "down";
499
500                         if (lnet_peer_aliveness_enabled(peer)) {
501                                 cfs_time_t     now = cfs_time_current();
502                                 cfs_duration_t delta;
503
504                                 delta = cfs_time_sub(now, peer->lp_last_alive);
505                                 lastalive = cfs_duration_sec(delta);
506
507                                 /* No need to mess up peers contents with
508                                  * arbitrarily long integers - it suffices to
509                                  * know that lastalive is more than 10000s old
510                                  */
511                                 if (lastalive >= 10000)
512                                         lastalive = 9999;
513                         }
514
515                         s += snprintf(s, tmpstr + tmpsiz - s,
516                                       "%-24s %4d %5s %5d %5d %5d %5d %5d %5d %d\n",
517                                       libcfs_nid2str(nid), nrefs, aliveness,
518                                       lastalive, maxcr, rtrcr, minrtrcr, txcr,
519                                       mintxcr, txqnob);
520                         LASSERT (tmpstr + tmpsiz - s > 0);
521                 }
522
523                 LNET_UNLOCK();
524         }
525
526         len = s - tmpstr;     /* how many bytes was written */
527
528         if (len > *lenp) {    /* linux-supplied buffer is too small */
529                 rc = -EINVAL;
530         } else if (len > 0) { /* wrote something */
531                 if (cfs_copy_to_user(buffer, tmpstr, len))
532                         rc = -EFAULT;
533                 else
534                         *ppos = LNET_PHASH_POS_MAKE(ver, idx, num);
535         }
536
537         LIBCFS_FREE(tmpstr, tmpsiz);
538
539         if (rc == 0)
540                 *lenp = len;
541
542         return rc;
543 }
544
545 static int __proc_lnet_buffers(void *data, int write,
546                                loff_t pos, void *buffer, int nob)
547 {
548
549         int              rc;
550         int              len;
551         char            *s;
552         char            *tmpstr;
553         const int        tmpsiz = 64 * (LNET_NRBPOOLS + 1); /* (4 %d) * 4 */
554         int              idx;
555
556         LASSERT (!write);
557
558         LIBCFS_ALLOC(tmpstr, tmpsiz);
559         if (tmpstr == NULL)
560                 return -ENOMEM;
561
562         s = tmpstr; /* points to current position in tmpstr[] */
563
564         s += snprintf(s, tmpstr + tmpsiz - s,
565                       "%5s %5s %7s %7s\n",
566                       "pages", "count", "credits", "min");
567         LASSERT (tmpstr + tmpsiz - s > 0);
568
569         LNET_LOCK();
570
571         for (idx = 0; idx < LNET_NRBPOOLS; idx++) {
572                 lnet_rtrbufpool_t *rbp = &the_lnet.ln_rtrpools[idx];
573
574                 int npages = rbp->rbp_npages;
575                 int nbuf   = rbp->rbp_nbuffers;
576                 int cr     = rbp->rbp_credits;
577                 int mincr  = rbp->rbp_mincredits;
578
579                 s += snprintf(s, tmpstr + tmpsiz - s,
580                               "%5d %5d %7d %7d\n",
581                               npages, nbuf, cr, mincr);
582                 LASSERT (tmpstr + tmpsiz - s > 0);
583         }
584
585         LNET_UNLOCK();
586
587         len = s - tmpstr;
588
589         if (pos >= min_t(int, len, strlen(tmpstr)))
590                 rc = 0;
591         else
592                 rc = cfs_trace_copyout_string(buffer, nob,
593                                               tmpstr + pos, NULL);
594
595         LIBCFS_FREE(tmpstr, tmpsiz);
596         return rc;
597 }
598
599 DECLARE_PROC_HANDLER(proc_lnet_buffers);
600
601 int LL_PROC_PROTO(proc_lnet_nis)
602 {
603         int        rc = 0;
604         char      *tmpstr;
605         char      *s;
606         const int  tmpsiz = 256;
607         int        len;
608
609         DECLARE_LL_PROC_PPOS_DECL;
610
611         LASSERT (!write);
612
613         if (*lenp == 0)
614                 return 0;
615
616         LIBCFS_ALLOC(tmpstr, tmpsiz);
617         if (tmpstr == NULL)
618                 return -ENOMEM;
619
620         s = tmpstr; /* points to current position in tmpstr[] */
621
622         if (*ppos == 0) {
623                 s += snprintf(s, tmpstr + tmpsiz - s,
624                               "%-24s %6s %5s %4s %4s %4s %5s %5s %5s\n",
625                               "nid", "status", "alive", "refs", "peer",
626                               "rtr", "max", "tx", "min");
627                 LASSERT (tmpstr + tmpsiz - s > 0);
628         } else {
629                 cfs_list_t        *n;
630                 lnet_ni_t         *ni   = NULL;
631                 int                skip = *ppos - 1;
632
633                 LNET_LOCK();
634
635                 n = the_lnet.ln_nis.next;
636
637                 while (n != &the_lnet.ln_nis) {
638                         lnet_ni_t *a_ni = cfs_list_entry(n, lnet_ni_t, ni_list);
639
640                         if (skip == 0) {
641                                 ni = a_ni;
642                                 break;
643                         }
644
645                         skip--;
646                         n = n->next;
647                 }
648
649                 if (ni != NULL) {
650                         cfs_time_t now = cfs_time_current();
651                         int        last_alive = -1;
652                         int        maxtxcr = ni->ni_maxtxcredits;
653                         int        txcr = ni->ni_txcredits;
654                         int        mintxcr = ni->ni_mintxcredits;
655                         int        npeertxcr = ni->ni_peertxcredits;
656                         int        npeerrtrcr = ni->ni_peerrtrcredits;
657                         lnet_nid_t nid = ni->ni_nid;
658                         int        nref = ni->ni_refcount;
659                         char      *stat;
660
661                         if (the_lnet.ln_routing)
662                                 last_alive = cfs_duration_sec(cfs_time_sub(now,
663                                                             ni->ni_last_alive));
664                         if (ni->ni_lnd->lnd_type == LOLND)  /* @lo forever alive */
665                                 last_alive = 0;
666
667                         LASSERT (ni->ni_status != NULL);
668                         stat = (ni->ni_status->ns_status == LNET_NI_STATUS_UP) ?
669                                                                   "up" : "down";
670
671                         s += snprintf(s, tmpstr + tmpsiz - s,
672                                       "%-24s %6s %5d %4d %4d %4d %5d %5d %5d\n",
673                                       libcfs_nid2str(nid), stat, last_alive, nref,
674                                       npeertxcr, npeerrtrcr, maxtxcr,
675                                       txcr, mintxcr);
676                         LASSERT (tmpstr + tmpsiz - s > 0);
677                 }
678
679                 LNET_UNLOCK();
680         }
681
682         len = s - tmpstr;     /* how many bytes was written */
683
684         if (len > *lenp) {    /* linux-supplied buffer is too small */
685                 rc = -EINVAL;
686         } else if (len > 0) { /* wrote something */
687                 if (cfs_copy_to_user(buffer, tmpstr, len))
688                         rc = -EFAULT;
689                 else
690                         *ppos += 1;
691         }
692
693         LIBCFS_FREE(tmpstr, tmpsiz);
694
695         if (rc == 0)
696                 *lenp = len;
697
698         return rc;
699 }
700
701 static cfs_sysctl_table_t lnet_table[] = {
702         /*
703          * NB No .strategy entries have been provided since sysctl(8) prefers
704          * to go via /proc for portability.
705          */
706         {
707                 INIT_CTL_NAME(PSDEV_LNET_STATS)
708                 .procname = "stats",
709                 .mode     = 0644,
710                 .proc_handler = &proc_lnet_stats,
711         },
712         {
713                 INIT_CTL_NAME(PSDEV_LNET_ROUTES)
714                 .procname = "routes",
715                 .mode     = 0444,
716                 .proc_handler = &proc_lnet_routes,
717         },
718         {
719                 INIT_CTL_NAME(PSDEV_LNET_ROUTERS)
720                 .procname = "routers",
721                 .mode     = 0444,
722                 .proc_handler = &proc_lnet_routers,
723         },
724         {
725                 INIT_CTL_NAME(PSDEV_LNET_PEERS)
726                 .procname = "peers",
727                 .mode     = 0444,
728                 .proc_handler = &proc_lnet_peers,
729         },
730         {
731                 INIT_CTL_NAME(PSDEV_LNET_PEERS)
732                 .procname = "buffers",
733                 .mode     = 0444,
734                 .proc_handler = &proc_lnet_buffers,
735         },
736         {
737                 INIT_CTL_NAME(PSDEV_LNET_NIS)
738                 .procname = "nis",
739                 .mode     = 0444,
740                 .proc_handler = &proc_lnet_nis,
741         },
742         {
743                 INIT_CTL_NAME(0)
744         }
745 };
746
747 static cfs_sysctl_table_t top_table[] = {
748         {
749                 INIT_CTL_NAME(CTL_LNET)
750                 .procname = "lnet",
751                 .mode     = 0555,
752                 .data     = NULL,
753                 .maxlen   = 0,
754                 .child    = lnet_table,
755         },
756         {
757                 INIT_CTL_NAME(0)
758         }
759 };
760
761 void
762 lnet_proc_init(void)
763 {
764 #ifdef CONFIG_SYSCTL
765         if (lnet_table_header == NULL)
766                 lnet_table_header = cfs_register_sysctl_table(top_table, 0);
767 #endif
768 }
769
770 void
771 lnet_proc_fini(void)
772 {
773 #ifdef CONFIG_SYSCTL
774         if (lnet_table_header != NULL)
775                 cfs_unregister_sysctl_table(lnet_table_header);
776
777         lnet_table_header = NULL;
778 #endif
779 }
780
781 #else
782
783 void
784 lnet_proc_init(void)
785 {
786 }
787
788 void
789 lnet_proc_fini(void)
790 {
791 }
792
793 #endif