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