Whamcloud - gitweb
LU-4423 lnet: remove memset(0) after LIBCFS_ALLOC
[fs/lustre-release.git] / lnet / selftest / console.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2012, 2014, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lnet/selftest/conctl.c
37  *
38  * Infrastructure of LST console
39  *
40  * Author: Liang Zhen <liangzhen@clusterfs.com>
41  */
42
43
44 #include <libcfs/libcfs.h>
45 #include <lnet/lib-lnet.h>
46 #include "console.h"
47 #include "conrpc.h"
48
49 #define LST_NODE_STATE_COUNTER(nd, p)                   \
50 do {                                                    \
51         if ((nd)->nd_state == LST_NODE_ACTIVE)          \
52                 (p)->nle_nactive ++;                    \
53         else if ((nd)->nd_state == LST_NODE_BUSY)       \
54                 (p)->nle_nbusy ++;                      \
55         else if ((nd)->nd_state == LST_NODE_DOWN)       \
56                 (p)->nle_ndown ++;                      \
57         else                                            \
58                 (p)->nle_nunknown ++;                   \
59         (p)->nle_nnode ++;                              \
60 } while (0)
61
62 lstcon_session_t        console_session;
63
64 static void
65 lstcon_node_get(lstcon_node_t *nd)
66 {
67         LASSERT (nd->nd_ref >= 1);
68
69         nd->nd_ref++;
70 }
71
72 static int
73 lstcon_node_find(lnet_process_id_t id, lstcon_node_t **ndpp, int create)
74 {
75         lstcon_ndlink_t *ndl;
76         unsigned int     idx = LNET_NIDADDR(id.nid) % LST_GLOBAL_HASHSIZE;
77
78         LASSERT(id.nid != LNET_NID_ANY);
79
80         list_for_each_entry(ndl, &console_session.ses_ndl_hash[idx],
81                             ndl_hlink) {
82                 if (ndl->ndl_node->nd_id.nid != id.nid ||
83                     ndl->ndl_node->nd_id.pid != id.pid)
84                         continue;
85
86                 lstcon_node_get(ndl->ndl_node);
87                 *ndpp = ndl->ndl_node;
88                 return 0;
89         }
90
91         if (!create)
92                 return -ENOENT;
93
94         LIBCFS_ALLOC(*ndpp, sizeof(lstcon_node_t) + sizeof(lstcon_ndlink_t));
95         if (*ndpp == NULL)
96                 return -ENOMEM;
97
98         ndl = (lstcon_ndlink_t *)(*ndpp + 1);
99
100         ndl->ndl_node = *ndpp;
101
102         ndl->ndl_node->nd_ref   = 1;
103         ndl->ndl_node->nd_id    = id;
104         ndl->ndl_node->nd_stamp = cfs_time_current();
105         ndl->ndl_node->nd_state = LST_NODE_UNKNOWN;
106         ndl->ndl_node->nd_timeout = 0;
107         memset(&ndl->ndl_node->nd_ping, 0, sizeof(lstcon_rpc_t));
108
109         /* queued in global hash & list, no refcount is taken by
110          * global hash & list, if caller release his refcount,
111          * node will be released */
112         list_add_tail(&ndl->ndl_hlink, &console_session.ses_ndl_hash[idx]);
113         list_add_tail(&ndl->ndl_link, &console_session.ses_ndl_list);
114
115         return 0;
116 }
117
118 static void
119 lstcon_node_put(lstcon_node_t *nd)
120 {
121         lstcon_ndlink_t *ndl;
122
123         LASSERT(nd->nd_ref > 0);
124
125         if (--nd->nd_ref > 0)
126                 return;
127
128         ndl = (lstcon_ndlink_t *)(nd + 1);
129
130         LASSERT(!list_empty(&ndl->ndl_link));
131         LASSERT(!list_empty(&ndl->ndl_hlink));
132
133         /* remove from session */
134         list_del(&ndl->ndl_link);
135         list_del(&ndl->ndl_hlink);
136
137         LIBCFS_FREE(nd, sizeof(lstcon_node_t) + sizeof(lstcon_ndlink_t));
138 }
139
140 static int
141 lstcon_ndlink_find(struct list_head *hash,
142                    lnet_process_id_t id, lstcon_ndlink_t **ndlpp, int create)
143 {
144         unsigned int     idx = LNET_NIDADDR(id.nid) % LST_NODE_HASHSIZE;
145         lstcon_ndlink_t *ndl;
146         lstcon_node_t   *nd;
147         int              rc;
148
149         if (id.nid == LNET_NID_ANY)
150                 return -EINVAL;
151
152         /* search in hash */
153         list_for_each_entry(ndl, &hash[idx], ndl_hlink) {
154                 if (ndl->ndl_node->nd_id.nid != id.nid ||
155                     ndl->ndl_node->nd_id.pid != id.pid)
156                         continue;
157
158                 *ndlpp = ndl;
159                 return 0;
160         }
161
162         if (create == 0)
163                 return -ENOENT;
164
165         /* find or create in session hash */
166         rc = lstcon_node_find(id, &nd, (create == 1) ? 1 : 0);
167         if (rc != 0)
168                 return rc;
169
170         LIBCFS_ALLOC(ndl, sizeof(lstcon_ndlink_t));
171         if (ndl == NULL) {
172                 lstcon_node_put(nd);
173                 return -ENOMEM;
174         }
175
176         *ndlpp = ndl;
177
178         ndl->ndl_node = nd;
179         INIT_LIST_HEAD(&ndl->ndl_link);
180         list_add_tail(&ndl->ndl_hlink, &hash[idx]);
181
182         return 0;
183 }
184
185 static void
186 lstcon_ndlink_release(lstcon_ndlink_t *ndl)
187 {
188         LASSERT(list_empty(&ndl->ndl_link));
189         LASSERT(!list_empty(&ndl->ndl_hlink));
190
191         list_del(&ndl->ndl_hlink); /* delete from hash */
192         lstcon_node_put(ndl->ndl_node);
193
194         LIBCFS_FREE(ndl, sizeof(*ndl));
195 }
196
197 static int
198 lstcon_group_alloc(char *name, lstcon_group_t **grpp)
199 {
200         lstcon_group_t *grp;
201         int             i;
202
203         LIBCFS_ALLOC(grp, offsetof(lstcon_group_t,
204                                    grp_ndl_hash[LST_NODE_HASHSIZE]));
205         if (grp == NULL)
206                 return -ENOMEM;
207
208         grp->grp_ref = 1;
209         if (name != NULL) {
210                 if (strlen(name) > sizeof(grp->grp_name)-1) {
211                         LIBCFS_FREE(grp, offsetof(lstcon_group_t,
212                                           grp_ndl_hash[LST_NODE_HASHSIZE]));
213                         return -E2BIG;
214                 }
215                 strncpy(grp->grp_name, name, sizeof(grp->grp_name));
216         }
217
218         INIT_LIST_HEAD(&grp->grp_link);
219         INIT_LIST_HEAD(&grp->grp_ndl_list);
220         INIT_LIST_HEAD(&grp->grp_trans_list);
221
222         for (i = 0; i < LST_NODE_HASHSIZE; i++)
223                 INIT_LIST_HEAD(&grp->grp_ndl_hash[i]);
224
225         *grpp = grp;
226
227         return 0;
228 }
229
230 static void
231 lstcon_group_addref(lstcon_group_t *grp)
232 {
233         grp->grp_ref++;
234 }
235
236 static void lstcon_group_ndlink_release(lstcon_group_t *, lstcon_ndlink_t *);
237
238 static void
239 lstcon_group_drain(lstcon_group_t *grp, int keep)
240 {
241         lstcon_ndlink_t *ndl;
242         lstcon_ndlink_t *tmp;
243
244         list_for_each_entry_safe(ndl, tmp, &grp->grp_ndl_list, ndl_link) {
245                 if ((ndl->ndl_node->nd_state & keep) == 0)
246                         lstcon_group_ndlink_release(grp, ndl);
247         }
248 }
249
250 static void
251 lstcon_group_decref(lstcon_group_t *grp)
252 {
253         int i;
254
255         if (--grp->grp_ref > 0)
256                 return;
257
258         if (!list_empty(&grp->grp_link))
259                 list_del(&grp->grp_link);
260
261         lstcon_group_drain(grp, 0);
262
263         for (i = 0; i < LST_NODE_HASHSIZE; i++)
264                 LASSERT(list_empty(&grp->grp_ndl_hash[i]));
265
266         LIBCFS_FREE(grp, offsetof(lstcon_group_t,
267                                   grp_ndl_hash[LST_NODE_HASHSIZE]));
268 }
269
270 static int
271 lstcon_group_find(const char *name, lstcon_group_t **grpp)
272 {
273         lstcon_group_t *grp;
274
275         list_for_each_entry(grp, &console_session.ses_grp_list, grp_link) {
276                 if (strncmp(grp->grp_name, name, LST_NAME_SIZE) != 0)
277                         continue;
278
279                 lstcon_group_addref(grp);  /* +1 ref for caller */
280                 *grpp = grp;
281                 return 0;
282         }
283
284         return -ENOENT;
285 }
286
287 static int
288 lstcon_group_ndlink_find(lstcon_group_t *grp, lnet_process_id_t id,
289                          lstcon_ndlink_t **ndlpp, int create)
290 {
291         int rc;
292
293         rc = lstcon_ndlink_find(&grp->grp_ndl_hash[0], id, ndlpp, create);
294         if (rc != 0)
295                 return rc;
296
297         if (!list_empty(&(*ndlpp)->ndl_link))
298                 return 0;
299
300         list_add_tail(&(*ndlpp)->ndl_link, &grp->grp_ndl_list);
301         grp->grp_nnode++;
302
303         return 0;
304 }
305
306 static void
307 lstcon_group_ndlink_release(lstcon_group_t *grp, lstcon_ndlink_t *ndl)
308 {
309         list_del_init(&ndl->ndl_link);
310         lstcon_ndlink_release(ndl);
311         grp->grp_nnode--;
312 }
313
314 static void
315 lstcon_group_ndlink_move(lstcon_group_t *old,
316                          lstcon_group_t *new, lstcon_ndlink_t *ndl)
317 {
318         unsigned int idx = LNET_NIDADDR(ndl->ndl_node->nd_id.nid) %
319                                         LST_NODE_HASHSIZE;
320
321         list_del(&ndl->ndl_hlink);
322         list_del(&ndl->ndl_link);
323         old->grp_nnode--;
324
325         list_add_tail(&ndl->ndl_hlink, &new->grp_ndl_hash[idx]);
326         list_add_tail(&ndl->ndl_link, &new->grp_ndl_list);
327         new->grp_nnode++;
328
329         return;
330 }
331
332 static void
333 lstcon_group_move(lstcon_group_t *old, lstcon_group_t *new)
334 {
335         lstcon_ndlink_t *ndl;
336
337         while (!list_empty(&old->grp_ndl_list)) {
338                 ndl = list_entry(old->grp_ndl_list.next,
339                                  lstcon_ndlink_t, ndl_link);
340                 lstcon_group_ndlink_move(old, new, ndl);
341         }
342 }
343
344 static int
345 lstcon_sesrpc_condition(int transop, lstcon_node_t *nd, void *arg)
346 {
347         lstcon_group_t *grp = (lstcon_group_t *)arg;
348
349         switch (transop) {
350         case LST_TRANS_SESNEW:
351                 if (nd->nd_state == LST_NODE_ACTIVE)
352                         return 0;
353                 break;
354
355         case LST_TRANS_SESEND:
356                 if (nd->nd_state != LST_NODE_ACTIVE)
357                         return 0;
358
359                 if (grp != NULL && nd->nd_ref > 1)
360                         return 0;
361                 break;
362
363         case LST_TRANS_SESQRY:
364                 break;
365
366         default:
367                 LBUG();
368         }
369
370         return 1;
371 }
372
373 static int
374 lstcon_sesrpc_readent(int transop, srpc_msg_t *msg,
375                       lstcon_rpc_ent_t __user *ent_up)
376 {
377         srpc_debug_reply_t *rep;
378
379         switch (transop) {
380         case LST_TRANS_SESNEW:
381         case LST_TRANS_SESEND:
382                 return 0;
383
384         case LST_TRANS_SESQRY:
385                 rep = &msg->msg_body.dbg_reply;
386
387                 if (copy_to_user(&ent_up->rpe_priv[0],
388                                      &rep->dbg_timeout, sizeof(int)) ||
389                     copy_to_user(&ent_up->rpe_payload[0],
390                                      &rep->dbg_name, LST_NAME_SIZE))
391                         return -EFAULT;
392
393                 return 0;
394
395         default:
396                 LBUG();
397         }
398
399         return 0;
400 }
401
402 static int
403 lstcon_group_nodes_add(lstcon_group_t *grp,
404                        int count, lnet_process_id_t __user *ids_up,
405                        unsigned *featp, struct list_head __user *result_up)
406 {
407         lstcon_rpc_trans_t      *trans;
408         lstcon_ndlink_t         *ndl;
409         lstcon_group_t          *tmp;
410         lnet_process_id_t        id;
411         int                      i;
412         int                      rc;
413
414         rc = lstcon_group_alloc(NULL, &tmp);
415         if (rc != 0) {
416                 CERROR("Out of memory\n");
417                 return -ENOMEM;
418         }
419
420         for (i = 0 ; i < count; i++) {
421                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
422                         rc = -EFAULT;
423                         break;
424                 }
425
426                 /* skip if it's in this group already */
427                 rc = lstcon_group_ndlink_find(grp, id, &ndl, 0);
428                 if (rc == 0)
429                         continue;
430
431                 /* add to tmp group */
432                 rc = lstcon_group_ndlink_find(tmp, id, &ndl, 1);
433                 if (rc != 0) {
434                         CERROR("Can't create ndlink, out of memory\n");
435                         break;
436                 }
437         }
438
439         if (rc != 0) {
440                 lstcon_group_decref(tmp);
441                 return rc;
442         }
443
444         rc = lstcon_rpc_trans_ndlist(&tmp->grp_ndl_list,
445                                      &tmp->grp_trans_list, LST_TRANS_SESNEW,
446                                      tmp, lstcon_sesrpc_condition, &trans);
447         if (rc != 0) {
448                 CERROR("Can't create transaction: %d\n", rc);
449                 lstcon_group_decref(tmp);
450                 return rc;
451         }
452
453         /* post all RPCs */
454         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
455
456         rc = lstcon_rpc_trans_interpreter(trans, result_up,
457                                           lstcon_sesrpc_readent);
458         *featp = trans->tas_features;
459
460         /* destroy all RPGs */
461         lstcon_rpc_trans_destroy(trans);
462
463         lstcon_group_move(tmp, grp);
464         lstcon_group_decref(tmp);
465
466         return rc;
467 }
468
469 static int
470 lstcon_group_nodes_remove(lstcon_group_t *grp,
471                           int count, lnet_process_id_t __user *ids_up,
472                           struct list_head __user *result_up)
473 {
474         lstcon_rpc_trans_t     *trans;
475         lstcon_ndlink_t        *ndl;
476         lstcon_group_t         *tmp;
477         lnet_process_id_t       id;
478         int                     rc;
479         int                     i;
480
481         /* End session and remove node from the group */
482
483         rc = lstcon_group_alloc(NULL, &tmp);
484         if (rc != 0) {
485                 CERROR("Out of memory\n");
486                 return -ENOMEM;
487         }
488
489         for (i = 0; i < count; i++) {
490                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
491                         rc = -EFAULT;
492                         goto error;
493                 }
494
495                 /* move node to tmp group */
496                 if (lstcon_group_ndlink_find(grp, id, &ndl, 0) == 0)
497                         lstcon_group_ndlink_move(grp, tmp, ndl);
498         }
499
500         rc = lstcon_rpc_trans_ndlist(&tmp->grp_ndl_list,
501                                      &tmp->grp_trans_list, LST_TRANS_SESEND,
502                                      tmp, lstcon_sesrpc_condition, &trans);
503         if (rc != 0) {
504                 CERROR("Can't create transaction: %d\n", rc);
505                 goto error;
506         }
507
508         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
509
510         rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
511
512         lstcon_rpc_trans_destroy(trans);
513         /* release nodes anyway, because we can't rollback status */
514         lstcon_group_decref(tmp);
515
516         return rc;
517 error:
518         lstcon_group_move(tmp, grp);
519         lstcon_group_decref(tmp);
520
521         return rc;
522 }
523
524 int
525 lstcon_group_add(char *name)
526 {
527         lstcon_group_t *grp;
528         int             rc;
529
530         rc = (lstcon_group_find(name, &grp) == 0)? -EEXIST: 0;
531         if (rc != 0) {
532                 /* find a group with same name */
533                 lstcon_group_decref(grp);
534                 return rc;
535         }
536
537         rc = lstcon_group_alloc(name, &grp);
538         if (rc != 0) {
539                 CERROR("Can't allocate descriptor for group %s\n", name);
540                 return -ENOMEM;
541         }
542
543         list_add_tail(&grp->grp_link, &console_session.ses_grp_list);
544
545         return rc;
546 }
547
548 int
549 lstcon_nodes_add(char *name, int count, lnet_process_id_t __user *ids_up,
550                  unsigned *featp, struct list_head __user *result_up)
551 {
552         lstcon_group_t         *grp;
553         int                     rc;
554
555         LASSERT (count > 0);
556         LASSERT (ids_up != NULL);
557
558         rc = lstcon_group_find(name, &grp);
559         if (rc != 0) {
560                 CDEBUG(D_NET, "Can't find group %s\n", name);
561                 return rc;
562         }
563
564         if (grp->grp_ref > 2) {
565                 /* referred by other threads or test */
566                 CDEBUG(D_NET, "Group %s is busy\n", name);
567                 lstcon_group_decref(grp);
568
569                 return -EBUSY;
570         }
571
572         rc = lstcon_group_nodes_add(grp, count, ids_up, featp, result_up);
573
574         lstcon_group_decref(grp);
575
576         return rc;
577 }
578
579 int
580 lstcon_group_del(char *name)
581 {
582         lstcon_rpc_trans_t *trans;
583         lstcon_group_t     *grp;
584         int                 rc;
585
586         rc = lstcon_group_find(name, &grp);
587         if (rc != 0) {
588                 CDEBUG(D_NET, "Can't find group: %s\n", name);
589                 return rc;
590         }
591
592         if (grp->grp_ref > 2) {
593                 /* referred by others threads or test */
594                 CDEBUG(D_NET, "Group %s is busy\n", name);
595                 lstcon_group_decref(grp);
596                 return -EBUSY;
597         }
598
599         rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
600                                      &grp->grp_trans_list, LST_TRANS_SESEND,
601                                      grp, lstcon_sesrpc_condition, &trans);
602         if (rc != 0) {
603                 CERROR("Can't create transaction: %d\n", rc);
604                 lstcon_group_decref(grp);
605                 return rc;
606         }
607
608         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
609
610         lstcon_rpc_trans_destroy(trans);
611
612         lstcon_group_decref(grp);
613         /* -ref for session, it's destroyed,
614          * status can't be rolled back, destroy group anway */
615         lstcon_group_decref(grp);
616
617         return rc;
618 }
619
620 int
621 lstcon_group_clean(char *name, int args)
622 {
623         lstcon_group_t *grp = NULL;
624         int             rc;
625
626         rc = lstcon_group_find(name, &grp);
627         if (rc != 0) {
628                 CDEBUG(D_NET, "Can't find group %s\n", name);
629                 return rc;
630         }
631
632         if (grp->grp_ref > 2) {
633                 /* referred by test */
634                 CDEBUG(D_NET, "Group %s is busy\n", name);
635                 lstcon_group_decref(grp);
636                 return -EBUSY;
637         }
638
639         args = (LST_NODE_ACTIVE | LST_NODE_BUSY |
640                 LST_NODE_DOWN | LST_NODE_UNKNOWN) & ~args;
641
642         lstcon_group_drain(grp, args);
643
644         lstcon_group_decref(grp);
645         /* release empty group */
646         if (list_empty(&grp->grp_ndl_list))
647                 lstcon_group_decref(grp);
648
649         return 0;
650 }
651
652 int
653 lstcon_nodes_remove(char *name, int count,
654                     lnet_process_id_t __user *ids_up,
655                     struct list_head __user *result_up)
656 {
657         lstcon_group_t *grp = NULL;
658         int             rc;
659
660         rc = lstcon_group_find(name, &grp);
661         if (rc != 0) {
662                 CDEBUG(D_NET, "Can't find group: %s\n", name);
663                 return rc;
664         }
665
666         if (grp->grp_ref > 2) {
667                 /* referred by test */
668                 CDEBUG(D_NET, "Group %s is busy\n", name);
669                 lstcon_group_decref(grp);
670                 return -EBUSY;
671         }
672
673         rc = lstcon_group_nodes_remove(grp, count, ids_up, result_up);
674
675         lstcon_group_decref(grp);
676         /* release empty group */
677         if (list_empty(&grp->grp_ndl_list))
678                 lstcon_group_decref(grp);
679
680         return rc;
681 }
682
683 int
684 lstcon_group_refresh(char *name, struct list_head __user *result_up)
685 {
686         lstcon_rpc_trans_t      *trans;
687         lstcon_group_t          *grp;
688         int                      rc;
689
690         rc = lstcon_group_find(name, &grp);
691         if (rc != 0) {
692                 CDEBUG(D_NET, "Can't find group: %s\n", name);
693                 return rc;
694         }
695
696         if (grp->grp_ref > 2) {
697                 /* referred by test */
698                 CDEBUG(D_NET, "Group %s is busy\n", name);
699                 lstcon_group_decref(grp);
700                 return -EBUSY;
701         }
702
703         /* re-invite all inactive nodes int the group */
704         rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
705                                      &grp->grp_trans_list, LST_TRANS_SESNEW,
706                                      grp, lstcon_sesrpc_condition, &trans);
707         if (rc != 0) {
708                 /* local error, return */
709                 CDEBUG(D_NET, "Can't create transaction: %d\n", rc);
710                 lstcon_group_decref(grp);
711                 return rc;
712         }
713
714         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
715
716         rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
717
718         lstcon_rpc_trans_destroy(trans);
719         /* -ref for me */
720         lstcon_group_decref(grp);
721
722         return rc;
723 }
724
725 int
726 lstcon_group_list(int index, int len, char __user *name_up)
727 {
728         lstcon_group_t *grp;
729
730         LASSERT(index >= 0);
731         LASSERT(name_up != NULL);
732
733         list_for_each_entry(grp, &console_session.ses_grp_list, grp_link) {
734                 if (index-- == 0) {
735                         return copy_to_user(name_up, grp->grp_name, len) ?
736                                             -EFAULT : 0;
737                 }
738         }
739
740         return -ENOENT;
741 }
742
743 static int
744 lstcon_nodes_getent(struct list_head *head, int *index_p,
745                     int *count_p, lstcon_node_ent_t __user *dents_up)
746 {
747         lstcon_ndlink_t  *ndl;
748         lstcon_node_t    *nd;
749         int               count = 0;
750         int               index = 0;
751
752         LASSERT(index_p != NULL && count_p != NULL);
753         LASSERT(dents_up != NULL);
754         LASSERT(*index_p >= 0);
755         LASSERT(*count_p > 0);
756
757         list_for_each_entry(ndl, head, ndl_link) {
758                 if (index++ < *index_p)
759                         continue;
760
761                 if (count >= *count_p)
762                         break;
763
764                 nd = ndl->ndl_node;
765                 if (copy_to_user(&dents_up[count].nde_id,
766                                      &nd->nd_id, sizeof(nd->nd_id)) ||
767                     copy_to_user(&dents_up[count].nde_state,
768                                      &nd->nd_state, sizeof(nd->nd_state)))
769                         return -EFAULT;
770
771                 count ++;
772         }
773
774         if (index <= *index_p)
775                 return -ENOENT;
776
777         *count_p = count;
778         *index_p = index;
779
780         return 0;
781 }
782
783 int
784 lstcon_group_info(char *name, lstcon_ndlist_ent_t __user *gents_p,
785                   int *index_p, int *count_p,
786                   lstcon_node_ent_t __user *dents_up)
787 {
788         lstcon_ndlist_ent_t *gentp;
789         lstcon_group_t      *grp;
790         lstcon_ndlink_t     *ndl;
791         int                  rc;
792
793         rc = lstcon_group_find(name, &grp);
794         if (rc != 0) {
795                 CDEBUG(D_NET, "Can't find group %s\n", name);
796                 return rc;
797         }
798
799         if (dents_up != NULL) {
800                 /* verbose query */
801                 rc = lstcon_nodes_getent(&grp->grp_ndl_list,
802                                          index_p, count_p, dents_up);
803                 lstcon_group_decref(grp);
804
805                 return rc;
806         }
807
808         /* non-verbose query */
809         LIBCFS_ALLOC(gentp, sizeof(lstcon_ndlist_ent_t));
810         if (gentp == NULL) {
811                 CERROR("Can't allocate ndlist_ent\n");
812                 lstcon_group_decref(grp);
813
814                 return -ENOMEM;
815         }
816
817         list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link)
818                 LST_NODE_STATE_COUNTER(ndl->ndl_node, gentp);
819
820         rc = copy_to_user(gents_p, gentp,
821                           sizeof(lstcon_ndlist_ent_t)) ? -EFAULT : 0;
822
823         LIBCFS_FREE(gentp, sizeof(lstcon_ndlist_ent_t));
824
825         lstcon_group_decref(grp);
826
827         return 0;
828 }
829
830 static int
831 lstcon_batch_find(const char *name, lstcon_batch_t **batpp)
832 {
833         lstcon_batch_t *bat;
834
835         list_for_each_entry(bat, &console_session.ses_bat_list, bat_link) {
836                 if (strncmp(bat->bat_name, name, LST_NAME_SIZE) == 0) {
837                         *batpp = bat;
838                         return 0;
839                 }
840         }
841
842         return -ENOENT;
843 }
844
845 int
846 lstcon_batch_add(char *name)
847 {
848         lstcon_batch_t   *bat;
849         int               i;
850         int               rc;
851
852         rc = (lstcon_batch_find(name, &bat) == 0)? -EEXIST: 0;
853         if (rc != 0) {
854                 CDEBUG(D_NET, "Batch %s already exists\n", name);
855                 return rc;
856         }
857
858         LIBCFS_ALLOC(bat, sizeof(lstcon_batch_t));
859         if (bat == NULL) {
860                 CERROR("Can't allocate descriptor for batch %s\n", name);
861                 return -ENOMEM;
862         }
863
864         LIBCFS_ALLOC(bat->bat_cli_hash,
865                      sizeof(struct list_head) * LST_NODE_HASHSIZE);
866         if (bat->bat_cli_hash == NULL) {
867                 CERROR("Can't allocate hash for batch %s\n", name);
868                 LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
869
870                 return -ENOMEM;
871         }
872
873         LIBCFS_ALLOC(bat->bat_srv_hash,
874                      sizeof(struct list_head) * LST_NODE_HASHSIZE);
875         if (bat->bat_srv_hash == NULL) {
876                 CERROR("Can't allocate hash for batch %s\n", name);
877                 LIBCFS_FREE(bat->bat_cli_hash, LST_NODE_HASHSIZE);
878                 LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
879
880                 return -ENOMEM;
881         }
882
883         if (strlen(name) > sizeof(bat->bat_name)-1) {
884                 LIBCFS_FREE(bat->bat_srv_hash, LST_NODE_HASHSIZE);
885                 LIBCFS_FREE(bat->bat_cli_hash, LST_NODE_HASHSIZE);
886                 LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
887                 return -E2BIG;
888         }
889         strncpy(bat->bat_name, name, sizeof(bat->bat_name));
890         bat->bat_hdr.tsb_index = 0;
891         bat->bat_hdr.tsb_id.bat_id = ++console_session.ses_id_cookie;
892
893         bat->bat_ntest = 0;
894         bat->bat_state = LST_BATCH_IDLE;
895
896         INIT_LIST_HEAD(&bat->bat_cli_list);
897         INIT_LIST_HEAD(&bat->bat_srv_list);
898         INIT_LIST_HEAD(&bat->bat_test_list);
899         INIT_LIST_HEAD(&bat->bat_trans_list);
900
901         for (i = 0; i < LST_NODE_HASHSIZE; i++) {
902                 INIT_LIST_HEAD(&bat->bat_cli_hash[i]);
903                 INIT_LIST_HEAD(&bat->bat_srv_hash[i]);
904         }
905
906         list_add_tail(&bat->bat_link, &console_session.ses_bat_list);
907
908         return rc;
909 }
910
911 int
912 lstcon_batch_list(int index, int len, char __user *name_up)
913 {
914         lstcon_batch_t *bat;
915
916         LASSERT(name_up != NULL);
917         LASSERT(index >= 0);
918
919         list_for_each_entry(bat, &console_session.ses_bat_list, bat_link) {
920                 if (index-- == 0) {
921                         return copy_to_user(name_up, bat->bat_name, len) ?
922                                             -EFAULT : 0;
923                 }
924         }
925
926         return -ENOENT;
927 }
928
929 int
930 lstcon_batch_info(char *name, lstcon_test_batch_ent_t __user *ent_up,
931                   int server, int testidx, int *index_p, int *ndent_p,
932                   lstcon_node_ent_t __user *dents_up)
933 {
934         lstcon_test_batch_ent_t *entp;
935         struct list_head        *clilst;
936         struct list_head        *srvlst;
937         lstcon_test_t           *test = NULL;
938         lstcon_batch_t          *bat;
939         lstcon_ndlink_t         *ndl;
940         int                      rc;
941
942         rc = lstcon_batch_find(name, &bat);
943         if (rc != 0) {
944                 CDEBUG(D_NET, "Can't find batch %s\n", name);
945                 return -ENOENT;
946         }
947
948         if (testidx > 0) {
949                 /* query test, test index start from 1 */
950                 list_for_each_entry(test, &bat->bat_test_list, tes_link) {
951                         if (testidx-- == 1)
952                                 break;
953                 }
954
955                 if (testidx > 0) {
956                         CDEBUG(D_NET, "Can't find specified test in batch\n");
957                         return -ENOENT;
958                 }
959         }
960
961         clilst = (test == NULL) ? &bat->bat_cli_list :
962                                   &test->tes_src_grp->grp_ndl_list;
963         srvlst = (test == NULL) ? &bat->bat_srv_list :
964                                   &test->tes_dst_grp->grp_ndl_list;
965
966         if (dents_up != NULL) {
967                 rc = lstcon_nodes_getent((server ? srvlst: clilst),
968                                          index_p, ndent_p, dents_up);
969                 return rc;
970         }
971
972         /* non-verbose query */
973         LIBCFS_ALLOC(entp, sizeof(lstcon_test_batch_ent_t));
974         if (entp == NULL)
975                 return -ENOMEM;
976
977         if (test == NULL) {
978                 entp->u.tbe_batch.bae_ntest = bat->bat_ntest;
979                 entp->u.tbe_batch.bae_state = bat->bat_state;
980
981         } else {
982
983                 entp->u.tbe_test.tse_type   = test->tes_type;
984                 entp->u.tbe_test.tse_loop   = test->tes_loop;
985                 entp->u.tbe_test.tse_concur = test->tes_concur;
986         }
987
988         list_for_each_entry(ndl, clilst, ndl_link)
989                 LST_NODE_STATE_COUNTER(ndl->ndl_node, &entp->tbe_cli_nle);
990
991         list_for_each_entry(ndl, srvlst, ndl_link)
992                 LST_NODE_STATE_COUNTER(ndl->ndl_node, &entp->tbe_srv_nle);
993
994         rc = copy_to_user(ent_up, entp,
995                           sizeof(lstcon_test_batch_ent_t)) ? -EFAULT : 0;
996
997         LIBCFS_FREE(entp, sizeof(lstcon_test_batch_ent_t));
998
999         return rc;
1000 }
1001
1002 static int
1003 lstcon_batrpc_condition(int transop, lstcon_node_t *nd, void *arg)
1004 {
1005         switch (transop) {
1006         case LST_TRANS_TSBRUN:
1007                 if (nd->nd_state != LST_NODE_ACTIVE)
1008                         return -ENETDOWN;
1009                 break;
1010
1011         case LST_TRANS_TSBSTOP:
1012                 if (nd->nd_state != LST_NODE_ACTIVE)
1013                         return 0;
1014                 break;
1015
1016         case LST_TRANS_TSBCLIQRY:
1017         case LST_TRANS_TSBSRVQRY:
1018                 break;
1019         }
1020
1021         return 1;
1022 }
1023
1024 static int
1025 lstcon_batch_op(lstcon_batch_t *bat, int transop,
1026                 struct list_head __user *result_up)
1027 {
1028         lstcon_rpc_trans_t *trans;
1029         int                 rc;
1030
1031         rc = lstcon_rpc_trans_ndlist(&bat->bat_cli_list,
1032                                      &bat->bat_trans_list, transop,
1033                                      bat, lstcon_batrpc_condition, &trans);
1034         if (rc != 0) {
1035                 CERROR("Can't create transaction: %d\n", rc);
1036                 return rc;
1037         }
1038
1039         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
1040
1041         rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
1042
1043         lstcon_rpc_trans_destroy(trans);
1044
1045         return rc;
1046 }
1047
1048 int
1049 lstcon_batch_run(char *name, int timeout, struct list_head __user *result_up)
1050 {
1051         lstcon_batch_t *bat;
1052         int             rc;
1053
1054         if (lstcon_batch_find(name, &bat) != 0) {
1055                 CDEBUG(D_NET, "Can't find batch %s\n", name);
1056                 return -ENOENT;
1057         }
1058
1059         bat->bat_arg = timeout;
1060
1061         rc = lstcon_batch_op(bat, LST_TRANS_TSBRUN, result_up);
1062
1063         /* mark batch as running if it's started in any node */
1064         if (lstcon_tsbop_stat_success(lstcon_trans_stat(), 0) != 0)
1065                 bat->bat_state = LST_BATCH_RUNNING;
1066
1067         return rc;
1068 }
1069
1070 int
1071 lstcon_batch_stop(char *name, int force, struct list_head __user *result_up)
1072 {
1073         lstcon_batch_t *bat;
1074         int             rc;
1075
1076         if (lstcon_batch_find(name, &bat) != 0) {
1077                 CDEBUG(D_NET, "Can't find batch %s\n", name);
1078                 return -ENOENT;
1079         }
1080
1081         bat->bat_arg = force;
1082
1083         rc = lstcon_batch_op(bat, LST_TRANS_TSBSTOP, result_up);
1084
1085         /* mark batch as stopped if all RPCs finished */
1086         if (lstcon_tsbop_stat_failure(lstcon_trans_stat(), 0) == 0)
1087                 bat->bat_state = LST_BATCH_IDLE;
1088
1089         return rc;
1090 }
1091
1092 static void
1093 lstcon_batch_destroy(lstcon_batch_t *bat)
1094 {
1095         lstcon_ndlink_t    *ndl;
1096         lstcon_test_t      *test;
1097         int                 i;
1098
1099         list_del(&bat->bat_link);
1100
1101         while (!list_empty(&bat->bat_test_list)) {
1102                 test = list_entry(bat->bat_test_list.next,
1103                                   lstcon_test_t, tes_link);
1104                 LASSERT(list_empty(&test->tes_trans_list));
1105
1106                 list_del(&test->tes_link);
1107
1108                 lstcon_group_decref(test->tes_src_grp);
1109                 lstcon_group_decref(test->tes_dst_grp);
1110
1111                 LIBCFS_FREE(test, offsetof(lstcon_test_t,
1112                                            tes_param[test->tes_paramlen]));
1113         }
1114
1115         LASSERT(list_empty(&bat->bat_trans_list));
1116
1117         while (!list_empty(&bat->bat_cli_list)) {
1118                 ndl = list_entry(bat->bat_cli_list.next,
1119                                  lstcon_ndlink_t, ndl_link);
1120                 list_del_init(&ndl->ndl_link);
1121
1122                 lstcon_ndlink_release(ndl);
1123         }
1124
1125         while (!list_empty(&bat->bat_srv_list)) {
1126                 ndl = list_entry(bat->bat_srv_list.next,
1127                                  lstcon_ndlink_t, ndl_link);
1128                 list_del_init(&ndl->ndl_link);
1129
1130                 lstcon_ndlink_release(ndl);
1131         }
1132
1133         for (i = 0; i < LST_NODE_HASHSIZE; i++) {
1134                 LASSERT(list_empty(&bat->bat_cli_hash[i]));
1135                 LASSERT(list_empty(&bat->bat_srv_hash[i]));
1136         }
1137
1138         LIBCFS_FREE(bat->bat_cli_hash,
1139                     sizeof(struct list_head) * LST_NODE_HASHSIZE);
1140         LIBCFS_FREE(bat->bat_srv_hash,
1141                     sizeof(struct list_head) * LST_NODE_HASHSIZE);
1142         LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
1143 }
1144
1145 static int
1146 lstcon_testrpc_condition(int transop, lstcon_node_t *nd, void *arg)
1147 {
1148         lstcon_test_t    *test;
1149         lstcon_batch_t   *batch;
1150         lstcon_ndlink_t  *ndl;
1151         struct list_head *hash;
1152         struct list_head *head;
1153
1154         test = (lstcon_test_t *)arg;
1155         LASSERT(test != NULL);
1156
1157         batch = test->tes_batch;
1158         LASSERT(batch != NULL);
1159
1160         if (test->tes_oneside &&
1161             transop == LST_TRANS_TSBSRVADD)
1162                 return 0;
1163
1164         if (nd->nd_state != LST_NODE_ACTIVE)
1165                 return -ENETDOWN;
1166
1167         if (transop == LST_TRANS_TSBCLIADD) {
1168                 hash = batch->bat_cli_hash;
1169                 head = &batch->bat_cli_list;
1170
1171         } else {
1172                 LASSERT (transop == LST_TRANS_TSBSRVADD);
1173
1174                 hash = batch->bat_srv_hash;
1175                 head = &batch->bat_srv_list;
1176         }
1177
1178         LASSERT (nd->nd_id.nid != LNET_NID_ANY);
1179
1180         if (lstcon_ndlink_find(hash, nd->nd_id, &ndl, 1) != 0)
1181                 return -ENOMEM;
1182
1183         if (list_empty(&ndl->ndl_link))
1184                 list_add_tail(&ndl->ndl_link, head);
1185
1186         return 1;
1187 }
1188
1189 static int
1190 lstcon_test_nodes_add(lstcon_test_t *test, struct list_head __user *result_up)
1191 {
1192         lstcon_rpc_trans_t     *trans;
1193         lstcon_group_t         *grp;
1194         int                     transop;
1195         int                     rc;
1196
1197         LASSERT (test->tes_src_grp != NULL);
1198         LASSERT (test->tes_dst_grp != NULL);
1199
1200         transop = LST_TRANS_TSBSRVADD;
1201         grp  = test->tes_dst_grp;
1202 again:
1203         rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
1204                                      &test->tes_trans_list, transop,
1205                                      test, lstcon_testrpc_condition, &trans);
1206         if (rc != 0) {
1207                 CERROR("Can't create transaction: %d\n", rc);
1208                 return rc;
1209         }
1210
1211         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
1212
1213         if (lstcon_trans_stat()->trs_rpc_errno != 0 ||
1214             lstcon_trans_stat()->trs_fwk_errno != 0) {
1215                 lstcon_rpc_trans_interpreter(trans, result_up, NULL);
1216
1217                 lstcon_rpc_trans_destroy(trans);
1218                 /* return if any error */
1219                 CDEBUG(D_NET, "Failed to add test %s, "
1220                               "RPC error %d, framework error %d\n",
1221                        transop == LST_TRANS_TSBCLIADD ? "client" : "server",
1222                        lstcon_trans_stat()->trs_rpc_errno,
1223                        lstcon_trans_stat()->trs_fwk_errno);
1224
1225                 return rc;
1226         }
1227
1228         lstcon_rpc_trans_destroy(trans);
1229
1230         if (transop == LST_TRANS_TSBCLIADD)
1231                 return rc;
1232
1233         transop = LST_TRANS_TSBCLIADD;
1234         grp = test->tes_src_grp;
1235         test->tes_cliidx = 0;
1236
1237         /* requests to test clients */
1238         goto again;
1239 }
1240
1241 static int
1242 lstcon_verify_batch(const char *name, lstcon_batch_t **batch)
1243 {
1244         int rc;
1245
1246         rc = lstcon_batch_find(name, batch);
1247         if (rc != 0) {
1248                 CDEBUG(D_NET, "Can't find batch %s\n", name);
1249                 return rc;
1250         }
1251
1252         if ((*batch)->bat_state != LST_BATCH_IDLE) {
1253                 CDEBUG(D_NET, "Can't change running batch %s\n", name);
1254                 return -EINVAL;
1255         }
1256
1257         return 0;
1258 }
1259
1260 static int
1261 lstcon_verify_group(const char *name, lstcon_group_t **grp)
1262 {
1263         int                     rc;
1264         lstcon_ndlink_t         *ndl;
1265
1266         rc = lstcon_group_find(name, grp);
1267         if (rc != 0) {
1268                 CDEBUG(D_NET, "can't find group %s\n", name);
1269                 return rc;
1270         }
1271
1272         list_for_each_entry(ndl, &(*grp)->grp_ndl_list, ndl_link) {
1273                 if (ndl->ndl_node->nd_state == LST_NODE_ACTIVE) {
1274                         return 0;
1275                 }
1276         }
1277
1278         CDEBUG(D_NET, "Group %s has no ACTIVE nodes\n", name);
1279
1280         return -EINVAL;
1281 }
1282
1283 int
1284 lstcon_test_add(char *batch_name, int type, int loop,
1285                 int concur, int dist, int span,
1286                 char *src_name, char *dst_name,
1287                 void *param, int paramlen, int *retp,
1288                 struct list_head __user *result_up)
1289 {
1290         lstcon_test_t    *test   = NULL;
1291         int              rc;
1292         lstcon_group_t   *src_grp = NULL;
1293         lstcon_group_t   *dst_grp = NULL;
1294         lstcon_batch_t   *batch = NULL;
1295
1296         /*
1297          * verify that a batch of the given name exists, and the groups
1298          * that will be part of the batch exist and have at least one
1299          * active node
1300          */
1301         rc = lstcon_verify_batch(batch_name, &batch);
1302         if (rc != 0)
1303                 goto out;
1304
1305         rc = lstcon_verify_group(src_name, &src_grp);
1306         if (rc != 0)
1307                 goto out;
1308
1309         rc = lstcon_verify_group(dst_name, &dst_grp);
1310         if (rc != 0)
1311                 goto out;
1312
1313         if (dst_grp->grp_userland)
1314                 *retp = 1;
1315
1316         LIBCFS_ALLOC(test, offsetof(lstcon_test_t, tes_param[paramlen]));
1317         if (!test) {
1318                 CERROR("Can't allocate test descriptor\n");
1319                 rc = -ENOMEM;
1320
1321                 goto out;
1322         }
1323
1324         test->tes_hdr.tsb_id    = batch->bat_hdr.tsb_id;
1325         test->tes_batch         = batch;
1326         test->tes_type          = type;
1327         test->tes_oneside       = 0; /* TODO */
1328         test->tes_loop          = loop;
1329         test->tes_concur        = concur;
1330         test->tes_stop_onerr    = 1; /* TODO */
1331         test->tes_span          = span;
1332         test->tes_dist          = dist;
1333         test->tes_cliidx        = 0; /* just used for creating RPC */
1334         test->tes_src_grp       = src_grp;
1335         test->tes_dst_grp       = dst_grp;
1336         INIT_LIST_HEAD(&test->tes_trans_list);
1337
1338         if (param != NULL) {
1339                 test->tes_paramlen = paramlen;
1340                 memcpy(&test->tes_param[0], param, paramlen);
1341         }
1342
1343         rc = lstcon_test_nodes_add(test, result_up);
1344
1345         if (rc != 0)
1346                 goto out;
1347
1348         if (lstcon_trans_stat()->trs_rpc_errno != 0 ||
1349             lstcon_trans_stat()->trs_fwk_errno != 0)
1350                 CDEBUG(D_NET, "Failed to add test %d to batch %s\n", type,
1351                        batch_name);
1352
1353         /* add to test list anyway, so user can check what's going on */
1354         list_add_tail(&test->tes_link, &batch->bat_test_list);
1355
1356         batch->bat_ntest++;
1357         test->tes_hdr.tsb_index = batch->bat_ntest;
1358
1359         /*  hold groups so nobody can change them */
1360         return rc;
1361 out:
1362         if (test != NULL)
1363                 LIBCFS_FREE(test, offsetof(lstcon_test_t, tes_param[paramlen]));
1364
1365         if (dst_grp != NULL)
1366                 lstcon_group_decref(dst_grp);
1367
1368         if (src_grp != NULL)
1369                 lstcon_group_decref(src_grp);
1370
1371         return rc;
1372 }
1373
1374 static int
1375 lstcon_test_find(lstcon_batch_t *batch, int idx, lstcon_test_t **testpp)
1376 {
1377         lstcon_test_t *test;
1378
1379         list_for_each_entry(test, &batch->bat_test_list, tes_link) {
1380                 if (idx == test->tes_hdr.tsb_index) {
1381                         *testpp = test;
1382                         return 0;
1383                 }
1384         }
1385
1386         return -ENOENT;
1387 }
1388
1389 static int
1390 lstcon_tsbrpc_readent(int transop, srpc_msg_t *msg,
1391                       lstcon_rpc_ent_t __user *ent_up)
1392 {
1393         srpc_batch_reply_t *rep = &msg->msg_body.bat_reply;
1394
1395         LASSERT (transop == LST_TRANS_TSBCLIQRY ||
1396                  transop == LST_TRANS_TSBSRVQRY);
1397
1398         /* positive errno, framework error code */
1399         if (copy_to_user(&ent_up->rpe_priv[0],
1400                              &rep->bar_active, sizeof(rep->bar_active)))
1401                 return -EFAULT;
1402
1403         return 0;
1404 }
1405
1406 int
1407 lstcon_test_batch_query(char *name, int testidx, int client,
1408                         int timeout, struct list_head __user *result_up)
1409 {
1410         lstcon_rpc_trans_t *trans;
1411         struct list_head   *translist;
1412         struct list_head   *ndlist;
1413         lstcon_tsb_hdr_t   *hdr;
1414         lstcon_batch_t     *batch;
1415         lstcon_test_t      *test = NULL;
1416         int                 transop;
1417         int                 rc;
1418
1419         rc = lstcon_batch_find(name, &batch);
1420         if (rc != 0) {
1421                 CDEBUG(D_NET, "Can't find batch: %s\n", name);
1422                 return rc;
1423         }
1424
1425         if (testidx == 0) {
1426                 translist = &batch->bat_trans_list;
1427                 ndlist    = &batch->bat_cli_list;
1428                 hdr       = &batch->bat_hdr;
1429
1430         } else {
1431                 /* query specified test only */
1432                 rc = lstcon_test_find(batch, testidx, &test);
1433                 if (rc != 0) {
1434                         CDEBUG(D_NET, "Can't find test: %d\n", testidx);
1435                         return rc;
1436                 }
1437
1438                 translist = &test->tes_trans_list;
1439                 ndlist    = &test->tes_src_grp->grp_ndl_list;
1440                 hdr       = &test->tes_hdr;
1441         }
1442
1443         transop = client ? LST_TRANS_TSBCLIQRY : LST_TRANS_TSBSRVQRY;
1444
1445         rc = lstcon_rpc_trans_ndlist(ndlist, translist, transop, hdr,
1446                                      lstcon_batrpc_condition, &trans);
1447         if (rc != 0) {
1448                 CERROR("Can't create transaction: %d\n", rc);
1449                 return rc;
1450         }
1451
1452         lstcon_rpc_trans_postwait(trans, timeout);
1453
1454         if (testidx == 0 && /* query a batch, not a test */
1455             lstcon_rpc_stat_failure(lstcon_trans_stat(), 0) == 0 &&
1456             lstcon_tsbqry_stat_run(lstcon_trans_stat(), 0) == 0) {
1457                 /* all RPCs finished, and no active test */
1458                 batch->bat_state = LST_BATCH_IDLE;
1459         }
1460
1461         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1462                                           lstcon_tsbrpc_readent);
1463         lstcon_rpc_trans_destroy(trans);
1464
1465         return rc;
1466 }
1467
1468 static int
1469 lstcon_statrpc_readent(int transop, srpc_msg_t *msg,
1470                        lstcon_rpc_ent_t __user *ent_up)
1471 {
1472         srpc_stat_reply_t *rep = &msg->msg_body.stat_reply;
1473         sfw_counters_t __user  *sfwk_stat;
1474         srpc_counters_t __user *srpc_stat;
1475         lnet_counters_t __user *lnet_stat;
1476
1477         if (rep->str_status != 0)
1478                 return 0;
1479
1480         sfwk_stat = (sfw_counters_t __user *)&ent_up->rpe_payload[0];
1481         srpc_stat = (srpc_counters_t __user *)
1482                 ((char __user *)sfwk_stat + sizeof(*sfwk_stat));
1483         lnet_stat = (lnet_counters_t __user *)
1484                 ((char __user *)srpc_stat + sizeof(*srpc_stat));
1485
1486         if (copy_to_user(sfwk_stat, &rep->str_fw, sizeof(*sfwk_stat)) ||
1487             copy_to_user(srpc_stat, &rep->str_rpc, sizeof(*srpc_stat)) ||
1488             copy_to_user(lnet_stat, &rep->str_lnet, sizeof(*lnet_stat)))
1489                 return -EFAULT;
1490
1491         return 0;
1492 }
1493
1494 static int
1495 lstcon_ndlist_stat(struct list_head *ndlist,
1496                    int timeout, struct list_head __user *result_up)
1497 {
1498         struct list_head    head;
1499         lstcon_rpc_trans_t *trans;
1500         int                 rc;
1501
1502         INIT_LIST_HEAD(&head);
1503
1504         rc = lstcon_rpc_trans_ndlist(ndlist, &head,
1505                                      LST_TRANS_STATQRY, NULL, NULL, &trans);
1506         if (rc != 0) {
1507                 CERROR("Can't create transaction: %d\n", rc);
1508                 return rc;
1509         }
1510
1511         lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
1512
1513         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1514                                           lstcon_statrpc_readent);
1515         lstcon_rpc_trans_destroy(trans);
1516
1517         return rc;
1518 }
1519
1520 int
1521 lstcon_group_stat(char *grp_name, int timeout,
1522                   struct list_head __user *result_up)
1523 {
1524         lstcon_group_t     *grp;
1525         int                 rc;
1526
1527         rc = lstcon_group_find(grp_name, &grp);
1528         if (rc != 0) {
1529                 CDEBUG(D_NET, "Can't find group %s\n", grp_name);
1530                 return rc;
1531         }
1532
1533         rc = lstcon_ndlist_stat(&grp->grp_ndl_list, timeout, result_up);
1534
1535         lstcon_group_decref(grp);
1536
1537         return rc;
1538 }
1539
1540 int
1541 lstcon_nodes_stat(int count, lnet_process_id_t __user *ids_up,
1542                   int timeout, struct list_head __user *result_up)
1543 {
1544         lstcon_ndlink_t         *ndl;
1545         lstcon_group_t          *tmp;
1546         lnet_process_id_t        id;
1547         int                      i;
1548         int                      rc;
1549
1550         rc = lstcon_group_alloc(NULL, &tmp);
1551         if (rc != 0) {
1552                 CERROR("Out of memory\n");
1553                 return -ENOMEM;
1554         }
1555
1556         for (i = 0 ; i < count; i++) {
1557                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
1558                         rc = -EFAULT;
1559                         break;
1560                 }
1561
1562                 /* add to tmp group */
1563                 rc = lstcon_group_ndlink_find(tmp, id, &ndl, 2);
1564                 if (rc != 0) {
1565                         CDEBUG((rc == -ENOMEM) ? D_ERROR : D_NET,
1566                                "Failed to find or create %s: %d\n",
1567                                libcfs_id2str(id), rc);
1568                         break;
1569                 }
1570         }
1571
1572         if (rc != 0) {
1573                 lstcon_group_decref(tmp);
1574                 return rc;
1575         }
1576
1577         rc = lstcon_ndlist_stat(&tmp->grp_ndl_list, timeout, result_up);
1578
1579         lstcon_group_decref(tmp);
1580
1581         return rc;
1582 }
1583
1584 static int
1585 lstcon_debug_ndlist(struct list_head *ndlist,
1586                     struct list_head *translist,
1587                     int timeout, struct list_head __user *result_up)
1588 {
1589         lstcon_rpc_trans_t *trans;
1590         int                 rc;
1591
1592         rc = lstcon_rpc_trans_ndlist(ndlist, translist, LST_TRANS_SESQRY,
1593                                      NULL, lstcon_sesrpc_condition, &trans);
1594         if (rc != 0) {
1595                 CERROR("Can't create transaction: %d\n", rc);
1596                 return rc;
1597         }
1598
1599         lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
1600
1601         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1602                                           lstcon_sesrpc_readent);
1603         lstcon_rpc_trans_destroy(trans);
1604
1605         return rc;
1606 }
1607
1608 int
1609 lstcon_session_debug(int timeout, struct list_head __user *result_up)
1610 {
1611         return lstcon_debug_ndlist(&console_session.ses_ndl_list,
1612                                    NULL, timeout, result_up);
1613 }
1614
1615 int
1616 lstcon_batch_debug(int timeout, char *name,
1617                    int client, struct list_head __user *result_up)
1618 {
1619         lstcon_batch_t *bat;
1620         int             rc;
1621
1622         rc = lstcon_batch_find(name, &bat);
1623         if (rc != 0)
1624                 return -ENOENT;
1625
1626         rc = lstcon_debug_ndlist(client ? &bat->bat_cli_list :
1627                                           &bat->bat_srv_list,
1628                                  NULL, timeout, result_up);
1629
1630         return rc;
1631 }
1632
1633 int
1634 lstcon_group_debug(int timeout, char *name,
1635                    struct list_head __user *result_up)
1636 {
1637         lstcon_group_t *grp;
1638         int             rc;
1639
1640         rc = lstcon_group_find(name, &grp);
1641         if (rc != 0)
1642                 return -ENOENT;
1643
1644         rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
1645                                  timeout, result_up);
1646         lstcon_group_decref(grp);
1647
1648         return rc;
1649 }
1650
1651 int
1652 lstcon_nodes_debug(int timeout,
1653                    int count, lnet_process_id_t __user *ids_up,
1654                    struct list_head __user *result_up)
1655 {
1656         lnet_process_id_t  id;
1657         lstcon_ndlink_t   *ndl;
1658         lstcon_group_t    *grp;
1659         int                i;
1660         int                rc;
1661
1662         rc = lstcon_group_alloc(NULL, &grp);
1663         if (rc != 0) {
1664                 CDEBUG(D_NET, "Out of memory\n");
1665                 return rc;
1666         }
1667
1668         for (i = 0; i < count; i++) {
1669                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
1670                         rc = -EFAULT;
1671                         break;
1672                 }
1673
1674                 /* node is added to tmp group */
1675                 rc = lstcon_group_ndlink_find(grp, id, &ndl, 1);
1676                 if (rc != 0) {
1677                         CERROR("Can't create node link\n");
1678                         break;
1679                 }
1680         }
1681
1682         if (rc != 0) {
1683                 lstcon_group_decref(grp);
1684                 return rc;
1685         }
1686
1687         rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
1688                                  timeout, result_up);
1689
1690         lstcon_group_decref(grp);
1691
1692         return rc;
1693 }
1694
1695 int
1696 lstcon_session_match(lst_sid_t sid)
1697 {
1698         return (console_session.ses_id.ses_nid   == sid.ses_nid &&
1699                 console_session.ses_id.ses_stamp == sid.ses_stamp) ?  1: 0;
1700 }
1701
1702 static void
1703 lstcon_new_session_id(lst_sid_t *sid)
1704 {
1705         lnet_process_id_t      id;
1706
1707         LASSERT (console_session.ses_state == LST_SESSION_NONE);
1708
1709         LNetGetId(1, &id);
1710         sid->ses_nid   = id.nid;
1711         sid->ses_stamp = cfs_time_current();
1712 }
1713
1714 int
1715 lstcon_session_new(char *name, int key, unsigned feats,
1716                    int timeout, int force, lst_sid_t __user *sid_up)
1717 {
1718         int     rc = 0;
1719         int     i;
1720
1721         if (console_session.ses_state != LST_SESSION_NONE) {
1722                 /* session exists */
1723                 if (!force) {
1724                         CNETERR("Session %s already exists\n",
1725                                 console_session.ses_name);
1726                         return -EEXIST;
1727                 }
1728
1729                 rc = lstcon_session_end();
1730
1731                 /* lstcon_session_end() only return local error */
1732                 if  (rc != 0)
1733                         return rc;
1734         }
1735
1736         if ((feats & ~LST_FEATS_MASK) != 0) {
1737                 CNETERR("Unknown session features %x\n",
1738                         (feats & ~LST_FEATS_MASK));
1739                 return -EINVAL;
1740         }
1741
1742         for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
1743                 LASSERT(list_empty(&console_session.ses_ndl_hash[i]));
1744
1745         lstcon_new_session_id(&console_session.ses_id);
1746
1747         console_session.ses_key     = key;
1748         console_session.ses_state   = LST_SESSION_ACTIVE;
1749         console_session.ses_force   = !!force;
1750         console_session.ses_features = feats;
1751         console_session.ses_feats_updated = 0;
1752         console_session.ses_timeout = (timeout <= 0) ?
1753                                       LST_CONSOLE_TIMEOUT : timeout;
1754
1755         if (strlen(name) > sizeof(console_session.ses_name)-1)
1756                 return -E2BIG;
1757         strlcpy(console_session.ses_name, name,
1758                 sizeof(console_session.ses_name));
1759
1760         rc = lstcon_batch_add(LST_DEFAULT_BATCH);
1761         if (rc != 0)
1762                 return rc;
1763
1764         rc = lstcon_rpc_pinger_start();
1765         if (rc != 0) {
1766                 lstcon_batch_t *bat = NULL;
1767
1768                 lstcon_batch_find(LST_DEFAULT_BATCH, &bat);
1769                 lstcon_batch_destroy(bat);
1770
1771                 return rc;
1772         }
1773
1774         if (copy_to_user(sid_up, &console_session.ses_id,
1775                              sizeof(lst_sid_t)) == 0)
1776                 return rc;
1777
1778         lstcon_session_end();
1779
1780         return -EFAULT;
1781 }
1782
1783 int
1784 lstcon_session_info(lst_sid_t __user *sid_up, int __user *key_up,
1785                     unsigned __user *featp,
1786                     lstcon_ndlist_ent_t __user *ndinfo_up,
1787                     char __user *name_up, int len)
1788 {
1789         lstcon_ndlist_ent_t *entp;
1790         lstcon_ndlink_t     *ndl;
1791         int                  rc = 0;
1792
1793         if (console_session.ses_state != LST_SESSION_ACTIVE)
1794                 return -ESRCH;
1795
1796         LIBCFS_ALLOC(entp, sizeof(*entp));
1797         if (entp == NULL)
1798                 return -ENOMEM;
1799
1800         list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link)
1801                 LST_NODE_STATE_COUNTER(ndl->ndl_node, entp);
1802
1803         if (copy_to_user(sid_up, &console_session.ses_id,
1804                              sizeof(lst_sid_t)) ||
1805             copy_to_user(key_up, &console_session.ses_key,
1806                              sizeof(*key_up)) ||
1807             copy_to_user(featp, &console_session.ses_features,
1808                              sizeof(*featp)) ||
1809             copy_to_user(ndinfo_up, entp, sizeof(*entp)) ||
1810             copy_to_user(name_up, console_session.ses_name, len))
1811                 rc = -EFAULT;
1812
1813         LIBCFS_FREE(entp, sizeof(*entp));
1814
1815         return rc;
1816 }
1817
1818 int
1819 lstcon_session_end()
1820 {
1821         lstcon_rpc_trans_t *trans;
1822         lstcon_group_t     *grp;
1823         lstcon_batch_t     *bat;
1824         int                 rc = 0;
1825
1826         LASSERT (console_session.ses_state == LST_SESSION_ACTIVE);
1827
1828         rc = lstcon_rpc_trans_ndlist(&console_session.ses_ndl_list,
1829                                      NULL, LST_TRANS_SESEND, NULL,
1830                                      lstcon_sesrpc_condition, &trans);
1831         if (rc != 0) {
1832                 CERROR("Can't create transaction: %d\n", rc);
1833                 return rc;
1834         }
1835
1836         console_session.ses_shutdown = 1;
1837
1838         lstcon_rpc_pinger_stop();
1839
1840         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
1841
1842         lstcon_rpc_trans_destroy(trans);
1843         /* User can do nothing even rpc failed, so go on */
1844
1845         /* waiting for orphan rpcs to die */
1846         lstcon_rpc_cleanup_wait();
1847
1848         console_session.ses_id    = LST_INVALID_SID;
1849         console_session.ses_state = LST_SESSION_NONE;
1850         console_session.ses_key   = 0;
1851         console_session.ses_force = 0;
1852         console_session.ses_feats_updated = 0;
1853
1854         /* destroy all batches */
1855         while (!list_empty(&console_session.ses_bat_list)) {
1856                 bat = list_entry(console_session.ses_bat_list.next,
1857                                  lstcon_batch_t, bat_link);
1858
1859                 lstcon_batch_destroy(bat);
1860         }
1861
1862         /* destroy all groups */
1863         while (!list_empty(&console_session.ses_grp_list)) {
1864                 grp = list_entry(console_session.ses_grp_list.next,
1865                                  lstcon_group_t, grp_link);
1866                 LASSERT(grp->grp_ref == 1);
1867
1868                 lstcon_group_decref(grp);
1869         }
1870
1871         /* all nodes should be released */
1872         LASSERT(list_empty(&console_session.ses_ndl_list));
1873
1874         console_session.ses_shutdown = 0;
1875         console_session.ses_expired  = 0;
1876
1877         return rc;
1878 }
1879
1880 int
1881 lstcon_session_feats_check(unsigned feats)
1882 {
1883         int rc = 0;
1884
1885         if ((feats & ~LST_FEATS_MASK) != 0) {
1886                 CERROR("Can't support these features: %x\n",
1887                        (feats & ~LST_FEATS_MASK));
1888                 return -EPROTO;
1889         }
1890
1891         spin_lock(&console_session.ses_rpc_lock);
1892
1893         if (!console_session.ses_feats_updated) {
1894                 console_session.ses_feats_updated = 1;
1895                 console_session.ses_features = feats;
1896         }
1897
1898         if (console_session.ses_features != feats)
1899                 rc = -EPROTO;
1900
1901         spin_unlock(&console_session.ses_rpc_lock);
1902
1903         if (rc != 0) {
1904                 CERROR("remote features %x do not match with "
1905                        "session features %x of console\n",
1906                        feats, console_session.ses_features);
1907         }
1908
1909         return rc;
1910 }
1911
1912 static int
1913 lstcon_acceptor_handle (srpc_server_rpc_t *rpc)
1914 {
1915         srpc_msg_t        *rep  = &rpc->srpc_replymsg;
1916         srpc_msg_t        *req  = &rpc->srpc_reqstbuf->buf_msg;
1917         srpc_join_reqst_t *jreq = &req->msg_body.join_reqst;
1918         srpc_join_reply_t *jrep = &rep->msg_body.join_reply;
1919         lstcon_group_t    *grp  = NULL;
1920         lstcon_ndlink_t   *ndl;
1921         int                rc   = 0;
1922
1923         sfw_unpack_message(req);
1924
1925         mutex_lock(&console_session.ses_mutex);
1926
1927         jrep->join_sid = console_session.ses_id;
1928
1929         if (console_session.ses_id.ses_nid == LNET_NID_ANY) {
1930                 jrep->join_status = ESRCH;
1931                 goto out;
1932         }
1933
1934         if (lstcon_session_feats_check(req->msg_ses_feats) != 0) {
1935                 jrep->join_status = EPROTO;
1936                 goto out;
1937         }
1938
1939         if (jreq->join_sid.ses_nid != LNET_NID_ANY &&
1940              !lstcon_session_match(jreq->join_sid)) {
1941                 jrep->join_status = EBUSY;
1942                 goto out;
1943         }
1944
1945         if (lstcon_group_find(jreq->join_group, &grp) != 0) {
1946                 rc = lstcon_group_alloc(jreq->join_group, &grp);
1947                 if (rc != 0) {
1948                         CERROR("Out of memory\n");
1949                         goto out;
1950                 }
1951
1952                 list_add_tail(&grp->grp_link,
1953                               &console_session.ses_grp_list);
1954                 lstcon_group_addref(grp);
1955         }
1956
1957         if (grp->grp_ref > 2) {
1958                 /* Group in using */
1959                 jrep->join_status = EBUSY;
1960                 goto out;
1961         }
1962
1963         rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 0);
1964         if (rc == 0) {
1965                 jrep->join_status = EEXIST;
1966                 goto out;
1967         }
1968
1969         rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 1);
1970         if (rc != 0) {
1971                 CERROR("Out of memory\n");
1972                 goto out;
1973         }
1974
1975         ndl->ndl_node->nd_state   = LST_NODE_ACTIVE;
1976         ndl->ndl_node->nd_timeout = console_session.ses_timeout;
1977
1978         if (grp->grp_userland == 0)
1979                 grp->grp_userland = 1;
1980
1981         strlcpy(jrep->join_session, console_session.ses_name,
1982                 sizeof(jrep->join_session));
1983         jrep->join_timeout = console_session.ses_timeout;
1984         jrep->join_status  = 0;
1985
1986 out:
1987         rep->msg_ses_feats = console_session.ses_features;
1988         if (grp != NULL)
1989                 lstcon_group_decref(grp);
1990
1991         mutex_unlock(&console_session.ses_mutex);
1992
1993         return rc;
1994 }
1995
1996 static srpc_service_t lstcon_acceptor_service;
1997 static void lstcon_init_acceptor_service(void)
1998 {
1999         /* initialize selftest console acceptor service table */
2000         lstcon_acceptor_service.sv_name    = "join session";
2001         lstcon_acceptor_service.sv_handler = lstcon_acceptor_handle;
2002         lstcon_acceptor_service.sv_id      = SRPC_SERVICE_JOIN;
2003         lstcon_acceptor_service.sv_wi_total = SFW_FRWK_WI_MAX;
2004 }
2005
2006 int lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_hdr *hdr);
2007
2008 DECLARE_IOCTL_HANDLER(lstcon_ioctl_handler, lstcon_ioctl_entry);
2009
2010 /* initialize console */
2011 int
2012 lstcon_console_init(void)
2013 {
2014         int     i;
2015         int     rc;
2016
2017         memset(&console_session, 0, sizeof(lstcon_session_t));
2018
2019         console_session.ses_id              = LST_INVALID_SID;
2020         console_session.ses_state           = LST_SESSION_NONE;
2021         console_session.ses_timeout         = 0;
2022         console_session.ses_force           = 0;
2023         console_session.ses_expired         = 0;
2024         console_session.ses_feats_updated   = 0;
2025         console_session.ses_features        = LST_FEATS_MASK;
2026         console_session.ses_laststamp       = cfs_time_current_sec();
2027
2028         mutex_init(&console_session.ses_mutex);
2029
2030         INIT_LIST_HEAD(&console_session.ses_ndl_list);
2031         INIT_LIST_HEAD(&console_session.ses_grp_list);
2032         INIT_LIST_HEAD(&console_session.ses_bat_list);
2033         INIT_LIST_HEAD(&console_session.ses_trans_list);
2034
2035         LIBCFS_ALLOC(console_session.ses_ndl_hash,
2036                      sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
2037         if (console_session.ses_ndl_hash == NULL)
2038                 return -ENOMEM;
2039
2040         for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
2041                 INIT_LIST_HEAD(&console_session.ses_ndl_hash[i]);
2042
2043
2044         /* initialize acceptor service table */
2045         lstcon_init_acceptor_service();
2046
2047         rc = srpc_add_service(&lstcon_acceptor_service);
2048         LASSERT(rc != -EBUSY);
2049         if (rc != 0) {
2050                 LIBCFS_FREE(console_session.ses_ndl_hash,
2051                             sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
2052                 return rc;
2053         }
2054
2055         rc = srpc_service_add_buffers(&lstcon_acceptor_service,
2056                                       lstcon_acceptor_service.sv_wi_total);
2057         if (rc != 0) {
2058                 rc = -ENOMEM;
2059                 goto out;
2060         }
2061
2062         rc = libcfs_register_ioctl(&lstcon_ioctl_handler);
2063
2064         if (rc == 0) {
2065                 lstcon_rpc_module_init();
2066                 return 0;
2067         }
2068
2069 out:
2070         srpc_shutdown_service(&lstcon_acceptor_service);
2071         srpc_remove_service(&lstcon_acceptor_service);
2072
2073         LIBCFS_FREE(console_session.ses_ndl_hash,
2074                     sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
2075
2076         srpc_wait_service_shutdown(&lstcon_acceptor_service);
2077
2078         return rc;
2079 }
2080
2081 int
2082 lstcon_console_fini(void)
2083 {
2084         int     i;
2085
2086         libcfs_deregister_ioctl(&lstcon_ioctl_handler);
2087
2088         mutex_lock(&console_session.ses_mutex);
2089
2090         srpc_shutdown_service(&lstcon_acceptor_service);
2091         srpc_remove_service(&lstcon_acceptor_service);
2092
2093         if (console_session.ses_state != LST_SESSION_NONE)
2094                 lstcon_session_end();
2095
2096         lstcon_rpc_module_fini();
2097
2098         mutex_unlock(&console_session.ses_mutex);
2099
2100         LASSERT(list_empty(&console_session.ses_ndl_list));
2101         LASSERT(list_empty(&console_session.ses_grp_list));
2102         LASSERT(list_empty(&console_session.ses_bat_list));
2103         LASSERT(list_empty(&console_session.ses_trans_list));
2104
2105         for (i = 0; i < LST_NODE_HASHSIZE; i++)
2106                 LASSERT(list_empty(&console_session.ses_ndl_hash[i]));
2107
2108         LIBCFS_FREE(console_session.ses_ndl_hash,
2109                     sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
2110
2111         srpc_wait_service_shutdown(&lstcon_acceptor_service);
2112
2113         return 0;
2114 }
2115