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