Whamcloud - gitweb
a48344d38d7837cd78950ada076d21e1f175b9cc
[fs/lustre-release.git] / lnet / selftest / conctl.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
5  * Use is subject to license terms.
6  *
7  * Copyright (c) 2012, 2014, Intel Corporation.
8  */
9
10 /*
11  * This file is part of Lustre, http://www.lustre.org/
12  *
13  * IOC handle in kernel
14  *
15  * Author: Liang Zhen <liangzhen@clusterfs.com>
16  */
17
18 #include <linux/generic-radix-tree.h>
19 #include <libcfs/linux/linux-net.h>
20 #include <libcfs/libcfs.h>
21 #include <lnet/lib-lnet.h>
22 #include "console.h"
23
24 static int
25 lst_debug_ioctl(struct lstio_debug_args *args)
26 {
27         char *name = NULL;
28         int client = 1;
29         int rc;
30
31         if (args->lstio_dbg_key != console_session.ses_key)
32                 return -EACCES;
33
34         if (args->lstio_dbg_resultp == NULL)
35                 return -EINVAL;
36
37         if (args->lstio_dbg_namep != NULL && /* name of batch/group */
38             (args->lstio_dbg_nmlen <= 0 ||
39              args->lstio_dbg_nmlen > LST_NAME_SIZE))
40                 return -EINVAL;
41
42         if (args->lstio_dbg_namep != NULL) {
43                 LIBCFS_ALLOC(name, args->lstio_dbg_nmlen + 1);
44                 if (name == NULL)
45                         return -ENOMEM;
46
47                 if (copy_from_user(name, args->lstio_dbg_namep,
48                                    args->lstio_dbg_nmlen)) {
49                         LIBCFS_FREE(name, args->lstio_dbg_nmlen + 1);
50
51                         return -EFAULT;
52                 }
53
54                 name[args->lstio_dbg_nmlen] = 0;
55         }
56
57         rc = -EINVAL;
58
59         switch (args->lstio_dbg_type) {
60         case LST_OPC_SESSION:
61                 rc = lstcon_session_debug(args->lstio_dbg_timeout,
62                                           args->lstio_dbg_resultp);
63                 break;
64
65         case LST_OPC_BATCHSRV:
66                 client = 0;
67                 fallthrough;
68         case LST_OPC_BATCHCLI:
69                 if (name == NULL)
70                         goto out;
71
72                 rc = lstcon_batch_debug(args->lstio_dbg_timeout,
73                                         name, client, args->lstio_dbg_resultp);
74                 break;
75
76         case LST_OPC_GROUP:
77                 if (name == NULL)
78                         goto out;
79
80                 rc = lstcon_group_debug(args->lstio_dbg_timeout,
81                                         name, args->lstio_dbg_resultp);
82                 break;
83
84         case LST_OPC_NODES:
85                 if (args->lstio_dbg_count <= 0 ||
86                     args->lstio_dbg_idsp == NULL)
87                         goto out;
88
89                 rc = lstcon_nodes_debug(args->lstio_dbg_timeout,
90                                         args->lstio_dbg_count,
91                                         args->lstio_dbg_idsp,
92                                         args->lstio_dbg_resultp);
93                 break;
94
95         default:
96                 break;
97         }
98
99 out:
100         if (name != NULL)
101                 LIBCFS_FREE(name, args->lstio_dbg_nmlen + 1);
102
103         return rc;
104 }
105
106 static int
107 lst_group_add_ioctl(struct lstio_group_add_args *args)
108 {
109         char *name;
110         int rc;
111
112         if (args->lstio_grp_key != console_session.ses_key)
113                 return -EACCES;
114
115         if (args->lstio_grp_namep == NULL ||
116             args->lstio_grp_nmlen <= 0 ||
117             args->lstio_grp_nmlen > LST_NAME_SIZE)
118                 return -EINVAL;
119
120         LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
121         if (name == NULL)
122                 return -ENOMEM;
123
124         if (copy_from_user(name, args->lstio_grp_namep,
125                            args->lstio_grp_nmlen)) {
126                 LIBCFS_FREE(name, args->lstio_grp_nmlen);
127                 return -EFAULT;
128         }
129
130         name[args->lstio_grp_nmlen] = 0;
131
132         rc = lstcon_group_add(name);
133
134         LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
135
136         return rc;
137 }
138
139 static int
140 lst_group_del_ioctl(struct lstio_group_del_args *args)
141 {
142         int rc;
143         char *name;
144
145         if (args->lstio_grp_key != console_session.ses_key)
146                 return -EACCES;
147
148         if (args->lstio_grp_namep == NULL ||
149             args->lstio_grp_nmlen <= 0 ||
150             args->lstio_grp_nmlen > LST_NAME_SIZE)
151                 return -EINVAL;
152
153         LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
154         if (name == NULL)
155                 return -ENOMEM;
156
157         if (copy_from_user(name, args->lstio_grp_namep,
158                            args->lstio_grp_nmlen)) {
159                 LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
160                 return -EFAULT;
161         }
162
163         name[args->lstio_grp_nmlen] = 0;
164
165         rc = lstcon_group_del(name);
166
167         LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
168
169         return rc;
170 }
171
172 static int
173 lst_group_update_ioctl(struct lstio_group_update_args *args)
174 {
175         int rc;
176         char *name;
177
178         if (args->lstio_grp_key != console_session.ses_key)
179                 return -EACCES;
180
181         if (args->lstio_grp_resultp == NULL ||
182             args->lstio_grp_namep == NULL ||
183             args->lstio_grp_nmlen <= 0 ||
184             args->lstio_grp_nmlen > LST_NAME_SIZE)
185                 return -EINVAL;
186
187         LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
188         if (name == NULL)
189                 return -ENOMEM;
190
191         if (copy_from_user(name, args->lstio_grp_namep,
192                            args->lstio_grp_nmlen)) {
193                 LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
194                 return -EFAULT;
195         }
196
197         name[args->lstio_grp_nmlen] = 0;
198
199         switch (args->lstio_grp_opc) {
200         case LST_GROUP_CLEAN:
201                 rc = lstcon_group_clean(name, args->lstio_grp_args);
202                 break;
203
204         case LST_GROUP_REFRESH:
205                 rc = lstcon_group_refresh(name, args->lstio_grp_resultp);
206                 break;
207
208         case LST_GROUP_RMND:
209                 if (args->lstio_grp_count <= 0 ||
210                     args->lstio_grp_idsp == NULL) {
211                         rc = -EINVAL;
212                         break;
213                 }
214                 rc = lstcon_nodes_remove(name, args->lstio_grp_count,
215                                          args->lstio_grp_idsp,
216                                          args->lstio_grp_resultp);
217                 break;
218
219         default:
220                 rc = -EINVAL;
221                 break;
222         }
223
224         LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
225
226         return rc;
227 }
228
229 static int
230 lst_nodes_add_ioctl(struct lstio_group_nodes_args *args)
231 {
232         unsigned int feats;
233         int rc;
234         char *name;
235
236         if (args->lstio_grp_key != console_session.ses_key)
237                 return -EACCES;
238
239         if (args->lstio_grp_idsp == NULL || /* array of ids */
240             args->lstio_grp_count <= 0 ||
241             args->lstio_grp_resultp == NULL ||
242             args->lstio_grp_featp == NULL ||
243             args->lstio_grp_namep == NULL ||
244             args->lstio_grp_nmlen <= 0 ||
245             args->lstio_grp_nmlen > LST_NAME_SIZE)
246                 return -EINVAL;
247
248         LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
249         if (name == NULL)
250                 return -ENOMEM;
251
252         if (copy_from_user(name, args->lstio_grp_namep,
253                            args->lstio_grp_nmlen)) {
254                 LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
255
256                 return -EFAULT;
257         }
258
259         name[args->lstio_grp_nmlen] = 0;
260
261         rc = lstcon_nodes_add(name, args->lstio_grp_count,
262                               args->lstio_grp_idsp, &feats,
263                               args->lstio_grp_resultp);
264
265         LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
266         if (rc == 0 &&
267             copy_to_user(args->lstio_grp_featp, &feats, sizeof(feats))) {
268                 return -EINVAL;
269         }
270
271         return rc;
272 }
273
274 static int
275 lst_batch_add_ioctl(struct lstio_batch_add_args *args)
276 {
277         int rc;
278         char *name;
279
280         if (args->lstio_bat_key != console_session.ses_key)
281                 return -EACCES;
282
283         if (args->lstio_bat_namep == NULL ||
284             args->lstio_bat_nmlen <= 0 ||
285             args->lstio_bat_nmlen > LST_NAME_SIZE)
286                 return -EINVAL;
287
288         LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
289         if (name == NULL)
290                 return -ENOMEM;
291
292         if (copy_from_user(name, args->lstio_bat_namep,
293                            args->lstio_bat_nmlen)) {
294                 LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
295                 return -EFAULT;
296         }
297
298         name[args->lstio_bat_nmlen] = 0;
299
300         rc = lstcon_batch_add(name);
301
302         LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
303
304         return rc;
305 }
306
307 static int
308 lst_batch_run_ioctl(struct lstio_batch_run_args *args)
309 {
310         int rc;
311         char *name;
312
313         if (args->lstio_bat_key != console_session.ses_key)
314                 return -EACCES;
315
316         if (args->lstio_bat_namep == NULL ||
317             args->lstio_bat_nmlen <= 0 ||
318             args->lstio_bat_nmlen > LST_NAME_SIZE)
319                 return -EINVAL;
320
321         LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
322         if (name == NULL)
323                 return -ENOMEM;
324
325         if (copy_from_user(name, args->lstio_bat_namep,
326                            args->lstio_bat_nmlen)) {
327                 LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
328                 return -EFAULT;
329         }
330
331         name[args->lstio_bat_nmlen] = 0;
332
333         rc = lstcon_batch_run(name, args->lstio_bat_timeout,
334                               args->lstio_bat_resultp);
335
336         LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
337
338         return rc;
339 }
340
341 static int
342 lst_batch_stop_ioctl(struct lstio_batch_stop_args *args)
343 {
344         int rc;
345         char *name;
346
347         if (args->lstio_bat_key != console_session.ses_key)
348                 return -EACCES;
349
350         if (args->lstio_bat_resultp == NULL ||
351             args->lstio_bat_namep == NULL ||
352             args->lstio_bat_nmlen <= 0 ||
353             args->lstio_bat_nmlen > LST_NAME_SIZE)
354                 return -EINVAL;
355
356         LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
357         if (name == NULL)
358                 return -ENOMEM;
359
360         if (copy_from_user(name, args->lstio_bat_namep,
361                            args->lstio_bat_nmlen)) {
362                 LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
363                 return -EFAULT;
364         }
365
366         name[args->lstio_bat_nmlen] = 0;
367
368         rc = lstcon_batch_stop(name, args->lstio_bat_force,
369                                args->lstio_bat_resultp);
370
371         LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
372
373         return rc;
374 }
375
376 static int
377 lst_batch_query_ioctl(struct lstio_batch_query_args *args)
378 {
379         char *name;
380         int rc;
381
382         if (args->lstio_bat_key != console_session.ses_key)
383                 return -EACCES;
384
385         if (args->lstio_bat_resultp == NULL ||
386             args->lstio_bat_namep == NULL ||
387             args->lstio_bat_nmlen <= 0 ||
388             args->lstio_bat_nmlen > LST_NAME_SIZE)
389                 return -EINVAL;
390
391         if (args->lstio_bat_testidx < 0)
392                 return -EINVAL;
393
394         LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
395         if (name == NULL)
396                 return -ENOMEM;
397
398         if (copy_from_user(name, args->lstio_bat_namep,
399                            args->lstio_bat_nmlen)) {
400                 LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
401                 return -EFAULT;
402         }
403
404         name[args->lstio_bat_nmlen] = 0;
405
406         rc = lstcon_test_batch_query(name,
407                                      args->lstio_bat_testidx,
408                                      args->lstio_bat_client,
409                                      args->lstio_bat_timeout,
410                                      args->lstio_bat_resultp);
411
412         LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
413
414         return rc;
415 }
416
417 static int
418 lst_batch_list_ioctl(struct lstio_batch_list_args *args)
419 {
420         if (args->lstio_bat_key != console_session.ses_key)
421                 return -EACCES;
422
423         if (args->lstio_bat_idx < 0 ||
424             args->lstio_bat_namep == NULL ||
425             args->lstio_bat_nmlen <= 0 ||
426             args->lstio_bat_nmlen > LST_NAME_SIZE)
427                 return -EINVAL;
428
429         return lstcon_batch_list(args->lstio_bat_idx,
430                                  args->lstio_bat_nmlen,
431                                  args->lstio_bat_namep);
432 }
433
434 static int
435 lst_batch_info_ioctl(struct lstio_batch_info_args *args)
436 {
437         char *name;
438         int rc;
439         int index;
440         int ndent;
441
442         if (args->lstio_bat_key != console_session.ses_key)
443                 return -EACCES;
444
445         if (args->lstio_bat_namep == NULL || /* batch name */
446             args->lstio_bat_nmlen <= 0 ||
447             args->lstio_bat_nmlen > LST_NAME_SIZE)
448                 return -EINVAL;
449
450         if (args->lstio_bat_entp == NULL && /* output: batch entry */
451             args->lstio_bat_dentsp == NULL) /* output: node entry */
452                 return -EINVAL;
453
454         if (args->lstio_bat_dentsp != NULL) { /* have node entry */
455                 if (args->lstio_bat_idxp == NULL || /* node index */
456                     args->lstio_bat_ndentp == NULL) /* # of node entry */
457                         return -EINVAL;
458
459                 if (copy_from_user(&index, args->lstio_bat_idxp,
460                                    sizeof(index)) ||
461                     copy_from_user(&ndent, args->lstio_bat_ndentp,
462                                    sizeof(ndent)))
463                         return -EFAULT;
464
465                 if (ndent <= 0 || index < 0)
466                         return -EINVAL;
467         }
468
469         LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
470         if (name == NULL)
471                 return -ENOMEM;
472
473         if (copy_from_user(name, args->lstio_bat_namep,
474                            args->lstio_bat_nmlen)) {
475                 LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
476                 return -EFAULT;
477         }
478
479         name[args->lstio_bat_nmlen] = 0;
480
481         rc = lstcon_batch_info(name,
482                                args->lstio_bat_entp, args->lstio_bat_server,
483                                args->lstio_bat_testidx, &index, &ndent,
484                                args->lstio_bat_dentsp);
485
486         LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
487
488         if (rc != 0)
489                 return rc;
490
491         if (args->lstio_bat_dentsp != NULL &&
492             (copy_to_user(args->lstio_bat_idxp, &index, sizeof(index)) ||
493              copy_to_user(args->lstio_bat_ndentp, &ndent, sizeof(ndent))))
494                 rc = -EFAULT;
495
496         return rc;
497 }
498
499 static int
500 lst_stat_query_ioctl(struct lstio_stat_args *args)
501 {
502         int rc;
503         char *name = NULL;
504
505         /* TODO: not finished */
506         if (args->lstio_sta_key != console_session.ses_key)
507                 return -EACCES;
508
509         if (args->lstio_sta_resultp == NULL)
510                 return -EINVAL;
511
512         if (args->lstio_sta_idsp != NULL) {
513                 if (args->lstio_sta_count <= 0)
514                         return -EINVAL;
515
516                 rc = lstcon_nodes_stat(args->lstio_sta_count,
517                                        args->lstio_sta_idsp,
518                                        args->lstio_sta_timeout,
519                                        args->lstio_sta_resultp);
520         } else if (args->lstio_sta_namep != NULL) {
521                 if (args->lstio_sta_nmlen <= 0 ||
522                     args->lstio_sta_nmlen > LST_NAME_SIZE)
523                         return -EINVAL;
524
525                 LIBCFS_ALLOC(name, args->lstio_sta_nmlen + 1);
526                 if (name == NULL)
527                         return -ENOMEM;
528
529                 rc = copy_from_user(name, args->lstio_sta_namep,
530                                     args->lstio_sta_nmlen);
531                 if (rc == 0)
532                         rc = lstcon_group_stat(name, args->lstio_sta_timeout,
533                                                args->lstio_sta_resultp);
534                 else
535                         rc = -EFAULT;
536
537         } else {
538                 rc = -EINVAL;
539         }
540
541         if (name != NULL)
542                 LIBCFS_FREE(name, args->lstio_sta_nmlen + 1);
543         return rc;
544 }
545
546 static int lst_test_add_ioctl(struct lstio_test_args *args)
547 {
548         char *batch_name;
549         char *src_name = NULL;
550         char *dst_name = NULL;
551         void *param = NULL;
552         int ret = 0;
553         int rc = -ENOMEM;
554
555         if (args->lstio_tes_resultp == NULL ||
556             args->lstio_tes_retp == NULL ||
557             args->lstio_tes_bat_name == NULL || /* no specified batch */
558             args->lstio_tes_bat_nmlen <= 0 ||
559             args->lstio_tes_bat_nmlen > LST_NAME_SIZE ||
560             args->lstio_tes_sgrp_name == NULL || /* no source group */
561             args->lstio_tes_sgrp_nmlen <= 0 ||
562             args->lstio_tes_sgrp_nmlen > LST_NAME_SIZE ||
563             args->lstio_tes_dgrp_name == NULL || /* no target group */
564             args->lstio_tes_dgrp_nmlen <= 0 ||
565             args->lstio_tes_dgrp_nmlen > LST_NAME_SIZE)
566                 return -EINVAL;
567
568         if (args->lstio_tes_loop == 0 || /* negative is infinite */
569             args->lstio_tes_concur <= 0 ||
570             args->lstio_tes_dist <= 0 ||
571             args->lstio_tes_span <= 0)
572                 return -EINVAL;
573
574         /* have parameter, check if parameter length is valid */
575         if (args->lstio_tes_param != NULL &&
576             (args->lstio_tes_param_len <= 0 ||
577              args->lstio_tes_param_len >
578              PAGE_SIZE - sizeof(struct lstcon_test)))
579                 return -EINVAL;
580
581         LIBCFS_ALLOC(batch_name, args->lstio_tes_bat_nmlen + 1);
582         if (batch_name == NULL)
583                 return rc;
584
585         LIBCFS_ALLOC(src_name, args->lstio_tes_sgrp_nmlen + 1);
586         if (src_name == NULL)
587                 goto out;
588
589         LIBCFS_ALLOC(dst_name, args->lstio_tes_dgrp_nmlen + 1);
590         if (dst_name == NULL)
591                 goto out;
592
593         if (args->lstio_tes_param != NULL) {
594                 LIBCFS_ALLOC(param, args->lstio_tes_param_len);
595                 if (param == NULL)
596                         goto out;
597                 if (copy_from_user(param, args->lstio_tes_param,
598                                    args->lstio_tes_param_len)) {
599                         rc = -EFAULT;
600                         goto out;
601                 }
602         }
603
604         rc = -EFAULT;
605         if (copy_from_user(batch_name, args->lstio_tes_bat_name,
606                            args->lstio_tes_bat_nmlen) ||
607             copy_from_user(src_name, args->lstio_tes_sgrp_name,
608                            args->lstio_tes_sgrp_nmlen) ||
609             copy_from_user(dst_name, args->lstio_tes_dgrp_name,
610                            args->lstio_tes_dgrp_nmlen))
611                 goto out;
612
613         rc = lstcon_test_add(batch_name,
614                              args->lstio_tes_type,
615                              args->lstio_tes_loop,
616                              args->lstio_tes_concur,
617                              args->lstio_tes_dist, args->lstio_tes_span,
618                              src_name, dst_name, param,
619                              args->lstio_tes_param_len,
620                              &ret, args->lstio_tes_resultp);
621
622         if (ret != 0)
623                 rc = (copy_to_user(args->lstio_tes_retp, &ret,
624                                    sizeof(ret))) ? -EFAULT : 0;
625 out:
626         if (batch_name != NULL)
627                 LIBCFS_FREE(batch_name, args->lstio_tes_bat_nmlen + 1);
628
629         if (src_name != NULL)
630                 LIBCFS_FREE(src_name, args->lstio_tes_sgrp_nmlen + 1);
631
632         if (dst_name != NULL)
633                 LIBCFS_FREE(dst_name, args->lstio_tes_dgrp_nmlen + 1);
634
635         if (param != NULL)
636                 LIBCFS_FREE(param, args->lstio_tes_param_len);
637
638         return rc;
639 }
640
641 int
642 lstcon_ioctl_entry(struct notifier_block *nb,
643                    unsigned long cmd, void *vdata)
644 {
645         struct libcfs_ioctl_hdr *hdr = vdata;
646         struct libcfs_ioctl_data *data;
647         char *buf = NULL;
648         int rc = -EINVAL;
649         int opc;
650
651         if (cmd != IOC_LIBCFS_LNETST)
652                 goto err;
653
654         data = container_of(hdr, struct libcfs_ioctl_data, ioc_hdr);
655
656         opc = data->ioc_u32[0];
657
658         if (data->ioc_plen1 > PAGE_SIZE)
659                 goto err;
660
661         LIBCFS_ALLOC(buf, data->ioc_plen1);
662         if (buf == NULL) {
663                 rc = -ENOMEM;
664                 goto err;
665         }
666
667         /* copy in parameter */
668         if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
669                 rc = -EFAULT;
670                 goto out_free_buf;
671         }
672
673         mutex_lock(&console_session.ses_mutex);
674
675         console_session.ses_laststamp = ktime_get_real_seconds();
676
677         if (console_session.ses_shutdown) {
678                 rc = -ESHUTDOWN;
679                 goto out;
680         }
681
682         if (console_session.ses_expired)
683                 lstcon_session_end();
684
685         if (opc != LSTIO_SESSION_NEW &&
686             console_session.ses_state == LST_SESSION_NONE) {
687                 CDEBUG(D_NET, "LST no active session\n");
688                 rc = -ESRCH;
689                 goto out;
690         }
691
692         memset(&console_session.ses_trans_stat, 0,
693                sizeof(struct lstcon_trans_stat));
694
695         switch (opc) {
696         case LSTIO_SESSION_NEW:
697                 fallthrough;
698         case LSTIO_SESSION_END:
699                 fallthrough;
700         case LSTIO_SESSION_INFO:
701                 rc = -EOPNOTSUPP;
702                 break;
703         case LSTIO_DEBUG:
704                 rc = lst_debug_ioctl((struct lstio_debug_args *)buf);
705                 break;
706         case LSTIO_GROUP_ADD:
707                 rc = lst_group_add_ioctl((struct lstio_group_add_args *)buf);
708                 break;
709         case LSTIO_GROUP_DEL:
710                 rc = lst_group_del_ioctl((struct lstio_group_del_args *)buf);
711                 break;
712         case LSTIO_GROUP_UPDATE:
713                 rc = lst_group_update_ioctl((struct lstio_group_update_args *)buf);
714                 break;
715         case LSTIO_NODES_ADD:
716                 rc = lst_nodes_add_ioctl((struct lstio_group_nodes_args *)buf);
717                 break;
718         case LSTIO_GROUP_LIST:
719                 fallthrough;
720         case LSTIO_GROUP_INFO:
721                 rc = -EOPNOTSUPP;
722                 break;
723         case LSTIO_BATCH_ADD:
724                 rc = lst_batch_add_ioctl((struct lstio_batch_add_args *)buf);
725                 break;
726         case LSTIO_BATCH_START:
727                 rc = lst_batch_run_ioctl((struct lstio_batch_run_args *)buf);
728                 break;
729         case LSTIO_BATCH_STOP:
730                 rc = lst_batch_stop_ioctl((struct lstio_batch_stop_args *)buf);
731                 break;
732         case LSTIO_BATCH_QUERY:
733                 rc = lst_batch_query_ioctl((struct lstio_batch_query_args *)buf);
734                 break;
735         case LSTIO_BATCH_LIST:
736                 rc = lst_batch_list_ioctl((struct lstio_batch_list_args *)buf);
737                 break;
738         case LSTIO_BATCH_INFO:
739                 rc = lst_batch_info_ioctl((struct lstio_batch_info_args *)buf);
740                 break;
741         case LSTIO_TEST_ADD:
742                 rc = lst_test_add_ioctl((struct lstio_test_args *)buf);
743                 break;
744         case LSTIO_STAT_QUERY:
745                 rc = lst_stat_query_ioctl((struct lstio_stat_args *)buf);
746                 break;
747         default:
748                 rc = -EINVAL;
749                 goto out;
750         }
751
752         if (copy_to_user(data->ioc_pbuf2, &console_session.ses_trans_stat,
753                          sizeof(struct lstcon_trans_stat)))
754                 rc = -EFAULT;
755 out:
756         mutex_unlock(&console_session.ses_mutex);
757 out_free_buf:
758         LIBCFS_FREE(buf, data->ioc_plen1);
759 err:
760         return notifier_from_ioctl_errno(rc);
761 }
762
763 static struct genl_family lst_family;
764
765 static const struct ln_key_list lst_session_keys = {
766         .lkl_maxattr                    = LNET_SELFTEST_SESSION_MAX,
767         .lkl_list                       = {
768                 [LNET_SELFTEST_SESSION_HDR]     = {
769                         .lkp_value              = "session",
770                         .lkp_key_format         = LNKF_MAPPING,
771                         .lkp_data_type          = NLA_NUL_STRING,
772                 },
773                 [LNET_SELFTEST_SESSION_NAME]    = {
774                         .lkp_value              = "name",
775                         .lkp_data_type          = NLA_STRING,
776                 },
777                 [LNET_SELFTEST_SESSION_KEY]     = {
778                         .lkp_value              = "key",
779                         .lkp_data_type          = NLA_U32,
780                 },
781                 [LNET_SELFTEST_SESSION_TIMESTAMP] = {
782                         .lkp_value              = "timestamp",
783                         .lkp_data_type          = NLA_S64,
784                 },
785                 [LNET_SELFTEST_SESSION_NID]     = {
786                         .lkp_value              = "nid",
787                         .lkp_data_type          = NLA_STRING,
788                 },
789                 [LNET_SELFTEST_SESSION_NODE_COUNT] = {
790                         .lkp_value              = "nodes",
791                         .lkp_data_type          = NLA_U16,
792                 },
793         },
794 };
795
796 static int lst_sessions_show_dump(struct sk_buff *msg,
797                                   struct netlink_callback *cb)
798 {
799         const struct ln_key_list *all[] = {
800                 &lst_session_keys, NULL
801         };
802 #ifdef HAVE_NL_PARSE_WITH_EXT_ACK
803         struct netlink_ext_ack *extack = NULL;
804 #endif
805         int portid = NETLINK_CB(cb->skb).portid;
806         int seq = cb->nlh->nlmsg_seq;
807         unsigned int node_count = 0;
808         struct lstcon_ndlink *ndl;
809         int flag = NLM_F_MULTI;
810         int rc = 0;
811         void *hdr;
812
813 #ifdef HAVE_NL_DUMP_WITH_EXT_ACK
814         extack = cb->extack;
815 #endif
816         if (console_session.ses_state != LST_SESSION_ACTIVE) {
817                 NL_SET_ERR_MSG(extack, "session is not active");
818                 GOTO(out_unlock, rc = -ESRCH);
819         }
820
821         list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link)
822                 node_count++;
823
824         rc = lnet_genl_send_scalar_list(msg, portid, seq, &lst_family,
825                                         NLM_F_CREATE | NLM_F_MULTI,
826                                         LNET_SELFTEST_CMD_SESSIONS, all);
827         if (rc < 0) {
828                 NL_SET_ERR_MSG(extack, "failed to send key table");
829                 GOTO(out_unlock, rc);
830         }
831
832         if (console_session.ses_force)
833                 flag |= NLM_F_REPLACE;
834
835         hdr = genlmsg_put(msg, portid, seq, &lst_family, flag,
836                           LNET_SELFTEST_CMD_SESSIONS);
837         if (!hdr) {
838                 NL_SET_ERR_MSG(extack, "failed to send values");
839                 genlmsg_cancel(msg, hdr);
840                 GOTO(out_unlock, rc = -EMSGSIZE);
841         }
842
843         nla_put_string(msg, LNET_SELFTEST_SESSION_NAME,
844                        console_session.ses_name);
845         nla_put_u32(msg, LNET_SELFTEST_SESSION_KEY,
846                     console_session.ses_key);
847         nla_put_u64_64bit(msg, LNET_SELFTEST_SESSION_TIMESTAMP,
848                           console_session.ses_id.ses_stamp,
849                           LNET_SELFTEST_SESSION_PAD);
850         nla_put_string(msg, LNET_SELFTEST_SESSION_NID,
851                        libcfs_nidstr(&console_session.ses_id.ses_nid));
852         nla_put_u16(msg, LNET_SELFTEST_SESSION_NODE_COUNT,
853                     node_count);
854         genlmsg_end(msg, hdr);
855 out_unlock:
856         return lnet_nl_send_error(cb->skb, portid, seq, rc);
857 }
858
859 static int lst_sessions_cmd(struct sk_buff *skb, struct genl_info *info)
860 {
861         struct sk_buff *msg = NULL;
862         int rc = 0;
863
864         mutex_lock(&console_session.ses_mutex);
865
866         console_session.ses_laststamp = ktime_get_real_seconds();
867
868         if (console_session.ses_shutdown) {
869                 GENL_SET_ERR_MSG(info, "session is shutdown");
870                 GOTO(out_unlock, rc = -ESHUTDOWN);
871         }
872
873         if (console_session.ses_expired)
874                 lstcon_session_end();
875
876         if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE) &&
877             console_session.ses_state == LST_SESSION_NONE) {
878                 GENL_SET_ERR_MSG(info, "session is not active");
879                 GOTO(out_unlock, rc = -ESRCH);
880         }
881
882         memset(&console_session.ses_trans_stat, 0,
883                sizeof(struct lstcon_trans_stat));
884
885         if (!(info->nlhdr->nlmsg_flags & NLM_F_CREATE)) {
886                 lstcon_session_end();
887                 GOTO(out_unlock, rc);
888         }
889
890         if (info->attrs[LN_SCALAR_ATTR_LIST]) {
891                 struct genlmsghdr *gnlh = nlmsg_data(info->nlhdr);
892                 const struct ln_key_list *all[] = {
893                         &lst_session_keys, NULL
894                 };
895                 char name[LST_NAME_SIZE];
896                 struct nlmsghdr *nlh;
897                 struct nlattr *item;
898                 bool force = false;
899                 s64 timeout = 300;
900                 void *hdr;
901                 int rem;
902
903                 if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE)
904                         force = true;
905
906                 nla_for_each_nested(item, info->attrs[LN_SCALAR_ATTR_LIST],
907                                     rem) {
908                         if (nla_type(item) != LN_SCALAR_ATTR_VALUE)
909                                 continue;
910
911                         if (nla_strcmp(item, "name") == 0) {
912                                 ssize_t len;
913
914                                 item = nla_next(item, &rem);
915                                 if (nla_type(item) != LN_SCALAR_ATTR_VALUE)
916                                         GOTO(err_conf, rc = -EINVAL);
917
918                                 len = nla_strscpy(name, item, sizeof(name));
919                                 if (len < 0)
920                                         rc = len;
921                         } else if (nla_strcmp(item, "timeout") == 0) {
922                                 item = nla_next(item, &rem);
923                                 if (nla_type(item) !=
924                                     LN_SCALAR_ATTR_INT_VALUE)
925                                         GOTO(err_conf, rc = -EINVAL);
926
927                                 timeout = nla_get_s64(item);
928                                 if (timeout < 0)
929                                         rc = -ERANGE;
930                         }
931                         if (rc < 0) {
932 err_conf:
933                                 GENL_SET_ERR_MSG(info,
934                                                  "failed to get config");
935                                 GOTO(out_unlock, rc);
936                         }
937                 }
938
939                 rc = lstcon_session_new(name, info->nlhdr->nlmsg_pid,
940                                         gnlh->version, timeout,
941                                         force);
942                 if (rc < 0) {
943                         GENL_SET_ERR_MSG(info, "new session creation failed");
944                         lstcon_session_end();
945                         GOTO(out_unlock, rc);
946                 }
947
948                 msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
949                 if (!msg) {
950                         GENL_SET_ERR_MSG(info, "msg allocation failed");
951                         GOTO(out_unlock, rc = -ENOMEM);
952                 }
953
954                 rc = lnet_genl_send_scalar_list(msg, info->snd_portid,
955                                                 info->snd_seq, &lst_family,
956                                                 NLM_F_CREATE | NLM_F_MULTI,
957                                                 LNET_SELFTEST_CMD_SESSIONS,
958                                                 all);
959                 if (rc < 0) {
960                         GENL_SET_ERR_MSG(info, "failed to send key table");
961                         GOTO(out_unlock, rc);
962                 }
963
964                 hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
965                                   &lst_family, NLM_F_MULTI,
966                                   LNET_SELFTEST_CMD_SESSIONS);
967                 if (!hdr) {
968                         GENL_SET_ERR_MSG(info, "failed to send values");
969                         genlmsg_cancel(msg, hdr);
970                         GOTO(out_unlock, rc = -EMSGSIZE);
971                 }
972
973                 nla_put_string(msg, LNET_SELFTEST_SESSION_NAME,
974                                console_session.ses_name);
975                 nla_put_u32(msg, LNET_SELFTEST_SESSION_KEY,
976                             console_session.ses_key);
977                 nla_put_u64_64bit(msg, LNET_SELFTEST_SESSION_TIMESTAMP,
978                                   console_session.ses_id.ses_stamp,
979                                   LNET_SELFTEST_SESSION_PAD);
980                 nla_put_string(msg, LNET_SELFTEST_SESSION_NID,
981                                libcfs_nidstr(&console_session.ses_id.ses_nid));
982                 nla_put_u16(msg, LNET_SELFTEST_SESSION_NODE_COUNT, 0);
983
984                 genlmsg_end(msg, hdr);
985
986                 nlh = nlmsg_put(msg, info->snd_portid, info->snd_seq,
987                                 NLMSG_DONE, 0, NLM_F_MULTI);
988                 if (!nlh) {
989                         GENL_SET_ERR_MSG(info, "failed to complete message");
990                         genlmsg_cancel(msg, hdr);
991                         GOTO(out_unlock, rc = -ENOMEM);
992                 }
993                 rc = genlmsg_reply(msg, info);
994                 if (rc)
995                         GENL_SET_ERR_MSG(info, "failed to send reply");
996         }
997 out_unlock:
998         if (rc < 0 && msg)
999                 nlmsg_free(msg);
1000         mutex_unlock(&console_session.ses_mutex);
1001         return rc;
1002 }
1003
1004 static char *lst_node_state2str(int state)
1005 {
1006         if (state == LST_NODE_ACTIVE)
1007                 return "Active";
1008         if (state == LST_NODE_BUSY)
1009                 return "Busy";
1010         if (state == LST_NODE_DOWN)
1011                 return "Down";
1012
1013         return "Unknown";
1014 }
1015
1016 static int lst_node_str2state(char *str)
1017 {
1018         int state = 0;
1019
1020         if (strcasecmp(str, "Active") == 0)
1021                 state = LST_NODE_ACTIVE;
1022         else if (strcasecmp(str, "Busy") == 0)
1023                 state = LST_NODE_BUSY;
1024         else if (strcasecmp(str, "Down") == 0)
1025                 state = LST_NODE_DOWN;
1026         else if (strcasecmp(str, "Unknown") == 0)
1027                 state = LST_NODE_UNKNOWN;
1028         else if (strcasecmp(str, "Invalid") == 0)
1029                 state = LST_NODE_UNKNOWN | LST_NODE_DOWN | LST_NODE_BUSY;
1030         return state;
1031 }
1032
1033 struct lst_genl_group_prop {
1034         struct lstcon_group     *lggp_grp;
1035         int                     lggp_state_filter;
1036 };
1037
1038 struct lst_genl_group_list {
1039         GENRADIX(struct lst_genl_group_prop)    lggl_groups;
1040         unsigned int                            lggl_count;
1041         unsigned int                            lggl_index;
1042         bool                                    lggl_verbose;
1043 };
1044
1045 static inline struct lst_genl_group_list *
1046 lst_group_dump_ctx(struct netlink_callback *cb)
1047 {
1048         return (struct lst_genl_group_list *)cb->args[0];
1049 }
1050
1051 static int lst_groups_show_done(struct netlink_callback *cb)
1052 {
1053         struct lst_genl_group_list *glist = lst_group_dump_ctx(cb);
1054
1055         if (glist) {
1056                 int i;
1057
1058                 for (i = 0; i < glist->lggl_count; i++) {
1059                         struct lst_genl_group_prop *prop;
1060
1061                         prop = genradix_ptr(&glist->lggl_groups, i);
1062                         if (!prop || !prop->lggp_grp)
1063                                 continue;
1064                         lstcon_group_decref(prop->lggp_grp);
1065                 }
1066                 genradix_free(&glist->lggl_groups);
1067                 LIBCFS_FREE(glist, sizeof(*glist));
1068         }
1069         cb->args[0] = 0;
1070
1071         return 0;
1072 }
1073
1074 /* LNet selftest groups ->start() handler for GET requests */
1075 static int lst_groups_show_start(struct netlink_callback *cb)
1076 {
1077         struct genlmsghdr *gnlh = nlmsg_data(cb->nlh);
1078 #ifdef HAVE_NL_PARSE_WITH_EXT_ACK
1079         struct netlink_ext_ack *extack = NULL;
1080 #endif
1081         struct nlattr *params = genlmsg_data(gnlh);
1082         struct lst_genl_group_list *glist;
1083         int msg_len = genlmsg_len(gnlh);
1084         struct lstcon_group *grp;
1085         struct nlattr *groups;
1086         int rem, rc = 0;
1087
1088         LIBCFS_ALLOC(glist, sizeof(*glist));
1089         if (!glist)
1090                 return -ENOMEM;
1091
1092         genradix_init(&glist->lggl_groups);
1093         cb->args[0] = (long)glist;
1094
1095         if (!msg_len) {
1096                 list_for_each_entry(grp, &console_session.ses_grp_list,
1097                                     grp_link) {
1098                         struct lst_genl_group_prop *prop;
1099
1100                         prop = genradix_ptr_alloc(&glist->lggl_groups,
1101                                                   glist->lggl_count++,
1102                                                   GFP_ATOMIC);
1103                         if (!prop) {
1104                                 NL_SET_ERR_MSG(extack,
1105                                                "failed to allocate group info");
1106                                 GOTO(report_err, rc = -ENOMEM);
1107                         }
1108                         lstcon_group_addref(grp);  /* +1 ref for caller */
1109                         prop->lggp_grp = grp;
1110                 }
1111
1112                 if (!glist->lggl_count) {
1113                         NL_SET_ERR_MSG(extack, "No groups found");
1114                         rc = -ENOENT;
1115                 }
1116                 GOTO(report_err, rc);
1117         }
1118         glist->lggl_verbose = true;
1119 #ifdef HAVE_NL_DUMP_WITH_EXT_ACK
1120         extack = cb->extack;
1121 #endif
1122
1123         if (!(nla_type(params) & LN_SCALAR_ATTR_LIST)) {
1124                 NL_SET_ERR_MSG(extack, "no configuration");
1125                 GOTO(report_err, rc);
1126         }
1127
1128         nla_for_each_nested(groups, params, rem) {
1129                 struct lst_genl_group_prop *prop = NULL;
1130                 struct nlattr *group;
1131                 int rem2;
1132
1133                 if (nla_type(groups) != LN_SCALAR_ATTR_LIST)
1134                         continue;
1135
1136                 nla_for_each_nested(group, groups, rem2) {
1137                         if (nla_type(group) == LN_SCALAR_ATTR_VALUE) {
1138                                 char name[LST_NAME_SIZE];
1139
1140                                 prop = genradix_ptr_alloc(&glist->lggl_groups,
1141                                                          glist->lggl_count++,
1142                                                          GFP_ATOMIC);
1143                                 if (!prop) {
1144                                         NL_SET_ERR_MSG(extack,
1145                                                        "failed to allocate group info");
1146                                         GOTO(report_err, rc = -ENOMEM);
1147                                 }
1148
1149                                 rc = nla_strscpy(name, group, sizeof(name));
1150                                 if (rc < 0) {
1151                                         NL_SET_ERR_MSG(extack,
1152                                                        "failed to get name");
1153                                         GOTO(report_err, rc);
1154                                 }
1155                                 rc = lstcon_group_find(name, &prop->lggp_grp);
1156                                 if (rc < 0) {
1157                                         /* don't stop reporting groups if one
1158                                          * doesn't exist.
1159                                          */
1160                                         CWARN("LNet selftest group %s does not exit\n",
1161                                               name);
1162                                         rc = 0;
1163                                 }
1164                         } else if (nla_type(group) == LN_SCALAR_ATTR_LIST) {
1165                                 struct nlattr *attr;
1166                                 int rem3;
1167
1168                                 if (!prop) {
1169                                         NL_SET_ERR_MSG(extack,
1170                                                        "missing group information");
1171                                         GOTO(report_err, rc = -EINVAL);
1172                                 }
1173
1174                                 nla_for_each_nested(attr, group, rem3) {
1175                                         char tmp[16];
1176
1177                                         if (nla_type(attr) != LN_SCALAR_ATTR_VALUE ||
1178                                             nla_strcmp(attr, "status") != 0)
1179                                                 continue;
1180
1181                                         attr = nla_next(attr, &rem3);
1182                                         if (nla_type(attr) !=
1183                                             LN_SCALAR_ATTR_VALUE) {
1184                                                 NL_SET_ERR_MSG(extack,
1185                                                                "invalid config param");
1186                                                 GOTO(report_err, rc = -EINVAL);
1187                                         }
1188
1189                                         rc = nla_strscpy(tmp, attr, sizeof(tmp));
1190                                         if (rc < 0) {
1191                                                 NL_SET_ERR_MSG(extack,
1192                                                                "failed to get prop attr");
1193                                                 GOTO(report_err, rc);
1194                                         }
1195                                         rc = 0;
1196                                         prop->lggp_state_filter |=
1197                                                 lst_node_str2state(tmp);
1198                                 }
1199                         }
1200                 }
1201         }
1202         if (!glist->lggl_count) {
1203                 NL_SET_ERR_MSG(extack, "No groups found");
1204                 rc = -ENOENT;
1205         }
1206 report_err:
1207         if (rc < 0)
1208                 lst_groups_show_done(cb);
1209
1210         return rc;
1211 }
1212
1213 static const struct ln_key_list lst_group_keys = {
1214         .lkl_maxattr                    = LNET_SELFTEST_GROUP_MAX,
1215         .lkl_list                       = {
1216                 [LNET_SELFTEST_GROUP_ATTR_HDR]  = {
1217                         .lkp_value              = "groups",
1218                         .lkp_key_format         = LNKF_SEQUENCE,
1219                         .lkp_data_type          = NLA_NUL_STRING,
1220                 },
1221                 [LNET_SELFTEST_GROUP_ATTR_NAME] = {
1222                         .lkp_data_type          = NLA_STRING,
1223                 },
1224                 [LNET_SELFTEST_GROUP_ATTR_NODELIST] = {
1225                         .lkp_key_format         = LNKF_MAPPING | LNKF_SEQUENCE,
1226                         .lkp_data_type          = NLA_NESTED,
1227                 },
1228         },
1229 };
1230
1231 static const struct ln_key_list lst_group_nodelist_keys = {
1232         .lkl_maxattr                    = LNET_SELFTEST_GROUP_NODELIST_PROP_MAX,
1233         .lkl_list                       = {
1234                 [LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID] = {
1235                         .lkp_value              = "nid",
1236                         .lkp_data_type          = NLA_STRING,
1237                 },
1238                 [LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS] = {
1239                         .lkp_value              = "status",
1240                         .lkp_data_type          = NLA_STRING,
1241                 },
1242         },
1243 };
1244
1245 static int lst_groups_show_dump(struct sk_buff *msg,
1246                                 struct netlink_callback *cb)
1247 {
1248         struct lst_genl_group_list *glist = lst_group_dump_ctx(cb);
1249 #ifdef HAVE_NL_PARSE_WITH_EXT_ACK
1250         struct netlink_ext_ack *extack = NULL;
1251 #endif
1252         int portid = NETLINK_CB(cb->skb).portid;
1253         int seq = cb->nlh->nlmsg_seq;
1254         int idx = 0, rc = 0;
1255
1256 #ifdef HAVE_NL_DUMP_WITH_EXT_ACK
1257         extack = cb->extack;
1258 #endif
1259         if (!glist->lggl_index) {
1260                 const struct ln_key_list *all[] = {
1261                         &lst_group_keys, &lst_group_nodelist_keys, NULL
1262                 };
1263
1264                 rc = lnet_genl_send_scalar_list(msg, portid, seq, &lst_family,
1265                                                 NLM_F_CREATE | NLM_F_MULTI,
1266                                                 LNET_SELFTEST_CMD_GROUPS, all);
1267                 if (rc < 0) {
1268                         NL_SET_ERR_MSG(extack, "failed to send key table");
1269                         GOTO(send_error, rc);
1270                 }
1271         }
1272
1273         for (idx = glist->lggl_index; idx < glist->lggl_count; idx++) {
1274                 struct lst_genl_group_prop *group;
1275                 struct lstcon_ndlink *ndl;
1276                 struct nlattr *nodelist;
1277                 unsigned int count = 1;
1278                 void *hdr;
1279
1280                 group = genradix_ptr(&glist->lggl_groups, idx);
1281                 if (!group)
1282                         continue;
1283
1284                 hdr = genlmsg_put(msg, portid, seq, &lst_family,
1285                                   NLM_F_MULTI, LNET_SELFTEST_CMD_GROUPS);
1286                 if (!hdr) {
1287                         NL_SET_ERR_MSG(extack, "failed to send values");
1288                         GOTO(send_error, rc = -EMSGSIZE);
1289                 }
1290
1291                 if (idx == 0)
1292                         nla_put_string(msg, LNET_SELFTEST_GROUP_ATTR_HDR, "");
1293
1294                 nla_put_string(msg, LNET_SELFTEST_GROUP_ATTR_NAME,
1295                                group->lggp_grp->grp_name);
1296
1297                 if (!glist->lggl_verbose)
1298                         goto skip_details;
1299
1300                 nodelist = nla_nest_start(msg,
1301                                           LNET_SELFTEST_GROUP_ATTR_NODELIST);
1302                 list_for_each_entry(ndl, &group->lggp_grp->grp_ndl_list,
1303                                     ndl_link) {
1304                         struct nlattr *node = nla_nest_start(msg, count);
1305                         char *ndstate;
1306
1307                         if (group->lggp_state_filter &&
1308                             !(group->lggp_state_filter & ndl->ndl_node->nd_state))
1309                                 continue;
1310
1311                         nla_put_string(msg,
1312                                        LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_NID,
1313                                        libcfs_id2str(ndl->ndl_node->nd_id));
1314
1315                         ndstate = lst_node_state2str(ndl->ndl_node->nd_state);
1316                         nla_put_string(msg,
1317                                        LNET_SELFTEST_GROUP_NODELIST_PROP_ATTR_STATUS,
1318                                        ndstate);
1319                         nla_nest_end(msg, node);
1320                 }
1321                 nla_nest_end(msg, nodelist);
1322 skip_details:
1323                 genlmsg_end(msg, hdr);
1324         }
1325         glist->lggl_index = idx;
1326 send_error:
1327         return lnet_nl_send_error(cb->skb, portid, seq, rc);
1328 }
1329
1330 #ifndef HAVE_NETLINK_CALLBACK_START
1331 static int lst_old_groups_show_dump(struct sk_buff *msg,
1332                                     struct netlink_callback *cb)
1333 {
1334         if (!cb->args[0]) {
1335                 int rc = lst_groups_show_start(cb);
1336
1337                 if (rc < 0)
1338                         return lnet_nl_send_error(cb->skb,
1339                                                   NETLINK_CB(cb->skb).portid,
1340                                                   cb->nlh->nlmsg_seq,
1341                                                   rc);
1342         }
1343
1344         return lst_groups_show_dump(msg, cb);
1345 }
1346 #endif
1347
1348 static const struct genl_multicast_group lst_mcast_grps[] = {
1349         { .name = "sessions",           },
1350         { .name = "groups",             },
1351 };
1352
1353 static const struct genl_ops lst_genl_ops[] = {
1354         {
1355                 .cmd            = LNET_SELFTEST_CMD_SESSIONS,
1356                 .dumpit         = lst_sessions_show_dump,
1357                 .doit           = lst_sessions_cmd,
1358         },
1359         {
1360                 .cmd            = LNET_SELFTEST_CMD_GROUPS,
1361 #ifdef HAVE_NETLINK_CALLBACK_START
1362                 .start          = lst_groups_show_start,
1363                 .dumpit         = lst_groups_show_dump,
1364 #else
1365                 .dumpit         = lst_old_groups_show_dump,
1366 #endif
1367                 .done           = lst_groups_show_done,
1368         },
1369 };
1370
1371 static struct genl_family lst_family = {
1372         .name           = LNET_SELFTEST_GENL_NAME,
1373         .version        = LNET_SELFTEST_GENL_VERSION,
1374         .maxattr        = LN_SCALAR_MAX,
1375         .module         = THIS_MODULE,
1376         .ops            = lst_genl_ops,
1377         .n_ops          = ARRAY_SIZE(lst_genl_ops),
1378         .mcgrps         = lst_mcast_grps,
1379         .n_mcgrps       = ARRAY_SIZE(lst_mcast_grps),
1380 #ifdef GENL_FAMILY_HAS_RESV_START_OP
1381         .resv_start_op  = __LNET_SELFTEST_CMD_MAX_PLUS_ONE,
1382 #endif
1383 };
1384
1385 int lstcon_init_netlink(void)
1386 {
1387         return genl_register_family(&lst_family);
1388 }
1389
1390 void lstcon_fini_netlink(void)
1391 {
1392         genl_unregister_family(&lst_family);
1393 }