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