Merge remote-tracking branch 'asoc/topic/simple' into asoc-next
[cascardo/linux.git] / drivers / staging / lustre / lnet / selftest / conrpc.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.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) 2011, 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  * Console framework rpcs
39  *
40  * Author: Liang Zhen <liang@whamcloud.com>
41  */
42
43 #include "../../include/linux/libcfs/libcfs.h"
44 #include "../../include/linux/lnet/lib-lnet.h"
45 #include "timer.h"
46 #include "conrpc.h"
47 #include "console.h"
48
49 void lstcon_rpc_stat_reply(lstcon_rpc_trans_t *, srpc_msg_t *,
50                            lstcon_node_t *, lstcon_trans_stat_t *);
51
52 static void
53 lstcon_rpc_done(srpc_client_rpc_t *rpc)
54 {
55         lstcon_rpc_t *crpc = (lstcon_rpc_t *)rpc->crpc_priv;
56
57         LASSERT(crpc && rpc == crpc->crp_rpc);
58         LASSERT(crpc->crp_posted && !crpc->crp_finished);
59
60         spin_lock(&rpc->crpc_lock);
61
62         if (!crpc->crp_trans) {
63                 /*
64                  * Orphan RPC is not in any transaction,
65                  * I'm just a poor body and nobody loves me
66                  */
67                 spin_unlock(&rpc->crpc_lock);
68
69                 /* release it */
70                 lstcon_rpc_put(crpc);
71                 return;
72         }
73
74         /* not an orphan RPC */
75         crpc->crp_finished = 1;
76
77         if (!crpc->crp_stamp) {
78                 /* not aborted */
79                 LASSERT(!crpc->crp_status);
80
81                 crpc->crp_stamp = cfs_time_current();
82                 crpc->crp_status = rpc->crpc_status;
83         }
84
85         /* wakeup (transaction)thread if I'm the last RPC in the transaction */
86         if (atomic_dec_and_test(&crpc->crp_trans->tas_remaining))
87                 wake_up(&crpc->crp_trans->tas_waitq);
88
89         spin_unlock(&rpc->crpc_lock);
90 }
91
92 static int
93 lstcon_rpc_init(lstcon_node_t *nd, int service, unsigned feats,
94                 int bulk_npg, int bulk_len, int embedded, lstcon_rpc_t *crpc)
95 {
96         crpc->crp_rpc = sfw_create_rpc(nd->nd_id, service,
97                                        feats, bulk_npg, bulk_len,
98                                        lstcon_rpc_done, (void *)crpc);
99         if (!crpc->crp_rpc)
100                 return -ENOMEM;
101
102         crpc->crp_trans = NULL;
103         crpc->crp_node = nd;
104         crpc->crp_posted = 0;
105         crpc->crp_finished = 0;
106         crpc->crp_unpacked = 0;
107         crpc->crp_status = 0;
108         crpc->crp_stamp = 0;
109         crpc->crp_embedded = embedded;
110         INIT_LIST_HEAD(&crpc->crp_link);
111
112         atomic_inc(&console_session.ses_rpc_counter);
113
114         return 0;
115 }
116
117 static int
118 lstcon_rpc_prep(lstcon_node_t *nd, int service, unsigned feats,
119                 int bulk_npg, int bulk_len, lstcon_rpc_t **crpcpp)
120 {
121         lstcon_rpc_t *crpc = NULL;
122         int rc;
123
124         spin_lock(&console_session.ses_rpc_lock);
125
126         crpc = list_first_entry_or_null(&console_session.ses_rpc_freelist,
127                                         lstcon_rpc_t, crp_link);
128         if (crpc)
129                 list_del_init(&crpc->crp_link);
130
131         spin_unlock(&console_session.ses_rpc_lock);
132
133         if (!crpc) {
134                 LIBCFS_ALLOC(crpc, sizeof(*crpc));
135                 if (!crpc)
136                         return -ENOMEM;
137         }
138
139         rc = lstcon_rpc_init(nd, service, feats, bulk_npg, bulk_len, 0, crpc);
140         if (!rc) {
141                 *crpcpp = crpc;
142                 return 0;
143         }
144
145         LIBCFS_FREE(crpc, sizeof(*crpc));
146
147         return rc;
148 }
149
150 void
151 lstcon_rpc_put(lstcon_rpc_t *crpc)
152 {
153         srpc_bulk_t *bulk = &crpc->crp_rpc->crpc_bulk;
154         int i;
155
156         LASSERT(list_empty(&crpc->crp_link));
157
158         for (i = 0; i < bulk->bk_niov; i++) {
159                 if (!bulk->bk_iovs[i].kiov_page)
160                         continue;
161
162                 __free_page(bulk->bk_iovs[i].kiov_page);
163         }
164
165         srpc_client_rpc_decref(crpc->crp_rpc);
166
167         if (crpc->crp_embedded) {
168                 /* embedded RPC, don't recycle it */
169                 memset(crpc, 0, sizeof(*crpc));
170                 crpc->crp_embedded = 1;
171
172         } else {
173                 spin_lock(&console_session.ses_rpc_lock);
174
175                 list_add(&crpc->crp_link,
176                          &console_session.ses_rpc_freelist);
177
178                 spin_unlock(&console_session.ses_rpc_lock);
179         }
180
181         /* RPC is not alive now */
182         atomic_dec(&console_session.ses_rpc_counter);
183 }
184
185 static void
186 lstcon_rpc_post(lstcon_rpc_t *crpc)
187 {
188         lstcon_rpc_trans_t *trans = crpc->crp_trans;
189
190         LASSERT(trans);
191
192         atomic_inc(&trans->tas_remaining);
193         crpc->crp_posted = 1;
194
195         sfw_post_rpc(crpc->crp_rpc);
196 }
197
198 static char *
199 lstcon_rpc_trans_name(int transop)
200 {
201         if (transop == LST_TRANS_SESNEW)
202                 return "SESNEW";
203
204         if (transop == LST_TRANS_SESEND)
205                 return "SESEND";
206
207         if (transop == LST_TRANS_SESQRY)
208                 return "SESQRY";
209
210         if (transop == LST_TRANS_SESPING)
211                 return "SESPING";
212
213         if (transop == LST_TRANS_TSBCLIADD)
214                 return "TSBCLIADD";
215
216         if (transop == LST_TRANS_TSBSRVADD)
217                 return "TSBSRVADD";
218
219         if (transop == LST_TRANS_TSBRUN)
220                 return "TSBRUN";
221
222         if (transop == LST_TRANS_TSBSTOP)
223                 return "TSBSTOP";
224
225         if (transop == LST_TRANS_TSBCLIQRY)
226                 return "TSBCLIQRY";
227
228         if (transop == LST_TRANS_TSBSRVQRY)
229                 return "TSBSRVQRY";
230
231         if (transop == LST_TRANS_STATQRY)
232                 return "STATQRY";
233
234         return "Unknown";
235 }
236
237 int
238 lstcon_rpc_trans_prep(struct list_head *translist, int transop,
239                       lstcon_rpc_trans_t **transpp)
240 {
241         lstcon_rpc_trans_t *trans;
242
243         if (translist) {
244                 list_for_each_entry(trans, translist, tas_link) {
245                         /*
246                          * Can't enqueue two private transaction on
247                          * the same object
248                          */
249                         if ((trans->tas_opc & transop) == LST_TRANS_PRIVATE)
250                                 return -EPERM;
251                 }
252         }
253
254         /* create a trans group */
255         LIBCFS_ALLOC(trans, sizeof(*trans));
256         if (!trans)
257                 return -ENOMEM;
258
259         trans->tas_opc = transop;
260
261         if (!translist)
262                 INIT_LIST_HEAD(&trans->tas_olink);
263         else
264                 list_add_tail(&trans->tas_olink, translist);
265
266         list_add_tail(&trans->tas_link, &console_session.ses_trans_list);
267
268         INIT_LIST_HEAD(&trans->tas_rpcs_list);
269         atomic_set(&trans->tas_remaining, 0);
270         init_waitqueue_head(&trans->tas_waitq);
271
272         spin_lock(&console_session.ses_rpc_lock);
273         trans->tas_features = console_session.ses_features;
274         spin_unlock(&console_session.ses_rpc_lock);
275
276         *transpp = trans;
277         return 0;
278 }
279
280 void
281 lstcon_rpc_trans_addreq(lstcon_rpc_trans_t *trans, lstcon_rpc_t *crpc)
282 {
283         list_add_tail(&crpc->crp_link, &trans->tas_rpcs_list);
284         crpc->crp_trans = trans;
285 }
286
287 void
288 lstcon_rpc_trans_abort(lstcon_rpc_trans_t *trans, int error)
289 {
290         srpc_client_rpc_t *rpc;
291         lstcon_rpc_t *crpc;
292         lstcon_node_t *nd;
293
294         list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
295                 rpc = crpc->crp_rpc;
296
297                 spin_lock(&rpc->crpc_lock);
298
299                 if (!crpc->crp_posted || /* not posted */
300                     crpc->crp_stamp) { /* rpc done or aborted already */
301                         if (!crpc->crp_stamp) {
302                                 crpc->crp_stamp = cfs_time_current();
303                                 crpc->crp_status = -EINTR;
304                         }
305                         spin_unlock(&rpc->crpc_lock);
306                         continue;
307                 }
308
309                 crpc->crp_stamp = cfs_time_current();
310                 crpc->crp_status = error;
311
312                 spin_unlock(&rpc->crpc_lock);
313
314                 sfw_abort_rpc(rpc);
315
316                 if (error != -ETIMEDOUT)
317                         continue;
318
319                 nd = crpc->crp_node;
320                 if (cfs_time_after(nd->nd_stamp, crpc->crp_stamp))
321                         continue;
322
323                 nd->nd_stamp = crpc->crp_stamp;
324                 nd->nd_state = LST_NODE_DOWN;
325         }
326 }
327
328 static int
329 lstcon_rpc_trans_check(lstcon_rpc_trans_t *trans)
330 {
331         if (console_session.ses_shutdown &&
332             !list_empty(&trans->tas_olink)) /* Not an end session RPC */
333                 return 1;
334
335         return !atomic_read(&trans->tas_remaining) ? 1 : 0;
336 }
337
338 int
339 lstcon_rpc_trans_postwait(lstcon_rpc_trans_t *trans, int timeout)
340 {
341         lstcon_rpc_t *crpc;
342         int rc;
343
344         if (list_empty(&trans->tas_rpcs_list))
345                 return 0;
346
347         if (timeout < LST_TRANS_MIN_TIMEOUT)
348                 timeout = LST_TRANS_MIN_TIMEOUT;
349
350         CDEBUG(D_NET, "Transaction %s started\n",
351                lstcon_rpc_trans_name(trans->tas_opc));
352
353         /* post all requests */
354         list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
355                 LASSERT(!crpc->crp_posted);
356
357                 lstcon_rpc_post(crpc);
358         }
359
360         mutex_unlock(&console_session.ses_mutex);
361
362         rc = wait_event_interruptible_timeout(trans->tas_waitq,
363                                               lstcon_rpc_trans_check(trans),
364                                               cfs_time_seconds(timeout));
365         rc = (rc > 0) ? 0 : ((rc < 0) ? -EINTR : -ETIMEDOUT);
366
367         mutex_lock(&console_session.ses_mutex);
368
369         if (console_session.ses_shutdown)
370                 rc = -ESHUTDOWN;
371
372         if (rc || atomic_read(&trans->tas_remaining)) {
373                 /* treat short timeout as canceled */
374                 if (rc == -ETIMEDOUT && timeout < LST_TRANS_MIN_TIMEOUT * 2)
375                         rc = -EINTR;
376
377                 lstcon_rpc_trans_abort(trans, rc);
378         }
379
380         CDEBUG(D_NET, "Transaction %s stopped: %d\n",
381                lstcon_rpc_trans_name(trans->tas_opc), rc);
382
383         lstcon_rpc_trans_stat(trans, lstcon_trans_stat());
384
385         return rc;
386 }
387
388 static int
389 lstcon_rpc_get_reply(lstcon_rpc_t *crpc, srpc_msg_t **msgpp)
390 {
391         lstcon_node_t *nd = crpc->crp_node;
392         srpc_client_rpc_t *rpc = crpc->crp_rpc;
393         srpc_generic_reply_t *rep;
394
395         LASSERT(nd && rpc);
396         LASSERT(crpc->crp_stamp);
397
398         if (crpc->crp_status) {
399                 *msgpp = NULL;
400                 return crpc->crp_status;
401         }
402
403         *msgpp = &rpc->crpc_replymsg;
404         if (!crpc->crp_unpacked) {
405                 sfw_unpack_message(*msgpp);
406                 crpc->crp_unpacked = 1;
407         }
408
409         if (cfs_time_after(nd->nd_stamp, crpc->crp_stamp))
410                 return 0;
411
412         nd->nd_stamp = crpc->crp_stamp;
413         rep = &(*msgpp)->msg_body.reply;
414
415         if (rep->sid.ses_nid == LNET_NID_ANY)
416                 nd->nd_state = LST_NODE_UNKNOWN;
417         else if (lstcon_session_match(rep->sid))
418                 nd->nd_state = LST_NODE_ACTIVE;
419         else
420                 nd->nd_state = LST_NODE_BUSY;
421
422         return 0;
423 }
424
425 void
426 lstcon_rpc_trans_stat(lstcon_rpc_trans_t *trans, lstcon_trans_stat_t *stat)
427 {
428         lstcon_rpc_t *crpc;
429         srpc_msg_t *rep;
430         int error;
431
432         LASSERT(stat);
433
434         memset(stat, 0, sizeof(*stat));
435
436         list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
437                 lstcon_rpc_stat_total(stat, 1);
438
439                 LASSERT(crpc->crp_stamp);
440
441                 error = lstcon_rpc_get_reply(crpc, &rep);
442                 if (error) {
443                         lstcon_rpc_stat_failure(stat, 1);
444                         if (!stat->trs_rpc_errno)
445                                 stat->trs_rpc_errno = -error;
446
447                         continue;
448                 }
449
450                 lstcon_rpc_stat_success(stat, 1);
451
452                 lstcon_rpc_stat_reply(trans, rep, crpc->crp_node, stat);
453         }
454
455         if (trans->tas_opc == LST_TRANS_SESNEW && !stat->trs_fwk_errno) {
456                 stat->trs_fwk_errno =
457                       lstcon_session_feats_check(trans->tas_features);
458         }
459
460         CDEBUG(D_NET, "transaction %s : success %d, failure %d, total %d, RPC error(%d), Framework error(%d)\n",
461                lstcon_rpc_trans_name(trans->tas_opc),
462                lstcon_rpc_stat_success(stat, 0),
463                lstcon_rpc_stat_failure(stat, 0),
464                lstcon_rpc_stat_total(stat, 0),
465                stat->trs_rpc_errno, stat->trs_fwk_errno);
466 }
467
468 int
469 lstcon_rpc_trans_interpreter(lstcon_rpc_trans_t *trans,
470                              struct list_head __user *head_up,
471                              lstcon_rpc_readent_func_t readent)
472 {
473         struct list_head tmp;
474         struct list_head __user *next;
475         lstcon_rpc_ent_t *ent;
476         srpc_generic_reply_t *rep;
477         lstcon_rpc_t *crpc;
478         srpc_msg_t *msg;
479         lstcon_node_t *nd;
480         long dur;
481         struct timeval tv;
482         int error;
483
484         LASSERT(head_up);
485
486         next = head_up;
487
488         list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
489                 if (copy_from_user(&tmp, next,
490                                    sizeof(struct list_head)))
491                         return -EFAULT;
492
493                 if (tmp.next == head_up)
494                         return 0;
495
496                 next = tmp.next;
497
498                 ent = list_entry(next, lstcon_rpc_ent_t, rpe_link);
499
500                 LASSERT(crpc->crp_stamp);
501
502                 error = lstcon_rpc_get_reply(crpc, &msg);
503
504                 nd = crpc->crp_node;
505
506                 dur = (long)cfs_time_sub(crpc->crp_stamp,
507                       (unsigned long)console_session.ses_id.ses_stamp);
508                 jiffies_to_timeval(dur, &tv);
509
510                 if (copy_to_user(&ent->rpe_peer, &nd->nd_id,
511                                  sizeof(lnet_process_id_t)) ||
512                     copy_to_user(&ent->rpe_stamp, &tv, sizeof(tv)) ||
513                     copy_to_user(&ent->rpe_state, &nd->nd_state,
514                                  sizeof(nd->nd_state)) ||
515                     copy_to_user(&ent->rpe_rpc_errno, &error,
516                                  sizeof(error)))
517                         return -EFAULT;
518
519                 if (error)
520                         continue;
521
522                 /* RPC is done */
523                 rep = (srpc_generic_reply_t *)&msg->msg_body.reply;
524
525                 if (copy_to_user(&ent->rpe_sid, &rep->sid, sizeof(lst_sid_t)) ||
526                     copy_to_user(&ent->rpe_fwk_errno, &rep->status,
527                                  sizeof(rep->status)))
528                         return -EFAULT;
529
530                 if (!readent)
531                         continue;
532
533                 error = readent(trans->tas_opc, msg, ent);
534
535                 if (error)
536                         return error;
537         }
538
539         return 0;
540 }
541
542 void
543 lstcon_rpc_trans_destroy(lstcon_rpc_trans_t *trans)
544 {
545         srpc_client_rpc_t *rpc;
546         lstcon_rpc_t *crpc;
547         lstcon_rpc_t *tmp;
548         int count = 0;
549
550         list_for_each_entry_safe(crpc, tmp, &trans->tas_rpcs_list, crp_link) {
551                 rpc = crpc->crp_rpc;
552
553                 spin_lock(&rpc->crpc_lock);
554
555                 /* free it if not posted or finished already */
556                 if (!crpc->crp_posted || crpc->crp_finished) {
557                         spin_unlock(&rpc->crpc_lock);
558
559                         list_del_init(&crpc->crp_link);
560                         lstcon_rpc_put(crpc);
561
562                         continue;
563                 }
564
565                 /*
566                  * rpcs can be still not callbacked (even LNetMDUnlink is called)
567                  * because huge timeout for inaccessible network, don't make
568                  * user wait for them, just abandon them, they will be recycled
569                  * in callback
570                  */
571                 LASSERT(crpc->crp_status);
572
573                 crpc->crp_node = NULL;
574                 crpc->crp_trans = NULL;
575                 list_del_init(&crpc->crp_link);
576                 count++;
577
578                 spin_unlock(&rpc->crpc_lock);
579
580                 atomic_dec(&trans->tas_remaining);
581         }
582
583         LASSERT(!atomic_read(&trans->tas_remaining));
584
585         list_del(&trans->tas_link);
586         if (!list_empty(&trans->tas_olink))
587                 list_del(&trans->tas_olink);
588
589         CDEBUG(D_NET, "Transaction %s destroyed with %d pending RPCs\n",
590                lstcon_rpc_trans_name(trans->tas_opc), count);
591
592         LIBCFS_FREE(trans, sizeof(*trans));
593 }
594
595 int
596 lstcon_sesrpc_prep(lstcon_node_t *nd, int transop,
597                    unsigned feats, lstcon_rpc_t **crpc)
598 {
599         srpc_mksn_reqst_t *msrq;
600         srpc_rmsn_reqst_t *rsrq;
601         int rc;
602
603         switch (transop) {
604         case LST_TRANS_SESNEW:
605                 rc = lstcon_rpc_prep(nd, SRPC_SERVICE_MAKE_SESSION,
606                                      feats, 0, 0, crpc);
607                 if (rc)
608                         return rc;
609
610                 msrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.mksn_reqst;
611                 msrq->mksn_sid = console_session.ses_id;
612                 msrq->mksn_force = console_session.ses_force;
613                 strlcpy(msrq->mksn_name, console_session.ses_name,
614                         sizeof(msrq->mksn_name));
615                 break;
616
617         case LST_TRANS_SESEND:
618                 rc = lstcon_rpc_prep(nd, SRPC_SERVICE_REMOVE_SESSION,
619                                      feats, 0, 0, crpc);
620                 if (rc)
621                         return rc;
622
623                 rsrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.rmsn_reqst;
624                 rsrq->rmsn_sid = console_session.ses_id;
625                 break;
626
627         default:
628                 LBUG();
629         }
630
631         return 0;
632 }
633
634 int
635 lstcon_dbgrpc_prep(lstcon_node_t *nd, unsigned feats, lstcon_rpc_t **crpc)
636 {
637         srpc_debug_reqst_t *drq;
638         int rc;
639
640         rc = lstcon_rpc_prep(nd, SRPC_SERVICE_DEBUG, feats, 0, 0, crpc);
641         if (rc)
642                 return rc;
643
644         drq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
645
646         drq->dbg_sid = console_session.ses_id;
647         drq->dbg_flags = 0;
648
649         return rc;
650 }
651
652 int
653 lstcon_batrpc_prep(lstcon_node_t *nd, int transop, unsigned feats,
654                    lstcon_tsb_hdr_t *tsb, lstcon_rpc_t **crpc)
655 {
656         lstcon_batch_t *batch;
657         srpc_batch_reqst_t *brq;
658         int rc;
659
660         rc = lstcon_rpc_prep(nd, SRPC_SERVICE_BATCH, feats, 0, 0, crpc);
661         if (rc)
662                 return rc;
663
664         brq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.bat_reqst;
665
666         brq->bar_sid = console_session.ses_id;
667         brq->bar_bid = tsb->tsb_id;
668         brq->bar_testidx = tsb->tsb_index;
669         brq->bar_opc = transop == LST_TRANS_TSBRUN ? SRPC_BATCH_OPC_RUN :
670                        (transop == LST_TRANS_TSBSTOP ? SRPC_BATCH_OPC_STOP :
671                        SRPC_BATCH_OPC_QUERY);
672
673         if (transop != LST_TRANS_TSBRUN &&
674             transop != LST_TRANS_TSBSTOP)
675                 return 0;
676
677         LASSERT(!tsb->tsb_index);
678
679         batch = (lstcon_batch_t *)tsb;
680         brq->bar_arg = batch->bat_arg;
681
682         return 0;
683 }
684
685 int
686 lstcon_statrpc_prep(lstcon_node_t *nd, unsigned feats, lstcon_rpc_t **crpc)
687 {
688         srpc_stat_reqst_t *srq;
689         int rc;
690
691         rc = lstcon_rpc_prep(nd, SRPC_SERVICE_QUERY_STAT, feats, 0, 0, crpc);
692         if (rc)
693                 return rc;
694
695         srq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.stat_reqst;
696
697         srq->str_sid = console_session.ses_id;
698         srq->str_type = 0; /* XXX remove it */
699
700         return 0;
701 }
702
703 static lnet_process_id_packed_t *
704 lstcon_next_id(int idx, int nkiov, lnet_kiov_t *kiov)
705 {
706         lnet_process_id_packed_t *pid;
707         int i;
708
709         i = idx / SFW_ID_PER_PAGE;
710
711         LASSERT(i < nkiov);
712
713         pid = (lnet_process_id_packed_t *)page_address(kiov[i].kiov_page);
714
715         return &pid[idx % SFW_ID_PER_PAGE];
716 }
717
718 static int
719 lstcon_dstnodes_prep(lstcon_group_t *grp, int idx,
720                      int dist, int span, int nkiov, lnet_kiov_t *kiov)
721 {
722         lnet_process_id_packed_t *pid;
723         lstcon_ndlink_t *ndl;
724         lstcon_node_t *nd;
725         int start;
726         int end;
727         int i = 0;
728
729         LASSERT(dist >= 1);
730         LASSERT(span >= 1);
731         LASSERT(grp->grp_nnode >= 1);
732
733         if (span > grp->grp_nnode)
734                 return -EINVAL;
735
736         start = ((idx / dist) * span) % grp->grp_nnode;
737         end = ((idx / dist) * span + span - 1) % grp->grp_nnode;
738
739         list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link) {
740                 nd = ndl->ndl_node;
741                 if (i < start) {
742                         i++;
743                         continue;
744                 }
745
746                 if (i > (end >= start ? end : grp->grp_nnode))
747                         break;
748
749                 pid = lstcon_next_id((i - start), nkiov, kiov);
750                 pid->nid = nd->nd_id.nid;
751                 pid->pid = nd->nd_id.pid;
752                 i++;
753         }
754
755         if (start <= end) /* done */
756                 return 0;
757
758         list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link) {
759                 if (i > grp->grp_nnode + end)
760                         break;
761
762                 nd = ndl->ndl_node;
763                 pid = lstcon_next_id((i - start), nkiov, kiov);
764                 pid->nid = nd->nd_id.nid;
765                 pid->pid = nd->nd_id.pid;
766                 i++;
767         }
768
769         return 0;
770 }
771
772 static int
773 lstcon_pingrpc_prep(lst_test_ping_param_t *param, srpc_test_reqst_t *req)
774 {
775         test_ping_req_t *prq = &req->tsr_u.ping;
776
777         prq->png_size = param->png_size;
778         prq->png_flags = param->png_flags;
779         /* TODO dest */
780         return 0;
781 }
782
783 static int
784 lstcon_bulkrpc_v0_prep(lst_test_bulk_param_t *param, srpc_test_reqst_t *req)
785 {
786         test_bulk_req_t *brq = &req->tsr_u.bulk_v0;
787
788         brq->blk_opc = param->blk_opc;
789         brq->blk_npg = (param->blk_size + PAGE_SIZE - 1) /
790                         PAGE_SIZE;
791         brq->blk_flags = param->blk_flags;
792
793         return 0;
794 }
795
796 static int
797 lstcon_bulkrpc_v1_prep(lst_test_bulk_param_t *param, srpc_test_reqst_t *req)
798 {
799         test_bulk_req_v1_t *brq = &req->tsr_u.bulk_v1;
800
801         brq->blk_opc = param->blk_opc;
802         brq->blk_flags = param->blk_flags;
803         brq->blk_len = param->blk_size;
804         brq->blk_offset = 0; /* reserved */
805
806         return 0;
807 }
808
809 int
810 lstcon_testrpc_prep(lstcon_node_t *nd, int transop, unsigned feats,
811                     lstcon_test_t *test, lstcon_rpc_t **crpc)
812 {
813         lstcon_group_t *sgrp = test->tes_src_grp;
814         lstcon_group_t *dgrp = test->tes_dst_grp;
815         srpc_test_reqst_t *trq;
816         srpc_bulk_t *bulk;
817         int i;
818         int npg = 0;
819         int nob = 0;
820         int rc = 0;
821
822         if (transop == LST_TRANS_TSBCLIADD) {
823                 npg = sfw_id_pages(test->tes_span);
824                 nob = !(feats & LST_FEAT_BULK_LEN) ?
825                       npg * PAGE_SIZE :
826                       sizeof(lnet_process_id_packed_t) * test->tes_span;
827         }
828
829         rc = lstcon_rpc_prep(nd, SRPC_SERVICE_TEST, feats, npg, nob, crpc);
830         if (rc)
831                 return rc;
832
833         trq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.tes_reqst;
834
835         if (transop == LST_TRANS_TSBSRVADD) {
836                 int ndist = (sgrp->grp_nnode + test->tes_dist - 1) /
837                             test->tes_dist;
838                 int nspan = (dgrp->grp_nnode + test->tes_span - 1) /
839                             test->tes_span;
840                 int nmax = (ndist + nspan - 1) / nspan;
841
842                 trq->tsr_ndest = 0;
843                 trq->tsr_loop = nmax * test->tes_dist * test->tes_concur;
844
845         } else {
846                 bulk = &(*crpc)->crp_rpc->crpc_bulk;
847
848                 for (i = 0; i < npg; i++) {
849                         int len;
850
851                         LASSERT(nob > 0);
852
853                         len = !(feats & LST_FEAT_BULK_LEN) ?
854                               PAGE_SIZE :
855                               min_t(int, nob, PAGE_SIZE);
856                         nob -= len;
857
858                         bulk->bk_iovs[i].kiov_offset = 0;
859                         bulk->bk_iovs[i].kiov_len = len;
860                         bulk->bk_iovs[i].kiov_page =
861                                 alloc_page(GFP_KERNEL);
862
863                         if (!bulk->bk_iovs[i].kiov_page) {
864                                 lstcon_rpc_put(*crpc);
865                                 return -ENOMEM;
866                         }
867                 }
868
869                 bulk->bk_sink = 0;
870
871                 LASSERT(transop == LST_TRANS_TSBCLIADD);
872
873                 rc = lstcon_dstnodes_prep(test->tes_dst_grp,
874                                           test->tes_cliidx++,
875                                           test->tes_dist,
876                                           test->tes_span,
877                                           npg, &bulk->bk_iovs[0]);
878                 if (rc) {
879                         lstcon_rpc_put(*crpc);
880                         return rc;
881                 }
882
883                 trq->tsr_ndest = test->tes_span;
884                 trq->tsr_loop = test->tes_loop;
885         }
886
887         trq->tsr_sid = console_session.ses_id;
888         trq->tsr_bid = test->tes_hdr.tsb_id;
889         trq->tsr_concur = test->tes_concur;
890         trq->tsr_is_client = (transop == LST_TRANS_TSBCLIADD) ? 1 : 0;
891         trq->tsr_stop_onerr = !!test->tes_stop_onerr;
892
893         switch (test->tes_type) {
894         case LST_TEST_PING:
895                 trq->tsr_service = SRPC_SERVICE_PING;
896                 rc = lstcon_pingrpc_prep((lst_test_ping_param_t *)
897                                          &test->tes_param[0], trq);
898                 break;
899
900         case LST_TEST_BULK:
901                 trq->tsr_service = SRPC_SERVICE_BRW;
902                 if (!(feats & LST_FEAT_BULK_LEN)) {
903                         rc = lstcon_bulkrpc_v0_prep((lst_test_bulk_param_t *)
904                                                     &test->tes_param[0], trq);
905                 } else {
906                         rc = lstcon_bulkrpc_v1_prep((lst_test_bulk_param_t *)
907                                                     &test->tes_param[0], trq);
908                 }
909
910                 break;
911         default:
912                 LBUG();
913                 break;
914         }
915
916         return rc;
917 }
918
919 static int
920 lstcon_sesnew_stat_reply(lstcon_rpc_trans_t *trans,
921                          lstcon_node_t *nd, srpc_msg_t *reply)
922 {
923         srpc_mksn_reply_t *mksn_rep = &reply->msg_body.mksn_reply;
924         int status = mksn_rep->mksn_status;
925
926         if (!status &&
927             (reply->msg_ses_feats & ~LST_FEATS_MASK)) {
928                 mksn_rep->mksn_status = EPROTO;
929                 status = EPROTO;
930         }
931
932         if (status == EPROTO) {
933                 CNETERR("session protocol error from %s: %u\n",
934                         libcfs_nid2str(nd->nd_id.nid),
935                         reply->msg_ses_feats);
936         }
937
938         if (status)
939                 return status;
940
941         if (!trans->tas_feats_updated) {
942                 spin_lock(&console_session.ses_rpc_lock);
943                 if (!trans->tas_feats_updated) { /* recheck with lock */
944                         trans->tas_feats_updated = 1;
945                         trans->tas_features = reply->msg_ses_feats;
946                 }
947                 spin_unlock(&console_session.ses_rpc_lock);
948         }
949
950         if (reply->msg_ses_feats != trans->tas_features) {
951                 CNETERR("Framework features %x from %s is different with features on this transaction: %x\n",
952                         reply->msg_ses_feats, libcfs_nid2str(nd->nd_id.nid),
953                         trans->tas_features);
954                 mksn_rep->mksn_status = EPROTO;
955                 status = EPROTO;
956         }
957
958         if (!status) {
959                 /* session timeout on remote node */
960                 nd->nd_timeout = mksn_rep->mksn_timeout;
961         }
962
963         return status;
964 }
965
966 void
967 lstcon_rpc_stat_reply(lstcon_rpc_trans_t *trans, srpc_msg_t *msg,
968                       lstcon_node_t *nd, lstcon_trans_stat_t *stat)
969 {
970         srpc_rmsn_reply_t *rmsn_rep;
971         srpc_debug_reply_t *dbg_rep;
972         srpc_batch_reply_t *bat_rep;
973         srpc_test_reply_t *test_rep;
974         srpc_stat_reply_t *stat_rep;
975         int rc = 0;
976
977         switch (trans->tas_opc) {
978         case LST_TRANS_SESNEW:
979                 rc = lstcon_sesnew_stat_reply(trans, nd, msg);
980                 if (!rc) {
981                         lstcon_sesop_stat_success(stat, 1);
982                         return;
983                 }
984
985                 lstcon_sesop_stat_failure(stat, 1);
986                 break;
987
988         case LST_TRANS_SESEND:
989                 rmsn_rep = &msg->msg_body.rmsn_reply;
990                 /* ESRCH is not an error for end session */
991                 if (!rmsn_rep->rmsn_status ||
992                     rmsn_rep->rmsn_status == ESRCH) {
993                         lstcon_sesop_stat_success(stat, 1);
994                         return;
995                 }
996
997                 lstcon_sesop_stat_failure(stat, 1);
998                 rc = rmsn_rep->rmsn_status;
999                 break;
1000
1001         case LST_TRANS_SESQRY:
1002         case LST_TRANS_SESPING:
1003                 dbg_rep = &msg->msg_body.dbg_reply;
1004
1005                 if (dbg_rep->dbg_status == ESRCH) {
1006                         lstcon_sesqry_stat_unknown(stat, 1);
1007                         return;
1008                 }
1009
1010                 if (lstcon_session_match(dbg_rep->dbg_sid))
1011                         lstcon_sesqry_stat_active(stat, 1);
1012                 else
1013                         lstcon_sesqry_stat_busy(stat, 1);
1014                 return;
1015
1016         case LST_TRANS_TSBRUN:
1017         case LST_TRANS_TSBSTOP:
1018                 bat_rep = &msg->msg_body.bat_reply;
1019
1020                 if (!bat_rep->bar_status) {
1021                         lstcon_tsbop_stat_success(stat, 1);
1022                         return;
1023                 }
1024
1025                 if (bat_rep->bar_status == EPERM &&
1026                     trans->tas_opc == LST_TRANS_TSBSTOP) {
1027                         lstcon_tsbop_stat_success(stat, 1);
1028                         return;
1029                 }
1030
1031                 lstcon_tsbop_stat_failure(stat, 1);
1032                 rc = bat_rep->bar_status;
1033                 break;
1034
1035         case LST_TRANS_TSBCLIQRY:
1036         case LST_TRANS_TSBSRVQRY:
1037                 bat_rep = &msg->msg_body.bat_reply;
1038
1039                 if (bat_rep->bar_active)
1040                         lstcon_tsbqry_stat_run(stat, 1);
1041                 else
1042                         lstcon_tsbqry_stat_idle(stat, 1);
1043
1044                 if (!bat_rep->bar_status)
1045                         return;
1046
1047                 lstcon_tsbqry_stat_failure(stat, 1);
1048                 rc = bat_rep->bar_status;
1049                 break;
1050
1051         case LST_TRANS_TSBCLIADD:
1052         case LST_TRANS_TSBSRVADD:
1053                 test_rep = &msg->msg_body.tes_reply;
1054
1055                 if (!test_rep->tsr_status) {
1056                         lstcon_tsbop_stat_success(stat, 1);
1057                         return;
1058                 }
1059
1060                 lstcon_tsbop_stat_failure(stat, 1);
1061                 rc = test_rep->tsr_status;
1062                 break;
1063
1064         case LST_TRANS_STATQRY:
1065                 stat_rep = &msg->msg_body.stat_reply;
1066
1067                 if (!stat_rep->str_status) {
1068                         lstcon_statqry_stat_success(stat, 1);
1069                         return;
1070                 }
1071
1072                 lstcon_statqry_stat_failure(stat, 1);
1073                 rc = stat_rep->str_status;
1074                 break;
1075
1076         default:
1077                 LBUG();
1078         }
1079
1080         if (!stat->trs_fwk_errno)
1081                 stat->trs_fwk_errno = rc;
1082 }
1083
1084 int
1085 lstcon_rpc_trans_ndlist(struct list_head *ndlist,
1086                         struct list_head *translist, int transop,
1087                         void *arg, lstcon_rpc_cond_func_t condition,
1088                         lstcon_rpc_trans_t **transpp)
1089 {
1090         lstcon_rpc_trans_t *trans;
1091         lstcon_ndlink_t *ndl;
1092         lstcon_node_t *nd;
1093         lstcon_rpc_t *rpc;
1094         unsigned feats;
1095         int rc;
1096
1097         /* Creating session RPG for list of nodes */
1098
1099         rc = lstcon_rpc_trans_prep(translist, transop, &trans);
1100         if (rc) {
1101                 CERROR("Can't create transaction %d: %d\n", transop, rc);
1102                 return rc;
1103         }
1104
1105         feats = trans->tas_features;
1106         list_for_each_entry(ndl, ndlist, ndl_link) {
1107                 rc = !condition ? 1 :
1108                      condition(transop, ndl->ndl_node, arg);
1109
1110                 if (!rc)
1111                         continue;
1112
1113                 if (rc < 0) {
1114                         CDEBUG(D_NET, "Condition error while creating RPC for transaction %d: %d\n",
1115                                transop, rc);
1116                         break;
1117                 }
1118
1119                 nd = ndl->ndl_node;
1120
1121                 switch (transop) {
1122                 case LST_TRANS_SESNEW:
1123                 case LST_TRANS_SESEND:
1124                         rc = lstcon_sesrpc_prep(nd, transop, feats, &rpc);
1125                         break;
1126                 case LST_TRANS_SESQRY:
1127                 case LST_TRANS_SESPING:
1128                         rc = lstcon_dbgrpc_prep(nd, feats, &rpc);
1129                         break;
1130                 case LST_TRANS_TSBCLIADD:
1131                 case LST_TRANS_TSBSRVADD:
1132                         rc = lstcon_testrpc_prep(nd, transop, feats,
1133                                                  (lstcon_test_t *)arg, &rpc);
1134                         break;
1135                 case LST_TRANS_TSBRUN:
1136                 case LST_TRANS_TSBSTOP:
1137                 case LST_TRANS_TSBCLIQRY:
1138                 case LST_TRANS_TSBSRVQRY:
1139                         rc = lstcon_batrpc_prep(nd, transop, feats,
1140                                                 (lstcon_tsb_hdr_t *)arg, &rpc);
1141                         break;
1142                 case LST_TRANS_STATQRY:
1143                         rc = lstcon_statrpc_prep(nd, feats, &rpc);
1144                         break;
1145                 default:
1146                         rc = -EINVAL;
1147                         break;
1148                 }
1149
1150                 if (rc) {
1151                         CERROR("Failed to create RPC for transaction %s: %d\n",
1152                                lstcon_rpc_trans_name(transop), rc);
1153                         break;
1154                 }
1155
1156                 lstcon_rpc_trans_addreq(trans, rpc);
1157         }
1158
1159         if (!rc) {
1160                 *transpp = trans;
1161                 return 0;
1162         }
1163
1164         lstcon_rpc_trans_destroy(trans);
1165
1166         return rc;
1167 }
1168
1169 static void
1170 lstcon_rpc_pinger(void *arg)
1171 {
1172         struct stt_timer *ptimer = (struct stt_timer *)arg;
1173         lstcon_rpc_trans_t *trans;
1174         lstcon_rpc_t *crpc;
1175         srpc_msg_t *rep;
1176         srpc_debug_reqst_t *drq;
1177         lstcon_ndlink_t *ndl;
1178         lstcon_node_t *nd;
1179         int intv;
1180         int count = 0;
1181         int rc;
1182
1183         /* RPC pinger is a special case of transaction,
1184          * it's called by timer at 8 seconds interval.
1185          */
1186         mutex_lock(&console_session.ses_mutex);
1187
1188         if (console_session.ses_shutdown || console_session.ses_expired) {
1189                 mutex_unlock(&console_session.ses_mutex);
1190                 return;
1191         }
1192
1193         if (!console_session.ses_expired &&
1194             ktime_get_real_seconds() - console_session.ses_laststamp >
1195             (time64_t)console_session.ses_timeout)
1196                 console_session.ses_expired = 1;
1197
1198         trans = console_session.ses_ping;
1199
1200         LASSERT(trans);
1201
1202         list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link) {
1203                 nd = ndl->ndl_node;
1204
1205                 if (console_session.ses_expired) {
1206                         /* idle console, end session on all nodes */
1207                         if (nd->nd_state != LST_NODE_ACTIVE)
1208                                 continue;
1209
1210                         rc = lstcon_sesrpc_prep(nd, LST_TRANS_SESEND,
1211                                                 trans->tas_features, &crpc);
1212                         if (rc) {
1213                                 CERROR("Out of memory\n");
1214                                 break;
1215                         }
1216
1217                         lstcon_rpc_trans_addreq(trans, crpc);
1218                         lstcon_rpc_post(crpc);
1219
1220                         continue;
1221                 }
1222
1223                 crpc = &nd->nd_ping;
1224
1225                 if (crpc->crp_rpc) {
1226                         LASSERT(crpc->crp_trans == trans);
1227                         LASSERT(!list_empty(&crpc->crp_link));
1228
1229                         spin_lock(&crpc->crp_rpc->crpc_lock);
1230
1231                         LASSERT(crpc->crp_posted);
1232
1233                         if (!crpc->crp_finished) {
1234                                 /* in flight */
1235                                 spin_unlock(&crpc->crp_rpc->crpc_lock);
1236                                 continue;
1237                         }
1238
1239                         spin_unlock(&crpc->crp_rpc->crpc_lock);
1240
1241                         lstcon_rpc_get_reply(crpc, &rep);
1242
1243                         list_del_init(&crpc->crp_link);
1244
1245                         lstcon_rpc_put(crpc);
1246                 }
1247
1248                 if (nd->nd_state != LST_NODE_ACTIVE)
1249                         continue;
1250
1251                 intv = (jiffies - nd->nd_stamp) / msecs_to_jiffies(MSEC_PER_SEC);
1252                 if (intv < nd->nd_timeout / 2)
1253                         continue;
1254
1255                 rc = lstcon_rpc_init(nd, SRPC_SERVICE_DEBUG,
1256                                      trans->tas_features, 0, 0, 1, crpc);
1257                 if (rc) {
1258                         CERROR("Out of memory\n");
1259                         break;
1260                 }
1261
1262                 drq = &crpc->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
1263
1264                 drq->dbg_sid = console_session.ses_id;
1265                 drq->dbg_flags = 0;
1266
1267                 lstcon_rpc_trans_addreq(trans, crpc);
1268                 lstcon_rpc_post(crpc);
1269
1270                 count++;
1271         }
1272
1273         if (console_session.ses_expired) {
1274                 mutex_unlock(&console_session.ses_mutex);
1275                 return;
1276         }
1277
1278         CDEBUG(D_NET, "Ping %d nodes in session\n", count);
1279
1280         ptimer->stt_expires = ktime_get_real_seconds() + LST_PING_INTERVAL;
1281         stt_add_timer(ptimer);
1282
1283         mutex_unlock(&console_session.ses_mutex);
1284 }
1285
1286 int
1287 lstcon_rpc_pinger_start(void)
1288 {
1289         struct stt_timer *ptimer;
1290         int rc;
1291
1292         LASSERT(list_empty(&console_session.ses_rpc_freelist));
1293         LASSERT(!atomic_read(&console_session.ses_rpc_counter));
1294
1295         rc = lstcon_rpc_trans_prep(NULL, LST_TRANS_SESPING,
1296                                    &console_session.ses_ping);
1297         if (rc) {
1298                 CERROR("Failed to create console pinger\n");
1299                 return rc;
1300         }
1301
1302         ptimer = &console_session.ses_ping_timer;
1303         ptimer->stt_expires = ktime_get_real_seconds() + LST_PING_INTERVAL;
1304
1305         stt_add_timer(ptimer);
1306
1307         return 0;
1308 }
1309
1310 void
1311 lstcon_rpc_pinger_stop(void)
1312 {
1313         LASSERT(console_session.ses_shutdown);
1314
1315         stt_del_timer(&console_session.ses_ping_timer);
1316
1317         lstcon_rpc_trans_abort(console_session.ses_ping, -ESHUTDOWN);
1318         lstcon_rpc_trans_stat(console_session.ses_ping, lstcon_trans_stat());
1319         lstcon_rpc_trans_destroy(console_session.ses_ping);
1320
1321         memset(lstcon_trans_stat(), 0, sizeof(lstcon_trans_stat_t));
1322
1323         console_session.ses_ping = NULL;
1324 }
1325
1326 void
1327 lstcon_rpc_cleanup_wait(void)
1328 {
1329         lstcon_rpc_trans_t *trans;
1330         lstcon_rpc_t *crpc;
1331         lstcon_rpc_t *temp;
1332         struct list_head *pacer;
1333         struct list_head zlist;
1334
1335         /* Called with hold of global mutex */
1336
1337         LASSERT(console_session.ses_shutdown);
1338
1339         while (!list_empty(&console_session.ses_trans_list)) {
1340                 list_for_each(pacer, &console_session.ses_trans_list) {
1341                         trans = list_entry(pacer, lstcon_rpc_trans_t,
1342                                            tas_link);
1343
1344                         CDEBUG(D_NET, "Session closed, wakeup transaction %s\n",
1345                                lstcon_rpc_trans_name(trans->tas_opc));
1346
1347                         wake_up(&trans->tas_waitq);
1348                 }
1349
1350                 mutex_unlock(&console_session.ses_mutex);
1351
1352                 CWARN("Session is shutting down, waiting for termination of transactions\n");
1353                 set_current_state(TASK_UNINTERRUPTIBLE);
1354                 schedule_timeout(cfs_time_seconds(1));
1355
1356                 mutex_lock(&console_session.ses_mutex);
1357         }
1358
1359         spin_lock(&console_session.ses_rpc_lock);
1360
1361         lst_wait_until(!atomic_read(&console_session.ses_rpc_counter),
1362                        console_session.ses_rpc_lock,
1363                        "Network is not accessible or target is down, waiting for %d console RPCs to being recycled\n",
1364                        atomic_read(&console_session.ses_rpc_counter));
1365
1366         list_add(&zlist, &console_session.ses_rpc_freelist);
1367         list_del_init(&console_session.ses_rpc_freelist);
1368
1369         spin_unlock(&console_session.ses_rpc_lock);
1370
1371         list_for_each_entry_safe(crpc, temp, &zlist, crp_link) {
1372                 list_del(&crpc->crp_link);
1373                 LIBCFS_FREE(crpc, sizeof(lstcon_rpc_t));
1374         }
1375 }
1376
1377 int
1378 lstcon_rpc_module_init(void)
1379 {
1380         INIT_LIST_HEAD(&console_session.ses_ping_timer.stt_list);
1381         console_session.ses_ping_timer.stt_func = lstcon_rpc_pinger;
1382         console_session.ses_ping_timer.stt_data = &console_session.ses_ping_timer;
1383
1384         console_session.ses_ping = NULL;
1385
1386         spin_lock_init(&console_session.ses_rpc_lock);
1387         atomic_set(&console_session.ses_rpc_counter, 0);
1388         INIT_LIST_HEAD(&console_session.ses_rpc_freelist);
1389
1390         return 0;
1391 }
1392
1393 void
1394 lstcon_rpc_module_fini(void)
1395 {
1396         LASSERT(list_empty(&console_session.ses_rpc_freelist));
1397         LASSERT(!atomic_read(&console_session.ses_rpc_counter));
1398 }