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