Whamcloud - gitweb
LU-3093 lnet: Fix assert on empty group in selftest module
[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(const 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 (copy_to_user(&ent_up->rpe_priv[0],
396                                      &rep->dbg_timeout, sizeof(int)) ||
397                     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 (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 (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 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 (copy_to_user(&dents_up[count].nde_id,
774                                      &nd->nd_id, sizeof(nd->nd_id)) ||
775                     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 = 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 static int
841 lstcon_batch_find(const 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 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 = 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 static int
1251 lstcon_verify_batch(const char *name, lstcon_batch_t **batch)
1252 {
1253         int rc;
1254
1255         rc = lstcon_batch_find(name, batch);
1256         if (rc != 0) {
1257                 CDEBUG(D_NET, "Can't find batch %s\n", name);
1258                 return rc;
1259         }
1260
1261         if ((*batch)->bat_state != LST_BATCH_IDLE) {
1262                 CDEBUG(D_NET, "Can't change running batch %s\n", name);
1263                 return -EINVAL;
1264         }
1265
1266         return 0;
1267 }
1268
1269 static int
1270 lstcon_verify_group(const char *name, lstcon_group_t **grp)
1271 {
1272         int                     rc;
1273         lstcon_ndlink_t         *ndl;
1274
1275         rc = lstcon_group_find(name, grp);
1276         if (rc != 0) {
1277                 CDEBUG(D_NET, "can't find group %s\n", name);
1278                 return rc;
1279         }
1280
1281         cfs_list_for_each_entry_typed(ndl, &(*grp)->grp_ndl_list,
1282                                       lstcon_ndlink_t, ndl_link) {
1283                 if (ndl->ndl_node->nd_state == LST_NODE_ACTIVE) {
1284                         return 0;
1285                 }
1286         }
1287
1288         CDEBUG(D_NET, "Group %s has no ACTIVE nodes\n", name);
1289
1290         return -EINVAL;
1291 }
1292
1293 int
1294 lstcon_test_add(char *batch_name, int type, int loop,
1295                 int concur, int dist, int span,
1296                 char *src_name, char *dst_name,
1297                 void *param, int paramlen, int *retp,
1298                 cfs_list_t *result_up)
1299 {
1300         lstcon_test_t    *test   = NULL;
1301         int              rc;
1302         lstcon_group_t   *src_grp = NULL;
1303         lstcon_group_t   *dst_grp = NULL;
1304         lstcon_batch_t   *batch = NULL;
1305
1306         /*
1307          * verify that a batch of the given name exists, and the groups
1308          * that will be part of the batch exist and have at least one
1309          * active node
1310          */
1311         rc = lstcon_verify_batch(batch_name, &batch);
1312         if (rc != 0)
1313                 goto out;
1314
1315         rc = lstcon_verify_group(src_name, &src_grp);
1316         if (rc != 0)
1317                 goto out;
1318
1319         rc = lstcon_verify_group(dst_name, &dst_grp);
1320         if (rc != 0)
1321                 goto out;
1322
1323         if (dst_grp->grp_userland)
1324                 *retp = 1;
1325
1326         LIBCFS_ALLOC(test, offsetof(lstcon_test_t, tes_param[paramlen]));
1327         if (!test) {
1328                 CERROR("Can't allocate test descriptor\n");
1329                 rc = -ENOMEM;
1330
1331                 goto out;
1332         }
1333
1334         memset(test, 0, offsetof(lstcon_test_t, tes_param[paramlen]));
1335         test->tes_hdr.tsb_id    = batch->bat_hdr.tsb_id;
1336         test->tes_batch         = batch;
1337         test->tes_type          = type;
1338         test->tes_oneside       = 0; /* TODO */
1339         test->tes_loop          = loop;
1340         test->tes_concur        = concur;
1341         test->tes_stop_onerr    = 1; /* TODO */
1342         test->tes_span          = span;
1343         test->tes_dist          = dist;
1344         test->tes_cliidx        = 0; /* just used for creating RPC */
1345         test->tes_src_grp       = src_grp;
1346         test->tes_dst_grp       = dst_grp;
1347         CFS_INIT_LIST_HEAD(&test->tes_trans_list);
1348
1349         if (param != NULL) {
1350                 test->tes_paramlen = paramlen;
1351                 memcpy(&test->tes_param[0], param, paramlen);
1352         }
1353
1354         rc = lstcon_test_nodes_add(test, result_up);
1355
1356         if (rc != 0)
1357                 goto out;
1358
1359         if (lstcon_trans_stat()->trs_rpc_errno != 0 ||
1360             lstcon_trans_stat()->trs_fwk_errno != 0)
1361                 CDEBUG(D_NET, "Failed to add test %d to batch %s\n", type,
1362                        batch_name);
1363
1364         /* add to test list anyway, so user can check what's going on */
1365         cfs_list_add_tail(&test->tes_link, &batch->bat_test_list);
1366
1367         batch->bat_ntest++;
1368         test->tes_hdr.tsb_index = batch->bat_ntest;
1369
1370         /*  hold groups so nobody can change them */
1371         return rc;
1372 out:
1373         if (test != NULL)
1374                 LIBCFS_FREE(test, offsetof(lstcon_test_t, tes_param[paramlen]));
1375
1376         if (dst_grp != NULL)
1377                 lstcon_group_put(dst_grp);
1378
1379         if (src_grp != NULL)
1380                 lstcon_group_put(src_grp);
1381
1382         return rc;
1383 }
1384
1385 int
1386 lstcon_test_find(lstcon_batch_t *batch, int idx, lstcon_test_t **testpp)
1387 {
1388         lstcon_test_t *test;
1389
1390         cfs_list_for_each_entry_typed(test, &batch->bat_test_list,
1391                                       lstcon_test_t, tes_link) {
1392                 if (idx == test->tes_hdr.tsb_index) {
1393                         *testpp = test;
1394                         return 0;
1395                 }
1396         }
1397
1398         return -ENOENT;
1399 }
1400
1401 int
1402 lstcon_tsbrpc_readent(int transop, srpc_msg_t *msg,
1403                       lstcon_rpc_ent_t *ent_up)
1404 {
1405         srpc_batch_reply_t *rep = &msg->msg_body.bat_reply;
1406
1407         LASSERT (transop == LST_TRANS_TSBCLIQRY ||
1408                  transop == LST_TRANS_TSBSRVQRY);
1409
1410         /* positive errno, framework error code */
1411         if (copy_to_user(&ent_up->rpe_priv[0],
1412                              &rep->bar_active, sizeof(rep->bar_active)))
1413                 return -EFAULT;
1414
1415         return 0;
1416 }
1417
1418 int
1419 lstcon_test_batch_query(char *name, int testidx, int client,
1420                         int timeout, cfs_list_t *result_up)
1421 {
1422         lstcon_rpc_trans_t *trans;
1423         cfs_list_t         *translist;
1424         cfs_list_t         *ndlist;
1425         lstcon_tsb_hdr_t   *hdr;
1426         lstcon_batch_t     *batch;
1427         lstcon_test_t      *test = NULL;
1428         int                 transop;
1429         int                 rc;
1430
1431         rc = lstcon_batch_find(name, &batch);
1432         if (rc != 0) {
1433                 CDEBUG(D_NET, "Can't find batch: %s\n", name);
1434                 return rc;
1435         }
1436
1437         if (testidx == 0) {
1438                 translist = &batch->bat_trans_list;
1439                 ndlist    = &batch->bat_cli_list;
1440                 hdr       = &batch->bat_hdr;
1441
1442         } else {
1443                 /* query specified test only */
1444                 rc = lstcon_test_find(batch, testidx, &test);
1445                 if (rc != 0) {
1446                         CDEBUG(D_NET, "Can't find test: %d\n", testidx);
1447                         return rc;
1448                 }
1449         
1450                 translist = &test->tes_trans_list;
1451                 ndlist    = &test->tes_src_grp->grp_ndl_list;
1452                 hdr       = &test->tes_hdr;
1453         } 
1454
1455         transop = client ? LST_TRANS_TSBCLIQRY : LST_TRANS_TSBSRVQRY;
1456
1457         rc = lstcon_rpc_trans_ndlist(ndlist, translist, transop, hdr,
1458                                      lstcon_batrpc_condition, &trans);
1459         if (rc != 0) {
1460                 CERROR("Can't create transaction: %d\n", rc);
1461                 return rc;
1462         }
1463
1464         lstcon_rpc_trans_postwait(trans, timeout);
1465
1466         if (testidx == 0 && /* query a batch, not a test */
1467             lstcon_rpc_stat_failure(lstcon_trans_stat(), 0) == 0 &&
1468             lstcon_tsbqry_stat_run(lstcon_trans_stat(), 0) == 0) {
1469                 /* all RPCs finished, and no active test */
1470                 batch->bat_state = LST_BATCH_IDLE;
1471         }
1472
1473         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1474                                           lstcon_tsbrpc_readent);
1475         lstcon_rpc_trans_destroy(trans);
1476
1477         return rc;
1478 }
1479
1480 int
1481 lstcon_statrpc_readent(int transop, srpc_msg_t *msg,
1482                        lstcon_rpc_ent_t *ent_up)
1483 {
1484         srpc_stat_reply_t *rep = &msg->msg_body.stat_reply;
1485         sfw_counters_t    *sfwk_stat;
1486         srpc_counters_t   *srpc_stat;
1487         lnet_counters_t   *lnet_stat;
1488         
1489         if (rep->str_status != 0)
1490                 return 0;
1491
1492         sfwk_stat = (sfw_counters_t *)&ent_up->rpe_payload[0];
1493         srpc_stat = (srpc_counters_t *)((char *)sfwk_stat + sizeof(*sfwk_stat));
1494         lnet_stat = (lnet_counters_t *)((char *)srpc_stat + sizeof(*srpc_stat));
1495
1496         if (copy_to_user(sfwk_stat, &rep->str_fw, sizeof(*sfwk_stat)) ||
1497             copy_to_user(srpc_stat, &rep->str_rpc, sizeof(*srpc_stat)) ||
1498             copy_to_user(lnet_stat, &rep->str_lnet, sizeof(*lnet_stat)))
1499                 return -EFAULT;
1500
1501         return 0;
1502 }
1503
1504 int
1505 lstcon_ndlist_stat(cfs_list_t *ndlist,
1506                    int timeout, cfs_list_t *result_up)
1507 {
1508         cfs_list_t          head;
1509         lstcon_rpc_trans_t *trans;
1510         int                 rc;
1511
1512         CFS_INIT_LIST_HEAD(&head);
1513
1514         rc = lstcon_rpc_trans_ndlist(ndlist, &head,
1515                                      LST_TRANS_STATQRY, NULL, NULL, &trans);
1516         if (rc != 0) {
1517                 CERROR("Can't create transaction: %d\n", rc);
1518                 return rc;
1519         }
1520
1521         lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
1522
1523         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1524                                           lstcon_statrpc_readent);
1525         lstcon_rpc_trans_destroy(trans);
1526
1527         return rc;
1528 }
1529
1530 int
1531 lstcon_group_stat(char *grp_name, int timeout, cfs_list_t *result_up)
1532 {
1533         lstcon_group_t     *grp;
1534         int                 rc;
1535
1536         rc = lstcon_group_find(grp_name, &grp);
1537         if (rc != 0) {
1538                 CDEBUG(D_NET, "Can't find group %s\n", grp_name);
1539                 return rc;
1540         }
1541
1542         rc = lstcon_ndlist_stat(&grp->grp_ndl_list, timeout, result_up);
1543
1544         lstcon_group_put(grp);
1545
1546         return rc;
1547 }
1548
1549 int
1550 lstcon_nodes_stat(int count, lnet_process_id_t *ids_up,
1551                   int timeout, cfs_list_t *result_up)
1552 {
1553         lstcon_ndlink_t         *ndl;
1554         lstcon_group_t          *tmp;
1555         lnet_process_id_t        id;
1556         int                      i;
1557         int                      rc;
1558
1559         rc = lstcon_group_alloc(NULL, &tmp);
1560         if (rc != 0) {
1561                 CERROR("Out of memory\n");
1562                 return -ENOMEM;
1563         }
1564
1565         for (i = 0 ; i < count; i++) {
1566                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
1567                         rc = -EFAULT;
1568                         break;
1569                 }
1570
1571                 /* add to tmp group */
1572                 rc = lstcon_group_ndlink_find(tmp, id, &ndl, 2);
1573                 if (rc != 0) {
1574                         CDEBUG((rc == -ENOMEM) ? D_ERROR : D_NET,
1575                                "Failed to find or create %s: %d\n",
1576                                libcfs_id2str(id), rc);
1577                         break;
1578                 }
1579         }
1580
1581         if (rc != 0) {
1582                 lstcon_group_put(tmp);
1583                 return rc;
1584         }
1585
1586         rc = lstcon_ndlist_stat(&tmp->grp_ndl_list, timeout, result_up);
1587
1588         lstcon_group_put(tmp);
1589
1590         return rc;
1591 }
1592
1593 int
1594 lstcon_debug_ndlist(cfs_list_t *ndlist,
1595                     cfs_list_t *translist,
1596                     int timeout, cfs_list_t *result_up)
1597 {
1598         lstcon_rpc_trans_t *trans;
1599         int                 rc;
1600
1601         rc = lstcon_rpc_trans_ndlist(ndlist, translist, LST_TRANS_SESQRY,
1602                                      NULL, lstcon_sesrpc_condition, &trans);
1603         if (rc != 0) {
1604                 CERROR("Can't create transaction: %d\n", rc);
1605                 return rc;
1606         }
1607
1608         lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
1609
1610         rc = lstcon_rpc_trans_interpreter(trans, result_up,
1611                                           lstcon_sesrpc_readent);
1612         lstcon_rpc_trans_destroy(trans);
1613
1614         return rc;
1615 }
1616
1617 int
1618 lstcon_session_debug(int timeout, cfs_list_t *result_up)
1619 {
1620         return lstcon_debug_ndlist(&console_session.ses_ndl_list,
1621                                    NULL, timeout, result_up);
1622 }
1623
1624 int
1625 lstcon_batch_debug(int timeout, char *name,
1626                    int client, cfs_list_t *result_up)
1627 {
1628         lstcon_batch_t *bat;
1629         int             rc;
1630
1631         rc = lstcon_batch_find(name, &bat);
1632         if (rc != 0)
1633                 return -ENOENT;
1634
1635         rc = lstcon_debug_ndlist(client ? &bat->bat_cli_list :
1636                                           &bat->bat_srv_list,
1637                                  NULL, timeout, result_up);
1638
1639         return rc;
1640 }
1641
1642 int
1643 lstcon_group_debug(int timeout, char *name,
1644                    cfs_list_t *result_up)
1645 {
1646         lstcon_group_t *grp;
1647         int             rc;
1648
1649         rc = lstcon_group_find(name, &grp);
1650         if (rc != 0)
1651                 return -ENOENT;
1652
1653         rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
1654                                  timeout, result_up);
1655         lstcon_group_put(grp);
1656
1657         return rc;
1658 }
1659
1660 int
1661 lstcon_nodes_debug(int timeout,
1662                    int count, lnet_process_id_t *ids_up, 
1663                    cfs_list_t *result_up)
1664 {
1665         lnet_process_id_t  id;
1666         lstcon_ndlink_t   *ndl;
1667         lstcon_group_t    *grp;
1668         int                i;
1669         int                rc;
1670
1671         rc = lstcon_group_alloc(NULL, &grp);
1672         if (rc != 0) {
1673                 CDEBUG(D_NET, "Out of memory\n");
1674                 return rc;
1675         }
1676
1677         for (i = 0; i < count; i++) {
1678                 if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
1679                         rc = -EFAULT;
1680                         break;
1681                 }
1682
1683                 /* node is added to tmp group */
1684                 rc = lstcon_group_ndlink_find(grp, id, &ndl, 1);
1685                 if (rc != 0) {
1686                         CERROR("Can't create node link\n");
1687                         break;
1688                 }
1689         }
1690
1691         if (rc != 0) {
1692                 lstcon_group_put(grp);
1693                 return rc;
1694         }
1695
1696         rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
1697                                  timeout, result_up);
1698
1699         lstcon_group_put(grp);
1700
1701         return rc;
1702 }
1703
1704 int
1705 lstcon_session_match(lst_sid_t sid)
1706 {
1707         return (console_session.ses_id.ses_nid   == sid.ses_nid &&
1708                 console_session.ses_id.ses_stamp == sid.ses_stamp) ?  1: 0;
1709 }
1710
1711 static void
1712 lstcon_new_session_id(lst_sid_t *sid)
1713 {
1714         lnet_process_id_t      id;
1715
1716         LASSERT (console_session.ses_state == LST_SESSION_NONE);
1717
1718         LNetGetId(1, &id);
1719         sid->ses_nid   = id.nid;
1720         sid->ses_stamp = cfs_time_current();
1721 }
1722
1723 extern srpc_service_t lstcon_acceptor_service;
1724
1725 int
1726 lstcon_session_new(char *name, int key, unsigned feats,
1727                    int timeout, int force, lst_sid_t *sid_up)
1728 {
1729         int     rc = 0;
1730         int     i;
1731
1732         if (console_session.ses_state != LST_SESSION_NONE) {
1733                 /* session exists */
1734                 if (!force) {
1735                         CNETERR("Session %s already exists\n",
1736                                 console_session.ses_name);
1737                         return -EEXIST;
1738                 }
1739
1740                 rc = lstcon_session_end();
1741
1742                 /* lstcon_session_end() only return local error */
1743                 if  (rc != 0)
1744                         return rc;
1745         }
1746
1747         if ((feats & ~LST_FEATS_MASK) != 0) {
1748                 CNETERR("Unknown session features %x\n",
1749                         (feats & ~LST_FEATS_MASK));
1750                 return -EINVAL;
1751         }
1752
1753         for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
1754                 LASSERT(cfs_list_empty(&console_session.ses_ndl_hash[i]));
1755
1756         lstcon_new_session_id(&console_session.ses_id);
1757
1758         console_session.ses_key     = key;
1759         console_session.ses_state   = LST_SESSION_ACTIVE;
1760         console_session.ses_force   = !!force;
1761         console_session.ses_features = feats;
1762         console_session.ses_feats_updated = 0;
1763         console_session.ses_timeout = (timeout <= 0) ?
1764                                       LST_CONSOLE_TIMEOUT : timeout;
1765         strcpy(console_session.ses_name, name);
1766
1767         rc = lstcon_batch_add(LST_DEFAULT_BATCH);
1768         if (rc != 0)
1769                 return rc;
1770
1771         rc = lstcon_rpc_pinger_start();
1772         if (rc != 0) {
1773                 lstcon_batch_t *bat = NULL;
1774
1775                 lstcon_batch_find(LST_DEFAULT_BATCH, &bat);
1776                 lstcon_batch_destroy(bat);
1777
1778                 return rc;
1779         }
1780
1781         if (copy_to_user(sid_up, &console_session.ses_id,
1782                              sizeof(lst_sid_t)) == 0)
1783                 return rc;
1784
1785         lstcon_session_end();
1786
1787         return -EFAULT;
1788 }
1789
1790 int
1791 lstcon_session_info(lst_sid_t *sid_up, int *key_up, unsigned *featp,
1792                     lstcon_ndlist_ent_t *ndinfo_up, char *name_up, int len)
1793 {
1794         lstcon_ndlist_ent_t *entp;
1795         lstcon_ndlink_t     *ndl;
1796         int                  rc = 0;
1797
1798         if (console_session.ses_state != LST_SESSION_ACTIVE)
1799                 return -ESRCH;
1800
1801         LIBCFS_ALLOC(entp, sizeof(*entp));
1802         if (entp == NULL)
1803                 return -ENOMEM;
1804
1805         memset(entp, 0, sizeof(*entp));
1806
1807         cfs_list_for_each_entry_typed(ndl, &console_session.ses_ndl_list,
1808                                       lstcon_ndlink_t, ndl_link)
1809                 LST_NODE_STATE_COUNTER(ndl->ndl_node, entp);
1810
1811         if (copy_to_user(sid_up, &console_session.ses_id,
1812                              sizeof(lst_sid_t)) ||
1813             copy_to_user(key_up, &console_session.ses_key,
1814                              sizeof(*key_up)) ||
1815             copy_to_user(featp, &console_session.ses_features,
1816                              sizeof(*featp)) ||
1817             copy_to_user(ndinfo_up, entp, sizeof(*entp)) ||
1818             copy_to_user(name_up, console_session.ses_name, len))
1819                 rc = -EFAULT;
1820
1821         LIBCFS_FREE(entp, sizeof(*entp));
1822
1823         return rc;
1824 }
1825
1826 int
1827 lstcon_session_end()
1828 {
1829         lstcon_rpc_trans_t *trans;
1830         lstcon_group_t     *grp;
1831         lstcon_batch_t     *bat;
1832         int                 rc = 0;
1833
1834         LASSERT (console_session.ses_state == LST_SESSION_ACTIVE);
1835
1836         rc = lstcon_rpc_trans_ndlist(&console_session.ses_ndl_list, 
1837                                      NULL, LST_TRANS_SESEND, NULL,
1838                                      lstcon_sesrpc_condition, &trans);
1839         if (rc != 0) {
1840                 CERROR("Can't create transaction: %d\n", rc);
1841                 return rc;
1842         }
1843
1844         console_session.ses_shutdown = 1;
1845
1846         lstcon_rpc_pinger_stop();
1847
1848         lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
1849
1850         lstcon_rpc_trans_destroy(trans);
1851         /* User can do nothing even rpc failed, so go on */
1852
1853         /* waiting for orphan rpcs to die */
1854         lstcon_rpc_cleanup_wait();
1855
1856         console_session.ses_id    = LST_INVALID_SID;
1857         console_session.ses_state = LST_SESSION_NONE;
1858         console_session.ses_key   = 0;
1859         console_session.ses_force = 0;
1860         console_session.ses_feats_updated = 0;
1861
1862         /* destroy all batches */
1863         while (!cfs_list_empty(&console_session.ses_bat_list)) {
1864                 bat = cfs_list_entry(console_session.ses_bat_list.next,
1865                                      lstcon_batch_t, bat_link);
1866
1867                 lstcon_batch_destroy(bat);
1868         }
1869
1870         /* destroy all groups */
1871         while (!cfs_list_empty(&console_session.ses_grp_list)) {
1872                 grp = cfs_list_entry(console_session.ses_grp_list.next,
1873                                      lstcon_group_t, grp_link);
1874                 LASSERT (grp->grp_ref == 1);
1875
1876                 lstcon_group_put(grp);
1877         }
1878
1879         /* all nodes should be released */
1880         LASSERT (cfs_list_empty(&console_session.ses_ndl_list));
1881
1882         console_session.ses_shutdown = 0;
1883         console_session.ses_expired  = 0;
1884
1885         return rc;
1886 }
1887
1888 int
1889 lstcon_session_feats_check(unsigned feats)
1890 {
1891         int rc = 0;
1892
1893         if ((feats & ~LST_FEATS_MASK) != 0) {
1894                 CERROR("Can't support these features: %x\n",
1895                        (feats & ~LST_FEATS_MASK));
1896                 return -EPROTO;
1897         }
1898
1899         spin_lock(&console_session.ses_rpc_lock);
1900
1901         if (!console_session.ses_feats_updated) {
1902                 console_session.ses_feats_updated = 1;
1903                 console_session.ses_features = feats;
1904         }
1905
1906         if (console_session.ses_features != feats)
1907                 rc = -EPROTO;
1908
1909         spin_unlock(&console_session.ses_rpc_lock);
1910
1911         if (rc != 0) {
1912                 CERROR("remote features %x do not match with "
1913                        "session features %x of console\n",
1914                        feats, console_session.ses_features);
1915         }
1916
1917         return rc;
1918 }
1919
1920 static int
1921 lstcon_acceptor_handle (srpc_server_rpc_t *rpc)
1922 {
1923         srpc_msg_t        *rep  = &rpc->srpc_replymsg;
1924         srpc_msg_t        *req  = &rpc->srpc_reqstbuf->buf_msg;
1925         srpc_join_reqst_t *jreq = &req->msg_body.join_reqst;
1926         srpc_join_reply_t *jrep = &rep->msg_body.join_reply;
1927         lstcon_group_t    *grp  = NULL;
1928         lstcon_ndlink_t   *ndl;
1929         int                rc   = 0;
1930
1931         sfw_unpack_message(req);
1932
1933         mutex_lock(&console_session.ses_mutex);
1934
1935         jrep->join_sid = console_session.ses_id;
1936
1937         if (console_session.ses_id.ses_nid == LNET_NID_ANY) {
1938                 jrep->join_status = ESRCH;
1939                 goto out;
1940         }
1941
1942         if (lstcon_session_feats_check(req->msg_ses_feats) != 0) {
1943                 jrep->join_status = EPROTO;
1944                 goto out;
1945         }
1946
1947         if (jreq->join_sid.ses_nid != LNET_NID_ANY &&
1948              !lstcon_session_match(jreq->join_sid)) {
1949                 jrep->join_status = EBUSY;
1950                 goto out;
1951         }
1952
1953         if (lstcon_group_find(jreq->join_group, &grp) != 0) {
1954                 rc = lstcon_group_alloc(jreq->join_group, &grp);
1955                 if (rc != 0) {
1956                         CERROR("Out of memory\n");
1957                         goto out;
1958                 }
1959
1960                 cfs_list_add_tail(&grp->grp_link,
1961                                   &console_session.ses_grp_list);
1962                 lstcon_group_addref(grp);
1963         }
1964
1965         if (grp->grp_ref > 2) {
1966                 /* Group in using */
1967                 jrep->join_status = EBUSY;
1968                 goto out;
1969         }
1970
1971         rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 0);
1972         if (rc == 0) {
1973                 jrep->join_status = EEXIST;
1974                 goto out;
1975         }
1976
1977         rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 1);
1978         if (rc != 0) {
1979                 CERROR("Out of memory\n");
1980                 goto out;
1981         }
1982
1983         ndl->ndl_node->nd_state   = LST_NODE_ACTIVE;
1984         ndl->ndl_node->nd_timeout = console_session.ses_timeout;
1985
1986         if (grp->grp_userland == 0)
1987                 grp->grp_userland = 1;
1988
1989         strcpy(jrep->join_session, console_session.ses_name);
1990         jrep->join_timeout = console_session.ses_timeout;
1991         jrep->join_status  = 0;
1992
1993 out:
1994         rep->msg_ses_feats = console_session.ses_features;
1995         if (grp != NULL)
1996                 lstcon_group_put(grp);
1997
1998         mutex_unlock(&console_session.ses_mutex);
1999
2000         return rc;
2001 }
2002
2003 srpc_service_t lstcon_acceptor_service;
2004 void lstcon_init_acceptor_service(void)
2005 {
2006         /* initialize selftest console acceptor service table */
2007         lstcon_acceptor_service.sv_name    = "join session";
2008         lstcon_acceptor_service.sv_handler = lstcon_acceptor_handle;
2009         lstcon_acceptor_service.sv_id      = SRPC_SERVICE_JOIN;
2010         lstcon_acceptor_service.sv_wi_total = SFW_FRWK_WI_MAX;
2011 }
2012
2013 extern int lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_data *data);
2014
2015 DECLARE_IOCTL_HANDLER(lstcon_ioctl_handler, lstcon_ioctl_entry);
2016
2017 /* initialize console */
2018 int
2019 lstcon_console_init(void)
2020 {
2021         int     i;
2022         int     rc;
2023
2024         memset(&console_session, 0, sizeof(lstcon_session_t));
2025
2026         console_session.ses_id              = LST_INVALID_SID;
2027         console_session.ses_state           = LST_SESSION_NONE;
2028         console_session.ses_timeout         = 0;
2029         console_session.ses_force           = 0;
2030         console_session.ses_expired         = 0;
2031         console_session.ses_feats_updated   = 0;
2032         console_session.ses_features        = LST_FEATS_MASK;
2033         console_session.ses_laststamp       = cfs_time_current_sec();
2034
2035         mutex_init(&console_session.ses_mutex);
2036
2037         CFS_INIT_LIST_HEAD(&console_session.ses_ndl_list);
2038         CFS_INIT_LIST_HEAD(&console_session.ses_grp_list);
2039         CFS_INIT_LIST_HEAD(&console_session.ses_bat_list);
2040         CFS_INIT_LIST_HEAD(&console_session.ses_trans_list);
2041
2042         LIBCFS_ALLOC(console_session.ses_ndl_hash,
2043                      sizeof(cfs_list_t) * LST_GLOBAL_HASHSIZE);
2044         if (console_session.ses_ndl_hash == NULL)
2045                 return -ENOMEM;
2046
2047         for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
2048                 CFS_INIT_LIST_HEAD(&console_session.ses_ndl_hash[i]);
2049
2050
2051         /* initialize acceptor service table */
2052         lstcon_init_acceptor_service();
2053
2054         rc = srpc_add_service(&lstcon_acceptor_service);
2055         LASSERT (rc != -EBUSY);
2056         if (rc != 0) {
2057                 LIBCFS_FREE(console_session.ses_ndl_hash,
2058                             sizeof(cfs_list_t) * LST_GLOBAL_HASHSIZE);
2059                 return rc;
2060         }
2061
2062         rc = srpc_service_add_buffers(&lstcon_acceptor_service,
2063                                       lstcon_acceptor_service.sv_wi_total);
2064         if (rc != 0) {
2065                 rc = -ENOMEM;
2066                 goto out;
2067         }
2068
2069         rc = libcfs_register_ioctl(&lstcon_ioctl_handler);
2070
2071         if (rc == 0) {
2072                 lstcon_rpc_module_init();
2073                 return 0;
2074         }
2075
2076 out:
2077         srpc_shutdown_service(&lstcon_acceptor_service);
2078         srpc_remove_service(&lstcon_acceptor_service);
2079
2080         LIBCFS_FREE(console_session.ses_ndl_hash,
2081                     sizeof(cfs_list_t) * LST_GLOBAL_HASHSIZE);
2082
2083         srpc_wait_service_shutdown(&lstcon_acceptor_service);
2084
2085         return rc;
2086 }
2087
2088 int
2089 lstcon_console_fini(void)
2090 {
2091         int     i;
2092
2093         libcfs_deregister_ioctl(&lstcon_ioctl_handler);
2094
2095         mutex_lock(&console_session.ses_mutex);
2096
2097         srpc_shutdown_service(&lstcon_acceptor_service);
2098         srpc_remove_service(&lstcon_acceptor_service);
2099
2100         if (console_session.ses_state != LST_SESSION_NONE)
2101                 lstcon_session_end();
2102
2103         lstcon_rpc_module_fini();
2104
2105         mutex_unlock(&console_session.ses_mutex);
2106
2107         LASSERT (cfs_list_empty(&console_session.ses_ndl_list));
2108         LASSERT (cfs_list_empty(&console_session.ses_grp_list));
2109         LASSERT (cfs_list_empty(&console_session.ses_bat_list));
2110         LASSERT (cfs_list_empty(&console_session.ses_trans_list));
2111
2112         for (i = 0; i < LST_NODE_HASHSIZE; i++) {
2113                 LASSERT (cfs_list_empty(&console_session.ses_ndl_hash[i]));
2114         }
2115
2116         LIBCFS_FREE(console_session.ses_ndl_hash,
2117                     sizeof(cfs_list_t) * LST_GLOBAL_HASHSIZE);
2118
2119         srpc_wait_service_shutdown(&lstcon_acceptor_service);
2120
2121         return 0;
2122 }
2123
2124 #endif