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