1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#define DEBUG_SUBSYSTEM S_SEC
38
39#include "../../include/linux/libcfs/libcfs.h"
40#include <linux/crypto.h>
41#include <linux/key.h>
42
43#include "../include/obd.h"
44#include "../include/obd_support.h"
45#include "../include/lustre_import.h"
46#include "../include/lustre_param.h"
47#include "../include/lustre_sec.h"
48
49#include "ptlrpc_internal.h"
50
51enum lustre_sec_part sptlrpc_target_sec_part(struct obd_device *obd)
52{
53 const char *type = obd->obd_type->typ_name;
54
55 if (!strcmp(type, LUSTRE_MDT_NAME))
56 return LUSTRE_SP_MDT;
57 if (!strcmp(type, LUSTRE_OST_NAME))
58 return LUSTRE_SP_OST;
59 if (!strcmp(type, LUSTRE_MGS_NAME))
60 return LUSTRE_SP_MGS;
61
62 CERROR("unknown target %p(%s)\n", obd, type);
63 return LUSTRE_SP_ANY;
64}
65EXPORT_SYMBOL(sptlrpc_target_sec_part);
66
67
68
69
70
71
72
73
74int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr)
75{
76 char buf[32];
77 char *bulk, *alg;
78
79 memset(flvr, 0, sizeof(*flvr));
80
81 if (!str || str[0] == '\0') {
82 flvr->sf_rpc = SPTLRPC_FLVR_INVALID;
83 return 0;
84 }
85
86 strlcpy(buf, str, sizeof(buf));
87
88 bulk = strchr(buf, '-');
89 if (bulk)
90 *bulk++ = '\0';
91
92 flvr->sf_rpc = sptlrpc_name2flavor_base(buf);
93 if (flvr->sf_rpc == SPTLRPC_FLVR_INVALID)
94 goto err_out;
95
96
97
98
99 if (flvr->sf_rpc == SPTLRPC_FLVR_PLAIN) {
100 flvr->u_bulk.hash.hash_alg = BULK_HASH_ALG_ADLER32;
101 if (bulk) {
102
103
104
105 alg = strchr(bulk, ':');
106 if (!alg)
107 goto err_out;
108 *alg++ = '\0';
109
110 if (strcmp(bulk, "hash"))
111 goto err_out;
112
113 flvr->u_bulk.hash.hash_alg = sptlrpc_get_hash_alg(alg);
114 if (flvr->u_bulk.hash.hash_alg >= BULK_HASH_ALG_MAX)
115 goto err_out;
116 }
117
118 if (flvr->u_bulk.hash.hash_alg == BULK_HASH_ALG_NULL)
119 flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_NULL);
120 else
121 flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_INTG);
122 } else {
123 if (bulk)
124 goto err_out;
125 }
126
127 flvr->sf_flags = 0;
128 return 0;
129
130err_out:
131 CERROR("invalid flavor string: %s\n", str);
132 return -EINVAL;
133}
134EXPORT_SYMBOL(sptlrpc_parse_flavor);
135
136
137
138
139
140static void get_default_flavor(struct sptlrpc_flavor *sf)
141{
142 memset(sf, 0, sizeof(*sf));
143
144 sf->sf_rpc = SPTLRPC_FLVR_NULL;
145 sf->sf_flags = 0;
146}
147
148static void sptlrpc_rule_init(struct sptlrpc_rule *rule)
149{
150 rule->sr_netid = LNET_NIDNET(LNET_NID_ANY);
151 rule->sr_from = LUSTRE_SP_ANY;
152 rule->sr_to = LUSTRE_SP_ANY;
153 rule->sr_padding = 0;
154
155 get_default_flavor(&rule->sr_flvr);
156}
157
158
159
160
161static int sptlrpc_parse_rule(char *param, struct sptlrpc_rule *rule)
162{
163 char *flavor, *dir;
164 int rc;
165
166 sptlrpc_rule_init(rule);
167
168 flavor = strchr(param, '=');
169 if (!flavor) {
170 CERROR("invalid param, no '='\n");
171 return -EINVAL;
172 }
173 *flavor++ = '\0';
174
175 dir = strchr(param, '.');
176 if (dir)
177 *dir++ = '\0';
178
179
180 if (strcmp(param, "default")) {
181 rule->sr_netid = libcfs_str2net(param);
182 if (rule->sr_netid == LNET_NIDNET(LNET_NID_ANY)) {
183 CERROR("invalid network name: %s\n", param);
184 return -EINVAL;
185 }
186 }
187
188
189 if (dir) {
190 if (!strcmp(dir, "mdt2ost")) {
191 rule->sr_from = LUSTRE_SP_MDT;
192 rule->sr_to = LUSTRE_SP_OST;
193 } else if (!strcmp(dir, "mdt2mdt")) {
194 rule->sr_from = LUSTRE_SP_MDT;
195 rule->sr_to = LUSTRE_SP_MDT;
196 } else if (!strcmp(dir, "cli2ost")) {
197 rule->sr_from = LUSTRE_SP_CLI;
198 rule->sr_to = LUSTRE_SP_OST;
199 } else if (!strcmp(dir, "cli2mdt")) {
200 rule->sr_from = LUSTRE_SP_CLI;
201 rule->sr_to = LUSTRE_SP_MDT;
202 } else {
203 CERROR("invalid rule dir segment: %s\n", dir);
204 return -EINVAL;
205 }
206 }
207
208
209 rc = sptlrpc_parse_flavor(flavor, &rule->sr_flvr);
210 if (rc)
211 return -EINVAL;
212
213 return 0;
214}
215
216static void sptlrpc_rule_set_free(struct sptlrpc_rule_set *rset)
217{
218 LASSERT(rset->srs_nslot ||
219 (rset->srs_nrule == 0 && !rset->srs_rules));
220
221 if (rset->srs_nslot) {
222 kfree(rset->srs_rules);
223 sptlrpc_rule_set_init(rset);
224 }
225}
226
227
228
229
230static int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *rset)
231{
232 struct sptlrpc_rule *rules;
233 int nslot;
234
235 might_sleep();
236
237 if (rset->srs_nrule < rset->srs_nslot)
238 return 0;
239
240 nslot = rset->srs_nslot + 8;
241
242
243 rules = kcalloc(nslot, sizeof(*rset->srs_rules), GFP_NOFS);
244 if (!rules)
245 return -ENOMEM;
246
247 if (rset->srs_nrule) {
248 LASSERT(rset->srs_nslot && rset->srs_rules);
249 memcpy(rules, rset->srs_rules,
250 rset->srs_nrule * sizeof(*rset->srs_rules));
251
252 kfree(rset->srs_rules);
253 }
254
255 rset->srs_rules = rules;
256 rset->srs_nslot = nslot;
257 return 0;
258}
259
260static inline int rule_spec_dir(struct sptlrpc_rule *rule)
261{
262 return (rule->sr_from != LUSTRE_SP_ANY ||
263 rule->sr_to != LUSTRE_SP_ANY);
264}
265
266static inline int rule_spec_net(struct sptlrpc_rule *rule)
267{
268 return (rule->sr_netid != LNET_NIDNET(LNET_NID_ANY));
269}
270
271static inline int rule_match_dir(struct sptlrpc_rule *r1,
272 struct sptlrpc_rule *r2)
273{
274 return (r1->sr_from == r2->sr_from && r1->sr_to == r2->sr_to);
275}
276
277static inline int rule_match_net(struct sptlrpc_rule *r1,
278 struct sptlrpc_rule *r2)
279{
280 return (r1->sr_netid == r2->sr_netid);
281}
282
283
284
285
286
287static int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset,
288 struct sptlrpc_rule *rule)
289{
290 struct sptlrpc_rule *p = rset->srs_rules;
291 int spec_dir, spec_net;
292 int rc, n, match = 0;
293
294 might_sleep();
295
296 spec_net = rule_spec_net(rule);
297 spec_dir = rule_spec_dir(rule);
298
299 for (n = 0; n < rset->srs_nrule; n++) {
300 p = &rset->srs_rules[n];
301
302
303
304
305
306
307
308 if (!rule_match_net(p, rule)) {
309 if (spec_net) {
310 if (rule_spec_net(p))
311 continue;
312 else
313 break;
314 } else {
315 continue;
316 }
317 }
318
319
320 if (!rule_match_dir(p, rule)) {
321 if (spec_dir) {
322 if (rule_spec_dir(p))
323 continue;
324 else
325 break;
326 } else {
327 continue;
328 }
329 }
330
331
332 match = 1;
333 break;
334 }
335
336 if (match) {
337 LASSERT(n >= 0 && n < rset->srs_nrule);
338
339 if (rule->sr_flvr.sf_rpc == SPTLRPC_FLVR_INVALID) {
340
341 if (n < rset->srs_nrule - 1)
342 memmove(&rset->srs_rules[n],
343 &rset->srs_rules[n + 1],
344 (rset->srs_nrule - n - 1) *
345 sizeof(*rule));
346 rset->srs_nrule--;
347 } else {
348
349 memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
350 }
351 } else {
352 LASSERT(n >= 0 && n <= rset->srs_nrule);
353
354 if (rule->sr_flvr.sf_rpc != SPTLRPC_FLVR_INVALID) {
355 rc = sptlrpc_rule_set_expand(rset);
356 if (rc)
357 return rc;
358
359 if (n < rset->srs_nrule)
360 memmove(&rset->srs_rules[n + 1],
361 &rset->srs_rules[n],
362 (rset->srs_nrule - n) * sizeof(*rule));
363 memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
364 rset->srs_nrule++;
365 } else {
366 CDEBUG(D_CONFIG, "ignore the unmatched deletion\n");
367 }
368 }
369
370 return 0;
371}
372
373
374
375
376
377static int sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset,
378 enum lustre_sec_part from,
379 enum lustre_sec_part to,
380 lnet_nid_t nid,
381 struct sptlrpc_flavor *sf)
382{
383 struct sptlrpc_rule *r;
384 int n;
385
386 for (n = 0; n < rset->srs_nrule; n++) {
387 r = &rset->srs_rules[n];
388
389 if (LNET_NIDNET(nid) != LNET_NIDNET(LNET_NID_ANY) &&
390 r->sr_netid != LNET_NIDNET(LNET_NID_ANY) &&
391 LNET_NIDNET(nid) != r->sr_netid)
392 continue;
393
394 if (from != LUSTRE_SP_ANY && r->sr_from != LUSTRE_SP_ANY &&
395 from != r->sr_from)
396 continue;
397
398 if (to != LUSTRE_SP_ANY && r->sr_to != LUSTRE_SP_ANY &&
399 to != r->sr_to)
400 continue;
401
402 *sf = r->sr_flvr;
403 return 1;
404 }
405
406 return 0;
407}
408
409
410
411
412
413struct sptlrpc_conf_tgt {
414 struct list_head sct_list;
415 char sct_name[MAX_OBD_NAME];
416 struct sptlrpc_rule_set sct_rset;
417};
418
419struct sptlrpc_conf {
420 struct list_head sc_list;
421 char sc_fsname[MTI_NAME_MAXLEN];
422 unsigned int sc_modified;
423 unsigned int sc_updated:1,
424 sc_local:1;
425 struct sptlrpc_rule_set sc_rset;
426 struct list_head sc_tgts;
427};
428
429static struct mutex sptlrpc_conf_lock;
430static LIST_HEAD(sptlrpc_confs);
431
432static inline int is_hex(char c)
433{
434 return ((c >= '0' && c <= '9') ||
435 (c >= 'a' && c <= 'f'));
436}
437
438static void target2fsname(const char *tgt, char *fsname, int buflen)
439{
440 const char *ptr;
441 int len;
442
443 ptr = strrchr(tgt, '-');
444 if (ptr) {
445 if ((strncmp(ptr, "-MDT", 4) != 0 &&
446 strncmp(ptr, "-OST", 4) != 0) ||
447 !is_hex(ptr[4]) || !is_hex(ptr[5]) ||
448 !is_hex(ptr[6]) || !is_hex(ptr[7]))
449 ptr = NULL;
450 }
451
452
453 if (!ptr)
454 len = strlen(tgt);
455 else
456 len = ptr - tgt;
457
458 len = min(len, buflen - 1);
459 memcpy(fsname, tgt, len);
460 fsname[len] = '\0';
461}
462
463static void sptlrpc_conf_free_rsets(struct sptlrpc_conf *conf)
464{
465 struct sptlrpc_conf_tgt *conf_tgt, *conf_tgt_next;
466
467 sptlrpc_rule_set_free(&conf->sc_rset);
468
469 list_for_each_entry_safe(conf_tgt, conf_tgt_next,
470 &conf->sc_tgts, sct_list) {
471 sptlrpc_rule_set_free(&conf_tgt->sct_rset);
472 list_del(&conf_tgt->sct_list);
473 kfree(conf_tgt);
474 }
475 LASSERT(list_empty(&conf->sc_tgts));
476
477 conf->sc_updated = 0;
478 conf->sc_local = 0;
479}
480
481static void sptlrpc_conf_free(struct sptlrpc_conf *conf)
482{
483 CDEBUG(D_SEC, "free sptlrpc conf %s\n", conf->sc_fsname);
484
485 sptlrpc_conf_free_rsets(conf);
486 list_del(&conf->sc_list);
487 kfree(conf);
488}
489
490static
491struct sptlrpc_conf_tgt *sptlrpc_conf_get_tgt(struct sptlrpc_conf *conf,
492 const char *name,
493 int create)
494{
495 struct sptlrpc_conf_tgt *conf_tgt;
496
497 list_for_each_entry(conf_tgt, &conf->sc_tgts, sct_list) {
498 if (strcmp(conf_tgt->sct_name, name) == 0)
499 return conf_tgt;
500 }
501
502 if (!create)
503 return NULL;
504
505 conf_tgt = kzalloc(sizeof(*conf_tgt), GFP_NOFS);
506 if (conf_tgt) {
507 strlcpy(conf_tgt->sct_name, name, sizeof(conf_tgt->sct_name));
508 sptlrpc_rule_set_init(&conf_tgt->sct_rset);
509 list_add(&conf_tgt->sct_list, &conf->sc_tgts);
510 }
511
512 return conf_tgt;
513}
514
515static
516struct sptlrpc_conf *sptlrpc_conf_get(const char *fsname,
517 int create)
518{
519 struct sptlrpc_conf *conf;
520 size_t len;
521
522 list_for_each_entry(conf, &sptlrpc_confs, sc_list) {
523 if (strcmp(conf->sc_fsname, fsname) == 0)
524 return conf;
525 }
526
527 if (!create)
528 return NULL;
529
530 conf = kzalloc(sizeof(*conf), GFP_NOFS);
531 if (!conf)
532 return NULL;
533
534 len = strlcpy(conf->sc_fsname, fsname, sizeof(conf->sc_fsname));
535 if (len >= sizeof(conf->sc_fsname)) {
536 kfree(conf);
537 return NULL;
538 }
539 sptlrpc_rule_set_init(&conf->sc_rset);
540 INIT_LIST_HEAD(&conf->sc_tgts);
541 list_add(&conf->sc_list, &sptlrpc_confs);
542
543 CDEBUG(D_SEC, "create sptlrpc conf %s\n", conf->sc_fsname);
544 return conf;
545}
546
547
548
549
550static int sptlrpc_conf_merge_rule(struct sptlrpc_conf *conf,
551 const char *target,
552 struct sptlrpc_rule *rule)
553{
554 struct sptlrpc_conf_tgt *conf_tgt;
555 struct sptlrpc_rule_set *rule_set;
556
557
558 if (strcmp(conf->sc_fsname, target) == 0) {
559 rule_set = &conf->sc_rset;
560 } else {
561 conf_tgt = sptlrpc_conf_get_tgt(conf, target, 1);
562 if (conf_tgt) {
563 rule_set = &conf_tgt->sct_rset;
564 } else {
565 CERROR("out of memory, can't merge rule!\n");
566 return -ENOMEM;
567 }
568 }
569
570 return sptlrpc_rule_set_merge(rule_set, rule);
571}
572
573
574
575
576
577
578static int __sptlrpc_process_config(struct lustre_cfg *lcfg,
579 struct sptlrpc_conf *conf)
580{
581 char *target, *param;
582 char fsname[MTI_NAME_MAXLEN];
583 struct sptlrpc_rule rule;
584 int rc;
585
586 target = lustre_cfg_string(lcfg, 1);
587 if (!target) {
588 CERROR("missing target name\n");
589 return -EINVAL;
590 }
591
592 param = lustre_cfg_string(lcfg, 2);
593 if (!param) {
594 CERROR("missing parameter\n");
595 return -EINVAL;
596 }
597
598 CDEBUG(D_SEC, "processing rule: %s.%s\n", target, param);
599
600
601 if (strncmp(param, PARAM_SRPC_FLVR, sizeof(PARAM_SRPC_FLVR) - 1) != 0) {
602 CERROR("Invalid sptlrpc parameter: %s\n", param);
603 return -EINVAL;
604 }
605 param += sizeof(PARAM_SRPC_FLVR) - 1;
606
607 rc = sptlrpc_parse_rule(param, &rule);
608 if (rc)
609 return -EINVAL;
610
611 if (!conf) {
612 target2fsname(target, fsname, sizeof(fsname));
613
614 mutex_lock(&sptlrpc_conf_lock);
615 conf = sptlrpc_conf_get(fsname, 0);
616 if (!conf) {
617 CERROR("can't find conf\n");
618 rc = -ENOMEM;
619 } else {
620 rc = sptlrpc_conf_merge_rule(conf, target, &rule);
621 }
622 mutex_unlock(&sptlrpc_conf_lock);
623 } else {
624 LASSERT(mutex_is_locked(&sptlrpc_conf_lock));
625 rc = sptlrpc_conf_merge_rule(conf, target, &rule);
626 }
627
628 if (rc == 0)
629 conf->sc_modified++;
630
631 return rc;
632}
633
634int sptlrpc_process_config(struct lustre_cfg *lcfg)
635{
636 return __sptlrpc_process_config(lcfg, NULL);
637}
638EXPORT_SYMBOL(sptlrpc_process_config);
639
640static int logname2fsname(const char *logname, char *buf, int buflen)
641{
642 char *ptr;
643 int len;
644
645 ptr = strrchr(logname, '-');
646 if (!ptr || strcmp(ptr, "-sptlrpc")) {
647 CERROR("%s is not a sptlrpc config log\n", logname);
648 return -EINVAL;
649 }
650
651 len = min((int) (ptr - logname), buflen - 1);
652
653 memcpy(buf, logname, len);
654 buf[len] = '\0';
655 return 0;
656}
657
658void sptlrpc_conf_log_update_begin(const char *logname)
659{
660 struct sptlrpc_conf *conf;
661 char fsname[16];
662
663 if (logname2fsname(logname, fsname, sizeof(fsname)))
664 return;
665
666 mutex_lock(&sptlrpc_conf_lock);
667
668 conf = sptlrpc_conf_get(fsname, 0);
669 if (conf) {
670 if (conf->sc_local) {
671 LASSERT(conf->sc_updated == 0);
672 sptlrpc_conf_free_rsets(conf);
673 }
674 conf->sc_modified = 0;
675 }
676
677 mutex_unlock(&sptlrpc_conf_lock);
678}
679EXPORT_SYMBOL(sptlrpc_conf_log_update_begin);
680
681
682
683
684void sptlrpc_conf_log_update_end(const char *logname)
685{
686 struct sptlrpc_conf *conf;
687 char fsname[16];
688
689 if (logname2fsname(logname, fsname, sizeof(fsname)))
690 return;
691
692 mutex_lock(&sptlrpc_conf_lock);
693
694 conf = sptlrpc_conf_get(fsname, 0);
695 if (conf) {
696
697
698
699
700 if (conf->sc_updated == 0)
701 conf->sc_modified++;
702
703 conf->sc_updated = 1;
704 }
705
706 mutex_unlock(&sptlrpc_conf_lock);
707}
708EXPORT_SYMBOL(sptlrpc_conf_log_update_end);
709
710void sptlrpc_conf_log_start(const char *logname)
711{
712 char fsname[16];
713
714 if (logname2fsname(logname, fsname, sizeof(fsname)))
715 return;
716
717 mutex_lock(&sptlrpc_conf_lock);
718 sptlrpc_conf_get(fsname, 1);
719 mutex_unlock(&sptlrpc_conf_lock);
720}
721EXPORT_SYMBOL(sptlrpc_conf_log_start);
722
723void sptlrpc_conf_log_stop(const char *logname)
724{
725 struct sptlrpc_conf *conf;
726 char fsname[16];
727
728 if (logname2fsname(logname, fsname, sizeof(fsname)))
729 return;
730
731 mutex_lock(&sptlrpc_conf_lock);
732 conf = sptlrpc_conf_get(fsname, 0);
733 if (conf)
734 sptlrpc_conf_free(conf);
735 mutex_unlock(&sptlrpc_conf_lock);
736}
737EXPORT_SYMBOL(sptlrpc_conf_log_stop);
738
739static inline void flavor_set_flags(struct sptlrpc_flavor *sf,
740 enum lustre_sec_part from,
741 enum lustre_sec_part to,
742 unsigned int fl_udesc)
743{
744
745
746
747
748 if (sf->sf_rpc == SPTLRPC_FLVR_NULL)
749 return;
750
751 if (from == LUSTRE_SP_MDT) {
752
753 sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY;
754 } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_OST) {
755
756 sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_BULK;
757 } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_MDT) {
758
759 if (fl_udesc && sf->sf_rpc != SPTLRPC_FLVR_NULL)
760 sf->sf_flags |= PTLRPC_SEC_FL_UDESC;
761 }
762}
763
764void sptlrpc_conf_choose_flavor(enum lustre_sec_part from,
765 enum lustre_sec_part to,
766 struct obd_uuid *target,
767 lnet_nid_t nid,
768 struct sptlrpc_flavor *sf)
769{
770 struct sptlrpc_conf *conf;
771 struct sptlrpc_conf_tgt *conf_tgt;
772 char name[MTI_NAME_MAXLEN];
773 int len, rc = 0;
774
775 target2fsname(target->uuid, name, sizeof(name));
776
777 mutex_lock(&sptlrpc_conf_lock);
778
779 conf = sptlrpc_conf_get(name, 0);
780 if (!conf)
781 goto out;
782
783
784 len = strlen(target->uuid);
785 LASSERT(len > 5);
786 memcpy(name, target->uuid, len - 5);
787 name[len - 5] = '\0';
788
789 conf_tgt = sptlrpc_conf_get_tgt(conf, name, 0);
790 if (conf_tgt) {
791 rc = sptlrpc_rule_set_choose(&conf_tgt->sct_rset,
792 from, to, nid, sf);
793 if (rc)
794 goto out;
795 }
796
797 rc = sptlrpc_rule_set_choose(&conf->sc_rset, from, to, nid, sf);
798out:
799 mutex_unlock(&sptlrpc_conf_lock);
800
801 if (rc == 0)
802 get_default_flavor(sf);
803
804 flavor_set_flags(sf, from, to, 1);
805}
806
807#define SEC_ADAPT_DELAY (10)
808
809
810
811
812
813void sptlrpc_conf_client_adapt(struct obd_device *obd)
814{
815 struct obd_import *imp;
816
817 LASSERT(strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 ||
818 strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) == 0);
819 CDEBUG(D_SEC, "obd %s\n", obd->u.cli.cl_target_uuid.uuid);
820
821
822 down_read(&obd->u.cli.cl_sem);
823
824 imp = obd->u.cli.cl_import;
825 if (imp) {
826 spin_lock(&imp->imp_lock);
827 if (imp->imp_sec)
828 imp->imp_sec_expire = ktime_get_real_seconds() +
829 SEC_ADAPT_DELAY;
830 spin_unlock(&imp->imp_lock);
831 }
832
833 up_read(&obd->u.cli.cl_sem);
834}
835EXPORT_SYMBOL(sptlrpc_conf_client_adapt);
836
837int sptlrpc_conf_init(void)
838{
839 mutex_init(&sptlrpc_conf_lock);
840 return 0;
841}
842
843void sptlrpc_conf_fini(void)
844{
845 struct sptlrpc_conf *conf, *conf_next;
846
847 mutex_lock(&sptlrpc_conf_lock);
848 list_for_each_entry_safe(conf, conf_next, &sptlrpc_confs, sc_list) {
849 sptlrpc_conf_free(conf);
850 }
851 LASSERT(list_empty(&sptlrpc_confs));
852 mutex_unlock(&sptlrpc_conf_lock);
853}
854