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