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