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