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