X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Fptlrpc%2Fsec_config.c;h=6812d82e3789fc2cc5d08bbb3b3b0b47d62f7da3;hp=3534ba2250cf7f0a1dadfa65a8cb2af37d28ac7e;hb=907a321c9b9e2cd5f5ccf488cc516ba05dee0ad8;hpb=3192e52a89946f12fd36d28a686c169d01d36e64 diff --git a/lustre/ptlrpc/sec_config.c b/lustre/ptlrpc/sec_config.c index 3534ba2..6812d82 100644 --- a/lustre/ptlrpc/sec_config.c +++ b/lustre/ptlrpc/sec_config.c @@ -1,44 +1,54 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. * - * Copyright (C) 2007 Cluster File Systems, Inc. + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). * - * This file is part of Lustre, http://www.lustre.org. + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GPL HEADER END + */ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Use is subject to license terms. * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Copyright (c) 2011, 2014, Intel Corporation. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * Lustre is a trademark of Sun Microsystems, Inc. */ -#ifndef EXPORT_SYMTAB -#define EXPORT_SYMTAB -#endif #define DEBUG_SUBSYSTEM S_SEC #include -#ifndef __KERNEL__ -#include -#include -#else #include #include -#endif #include #include #include #include #include +#include +#include #include +#include #include #include "ptlrpc_internal.h" @@ -52,6 +62,8 @@ const char *sptlrpc_part2name(enum lustre_sec_part part) return "mdt"; case LUSTRE_SP_OST: return "ost"; + case LUSTRE_SP_MGC: + return "mgc"; case LUSTRE_SP_MGS: return "mgs"; case LUSTRE_SP_ANY: @@ -76,215 +88,88 @@ enum lustre_sec_part sptlrpc_target_sec_part(struct obd_device *obd) CERROR("unknown target %p(%s)\n", obd, type); return LUSTRE_SP_ANY; } -EXPORT_SYMBOL(sptlrpc_target_sec_part); /**************************************** * user supplied flavor string parsing * ****************************************/ -typedef enum { - BULK_TYPE_N = 0, - BULK_TYPE_I = 1, - BULK_TYPE_P = 2 -} bulk_type_t; - -static void get_default_flavor(struct sptlrpc_flavor *sf) -{ - sf->sf_rpc = SPTLRPC_FLVR_NULL; - sf->sf_bulk_priv = BULK_PRIV_ALG_NULL; - sf->sf_bulk_csum = BULK_CSUM_ALG_NULL; - sf->sf_flags = 0; -} - -static void get_flavor_by_rpc(struct sptlrpc_rule *rule, __u16 rpc_flavor) -{ - get_default_flavor(&rule->sr_flvr); - - rule->sr_flvr.sf_rpc = rpc_flavor; - - switch (rpc_flavor) { - case SPTLRPC_FLVR_NULL: - case SPTLRPC_FLVR_PLAIN: - case SPTLRPC_FLVR_KRB5N: - case SPTLRPC_FLVR_KRB5A: - break; - case SPTLRPC_FLVR_KRB5P: - rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_ARC4; - /* fall through */ - case SPTLRPC_FLVR_KRB5I: - rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1; - break; - default: - LBUG(); - } -} - -static void get_flavor_by_bulk(struct sptlrpc_rule *rule, bulk_type_t bulk_type) -{ - switch (bulk_type) { - case BULK_TYPE_N: - rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_NULL; - rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL; - break; - case BULK_TYPE_I: - rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1; - rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL; - break; - case BULK_TYPE_P: - rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1; - rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_ARC4; - break; - default: - LBUG(); - } -} - -static __u16 __flavors[] = { - SPTLRPC_FLVR_NULL, - SPTLRPC_FLVR_PLAIN, - SPTLRPC_FLVR_KRB5N, - SPTLRPC_FLVR_KRB5A, - SPTLRPC_FLVR_KRB5I, - SPTLRPC_FLVR_KRB5P, -}; - -#define __nflavors ARRAY_SIZE(__flavors) - /* - * flavor string format: rpc[-bulk{n|i|p}[:cksum/enc]] - * for examples: - * null - * plain-bulki - * krb5p-bulkn - * krb5i-bulkp - * krb5i-bulkp:sha512/arc4 + * format: [-] */ -static int parse_flavor(char *str, struct sptlrpc_rule *rule) +int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr) { - const char *f; - char *bulk, *alg, *enc; - char buf[64]; - bulk_type_t bulk_type; - __u8 i; - ENTRY; + char buf[32]; + char *bulk, *alg; - if (str == NULL || str[0] == '\0') { - rule->sr_flvr.sf_rpc = SPTLRPC_FLVR_INVALID; - goto out; - } + memset(flvr, 0, sizeof(*flvr)); - for (i = 0; i < __nflavors; i++) { - f = sptlrpc_rpcflavor2name(__flavors[i]); - if (strncmp(str, f, strlen(f)) == 0) - break; + if (str == NULL || str[0] == '\0') { + flvr->sf_rpc = SPTLRPC_FLVR_INVALID; + return 0; } - if (i >= __nflavors) - GOTO(invalid, -EINVAL); + strlcpy(buf, str, sizeof(buf)); - /* prepare local buffer thus we can modify it as we want */ - strncpy(buf, str, 64); - buf[64 - 1] = '\0'; - - /* find bulk string */ bulk = strchr(buf, '-'); if (bulk) *bulk++ = '\0'; - /* now the first part must equal to rpc flavor name */ - if (strcmp(buf, f) != 0) - GOTO(invalid, -EINVAL); - - get_flavor_by_rpc(rule, __flavors[i]); - - if (bulk == NULL) - goto out; - - /* find bulk algorithm string */ - alg = strchr(bulk, ':'); - if (alg) - *alg++ = '\0'; - - /* verify bulk section */ - if (strcmp(bulk, "bulkn") == 0) { - rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_NULL; - rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL; - bulk_type = BULK_TYPE_N; - } else if (strcmp(bulk, "bulki") == 0) - bulk_type = BULK_TYPE_I; - else if (strcmp(bulk, "bulkp") == 0) - bulk_type = BULK_TYPE_P; - else - GOTO(invalid, -EINVAL); - - /* null flavor don't support bulk i/p */ - if (__flavors[i] == SPTLRPC_FLVR_NULL && bulk_type != BULK_TYPE_N) - GOTO(invalid, -EINVAL); - - /* plain policy dosen't support bulk p */ - if (__flavors[i] == SPTLRPC_FLVR_PLAIN && bulk_type == BULK_TYPE_P) - GOTO(invalid, -EINVAL); - - get_flavor_by_bulk(rule, bulk_type); - - if (alg == NULL) - goto out; - - /* find encryption algorithm string */ - enc = strchr(alg, '/'); - if (enc) - *enc++ = '\0'; - - /* checksum algorithm */ - for (i = 0; i < BULK_CSUM_ALG_MAX; i++) { - if (strcmp(alg, sptlrpc_bulk_csum_alg2name(i)) == 0) { - rule->sr_flvr.sf_bulk_csum = i; - break; - } - } - if (i >= BULK_CSUM_ALG_MAX) - GOTO(invalid, -EINVAL); - - /* privacy algorithm */ - if (enc) { - for (i = 0; i < BULK_PRIV_ALG_MAX; i++) { - if (strcmp(enc, sptlrpc_bulk_priv_alg2name(i)) == 0) { - rule->sr_flvr.sf_bulk_priv = i; - break; - } - } - if (i >= BULK_PRIV_ALG_MAX) - GOTO(invalid, -EINVAL); - } + flvr->sf_rpc = sptlrpc_name2flavor_base(buf); + if (flvr->sf_rpc == SPTLRPC_FLVR_INVALID) + goto err_out; /* - * bulk combination sanity checks + * currently only base flavor "plain" can have bulk specification. */ - if (bulk_type == BULK_TYPE_P && - rule->sr_flvr.sf_bulk_priv == BULK_PRIV_ALG_NULL) - GOTO(invalid, -EINVAL); - - if (bulk_type == BULK_TYPE_I && - (rule->sr_flvr.sf_bulk_csum == BULK_CSUM_ALG_NULL || - rule->sr_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL)) - GOTO(invalid, -EINVAL); + if (flvr->sf_rpc == SPTLRPC_FLVR_PLAIN) { + flvr->u_bulk.hash.hash_alg = BULK_HASH_ALG_ADLER32; + if (bulk) { + /* + * format: plain-hash: + */ + alg = strchr(bulk, ':'); + if (alg == NULL) + goto err_out; + *alg++ = '\0'; + + if (strcmp(bulk, "hash")) + goto err_out; + + flvr->u_bulk.hash.hash_alg = sptlrpc_get_hash_alg(alg); + if (flvr->u_bulk.hash.hash_alg >= BULK_HASH_ALG_MAX) + goto err_out; + } - if (bulk_type == BULK_TYPE_N && - (rule->sr_flvr.sf_bulk_csum != BULK_CSUM_ALG_NULL || - rule->sr_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL)) - GOTO(invalid, -EINVAL); + if (flvr->u_bulk.hash.hash_alg == BULK_HASH_ALG_NULL) + flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_NULL); + else + flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_INTG); + } else { + if (bulk) + goto err_out; + } -out: + flvr->sf_flags = 0; return 0; -invalid: + +err_out: CERROR("invalid flavor string: %s\n", str); return -EINVAL; } +EXPORT_SYMBOL(sptlrpc_parse_flavor); /**************************************** * configure rules * ****************************************/ +static void get_default_flavor(struct sptlrpc_flavor *sf) +{ + memset(sf, 0, sizeof(*sf)); + + sf->sf_rpc = SPTLRPC_FLVR_NULL; + sf->sf_flags = 0; +} + static void sptlrpc_rule_init(struct sptlrpc_rule *rule) { rule->sr_netid = LNET_NIDNET(LNET_NID_ANY); @@ -346,7 +231,7 @@ int sptlrpc_parse_rule(char *param, struct sptlrpc_rule *rule) } /* 2.1 flavor */ - rc = parse_flavor(flavor, rule); + rc = sptlrpc_parse_flavor(flavor, &rule->sr_flvr); if (rc) RETURN(-EINVAL); @@ -369,41 +254,37 @@ EXPORT_SYMBOL(sptlrpc_rule_set_free); /* * return 0 if the rule set could accomodate one more rule. - * if @expand != 0, the rule set might be expanded. */ -int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *rset, int expand) +int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *rset) { - struct sptlrpc_rule *rules; - int nslot; + struct sptlrpc_rule *rules; + int nslot; - if (rset->srs_nrule < rset->srs_nslot) - return 0; + might_sleep(); - if (expand == 0) - return -E2BIG; + if (rset->srs_nrule < rset->srs_nslot) + return 0; - if (rset->srs_nslot == 0) - nslot = 8; - else - nslot = rset->srs_nslot + 8; + nslot = rset->srs_nslot + 8; /* better use realloc() if available */ OBD_ALLOC(rules, nslot * sizeof(*rset->srs_rules)); if (rules == NULL) return -ENOMEM; - memcpy(rules, rset->srs_rules, - rset->srs_nrule * sizeof(*rset->srs_rules)); + if (rset->srs_nrule) { + LASSERT(rset->srs_nslot && rset->srs_rules); + memcpy(rules, rset->srs_rules, + rset->srs_nrule * sizeof(*rset->srs_rules)); - if (rset->srs_rules) OBD_FREE(rset->srs_rules, rset->srs_nslot * sizeof(*rset->srs_rules)); + } rset->srs_rules = rules; rset->srs_nslot = nslot; return 0; } -EXPORT_SYMBOL(sptlrpc_rule_set_expand); static inline int rule_spec_dir(struct sptlrpc_rule *rule) { @@ -427,18 +308,19 @@ static inline int rule_match_net(struct sptlrpc_rule *r1, /* * merge @rule into @rset. - * if @expand != 0 then @rset slots might be expanded. + * the @rset slots might be expanded. */ int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset, - struct sptlrpc_rule *rule, - int expand) + struct sptlrpc_rule *rule) { - struct sptlrpc_rule *p = rset->srs_rules; - int spec_dir, spec_net; - int rc, n, match = 0; + struct sptlrpc_rule *p = rset->srs_rules; + int spec_dir, spec_net; + int rc, n, match = 0; - spec_net = rule_spec_net(rule); - spec_dir = rule_spec_dir(rule); + might_sleep(); + + spec_net = rule_spec_net(rule); + spec_dir = rule_spec_dir(rule); for (n = 0; n < rset->srs_nrule; n++) { p = &rset->srs_rules[n]; @@ -496,7 +378,7 @@ int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset, LASSERT(n >= 0 && n <= rset->srs_nrule); if (rule->sr_flvr.sf_rpc != SPTLRPC_FLVR_INVALID) { - rc = sptlrpc_rule_set_expand(rset, expand); + rc = sptlrpc_rule_set_expand(rset); if (rc) return rc; @@ -507,7 +389,7 @@ int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset, memcpy(&rset->srs_rules[n], rule, sizeof(*rule)); rset->srs_nrule++; } else { - CWARN("ignore the unmatched deletion\n"); + CDEBUG(D_CONFIG, "ignore the unmatched deletion\n"); } } @@ -515,35 +397,15 @@ int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset, } EXPORT_SYMBOL(sptlrpc_rule_set_merge); -int sptlrpc_rule_set_from_log(struct sptlrpc_rule_set *rset, - struct sptlrpc_conf_log *log) -{ - LASSERT(rset); - LASSERT(log); - - sptlrpc_rule_set_free(rset); - - if (log->scl_nrule == 0) - return 0; - - OBD_ALLOC(rset->srs_rules, log->scl_nrule * sizeof(*log->scl_rules)); - if (!rset->srs_rules) - return -ENOMEM; - - memcpy(rset->srs_rules, log->scl_rules, - log->scl_nrule * sizeof(*log->scl_rules)); - rset->srs_nslot = rset->srs_nrule = log->scl_nrule; - return 0; -} -EXPORT_SYMBOL(sptlrpc_rule_set_from_log); - -/* - * according to NID/from choose a flavor from rule set. +/** + * given from/to/nid, determine a matching flavor in ruleset. + * return 1 if a match found, otherwise return 0. */ -void sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset, - enum lustre_sec_part from, - lnet_nid_t nid, - struct sptlrpc_flavor *sf) +int sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset, + enum lustre_sec_part from, + enum lustre_sec_part to, + lnet_nid_t nid, + struct sptlrpc_flavor *sf) { struct sptlrpc_rule *r; int n; @@ -560,14 +422,16 @@ void sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset, from != r->sr_from) continue; + if (to != LUSTRE_SP_ANY && r->sr_to != LUSTRE_SP_ANY && + to != r->sr_to) + continue; + *sf = r->sr_flvr; - return; + return 1; } - /* no match found, set as default flavor */ - get_default_flavor(sf); + return 0; } -EXPORT_SYMBOL(sptlrpc_rule_set_choose); void sptlrpc_rule_set_dump(struct sptlrpc_rule_set *rset) { @@ -576,81 +440,27 @@ void sptlrpc_rule_set_dump(struct sptlrpc_rule_set *rset) for (n = 0; n < rset->srs_nrule; n++) { r = &rset->srs_rules[n]; - CWARN("<%02d> from %x to %x, net %x, rpc %x\n", n, - r->sr_from, r->sr_to, r->sr_netid, r->sr_flvr.sf_rpc); + CDEBUG(D_SEC, "<%02d> from %x to %x, net %x, rpc %x\n", n, + r->sr_from, r->sr_to, r->sr_netid, r->sr_flvr.sf_rpc); } } -EXPORT_SYMBOL(sptlrpc_rule_set_dump); -/**************************************** - * sptlrpc config log * - ****************************************/ - -struct sptlrpc_conf_log *sptlrpc_conf_log_alloc(void) -{ - struct sptlrpc_conf_log *log; - - OBD_ALLOC_PTR(log); - if (log == NULL) - return ERR_PTR(-ENOMEM); - - log->scl_max = SPTLRPC_CONF_LOG_MAX; - return log; -} -EXPORT_SYMBOL(sptlrpc_conf_log_alloc); - -void sptlrpc_conf_log_free(struct sptlrpc_conf_log *log) +static int sptlrpc_rule_set_extract(struct sptlrpc_rule_set *gen, + struct sptlrpc_rule_set *tgt, + enum lustre_sec_part from, + enum lustre_sec_part to, + struct sptlrpc_rule_set *rset) { - LASSERT(log->scl_max == SPTLRPC_CONF_LOG_MAX); - OBD_FREE_PTR(log); -} -EXPORT_SYMBOL(sptlrpc_conf_log_free); + struct sptlrpc_rule_set *src[2] = { gen, tgt }; + struct sptlrpc_rule *rule; + int i, n, rc; -static __u32 get_log_rule_flags(enum lustre_sec_part from, - enum lustre_sec_part to, - unsigned int fl_udesc) -{ - /* MDT->MDT; MDT->OST */ - if (from == LUSTRE_SP_MDT) - return PTLRPC_SEC_FL_ROOTONLY; - /* CLI->OST */ - if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_OST) - return PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_BULK; - /* CLI->MDT */ - if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_MDT) - if (fl_udesc) - return PTLRPC_SEC_FL_UDESC; + might_sleep(); - return 0; -} - -/* - * generate config log: merge general and target rules, which - * match @from @to - */ -int sptlrpc_conf_log_populate(struct sptlrpc_rule_set *gen, - struct sptlrpc_rule_set *tgt, - enum lustre_sec_part from, - enum lustre_sec_part to, - unsigned int fl_udesc, - struct sptlrpc_conf_log *log) -{ - struct sptlrpc_rule_set *src[2] = { gen, tgt }; - struct sptlrpc_rule_set dst; - struct sptlrpc_rule *rule; - __u32 flags; - int i, n, rc; - - LASSERT(log); - - dst.srs_nslot = log->scl_max; - dst.srs_nrule = 0; - dst.srs_rules = log->scl_rules; - - /* merge general rules firstly, then target-specific rules */ - for (i = 0; i < 2; i++) { - if (src[i] == NULL) - continue; + /* merge general rules firstly, then target-specific rules */ + for (i = 0; i < 2; i++) { + if (src[i] == NULL) + continue; for (n = 0; n < src[i]->srs_nrule; n++) { rule = &src[i]->srs_rules[n]; @@ -664,7 +474,7 @@ int sptlrpc_conf_log_populate(struct sptlrpc_rule_set *gen, rule->sr_to != to) continue; - rc = sptlrpc_rule_set_merge(&dst, rule, 0); + rc = sptlrpc_rule_set_merge(rset, rule); if (rc) { CERROR("can't merge: %d\n", rc); return rc; @@ -672,125 +482,743 @@ int sptlrpc_conf_log_populate(struct sptlrpc_rule_set *gen, } } - log->scl_nrule = dst.srs_nrule; + return 0; +} + +/********************************** + * sptlrpc configuration support * + **********************************/ + +struct sptlrpc_conf_tgt { + struct list_head sct_list; + char sct_name[MAX_OBD_NAME]; + struct sptlrpc_rule_set sct_rset; +}; + +struct sptlrpc_conf { + struct list_head sc_list; + char sc_fsname[MTI_NAME_MAXLEN]; + unsigned int sc_modified; /* modified during updating */ + unsigned int sc_updated:1, /* updated copy from MGS */ + sc_local:1; /* local copy from target */ + struct sptlrpc_rule_set sc_rset; /* fs general rules */ + struct list_head sc_tgts; /* target-specific rules */ +}; - /* set flags for each rule */ - flags = get_log_rule_flags(from, to, fl_udesc); +static struct mutex sptlrpc_conf_lock; +static struct list_head sptlrpc_confs; - for (i = 0; i < log->scl_nrule; i++) { - log->scl_rules[i].sr_flvr.sf_flags = flags; +static inline int is_hex(char c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f')); +} - /* also clear the from/to fields which don't need to be known - * accordingly. @from == ANY means this log is for target, - * otherwise for client. */ - if (from != LUSTRE_SP_ANY) - log->scl_rules[i].sr_from = LUSTRE_SP_ANY; - log->scl_rules[i].sr_to = LUSTRE_SP_ANY; +static void target2fsname(const char *tgt, char *fsname, int buflen) +{ + const char *ptr; + int len; + + ptr = strrchr(tgt, '-'); + if (ptr) { + if ((strncmp(ptr, "-MDT", 4) != 0 && + strncmp(ptr, "-OST", 4) != 0) || + !is_hex(ptr[4]) || !is_hex(ptr[5]) || + !is_hex(ptr[6]) || !is_hex(ptr[7])) + ptr = NULL; } - return 0; + /* if we didn't find the pattern, treat the whole string as fsname */ + if (ptr == NULL) + len = strlen(tgt); + else + len = ptr - tgt; + + len = min(len, buflen - 1); + memcpy(fsname, tgt, len); + fsname[len] = '\0'; } -EXPORT_SYMBOL(sptlrpc_conf_log_populate); -/* - * extract config log from @lcfg +static void sptlrpc_conf_free_rsets(struct sptlrpc_conf *conf) +{ + struct sptlrpc_conf_tgt *conf_tgt, *conf_tgt_next; + + sptlrpc_rule_set_free(&conf->sc_rset); + + list_for_each_entry_safe(conf_tgt, conf_tgt_next, + &conf->sc_tgts, sct_list) { + sptlrpc_rule_set_free(&conf_tgt->sct_rset); + list_del(&conf_tgt->sct_list); + OBD_FREE_PTR(conf_tgt); + } + LASSERT(list_empty(&conf->sc_tgts)); + + conf->sc_updated = 0; + conf->sc_local = 0; +} + +static void sptlrpc_conf_free(struct sptlrpc_conf *conf) +{ + CDEBUG(D_SEC, "free sptlrpc conf %s\n", conf->sc_fsname); + + sptlrpc_conf_free_rsets(conf); + list_del(&conf->sc_list); + OBD_FREE_PTR(conf); +} + +static +struct sptlrpc_conf_tgt *sptlrpc_conf_get_tgt(struct sptlrpc_conf *conf, + const char *name, + int create) +{ + struct sptlrpc_conf_tgt *conf_tgt; + + list_for_each_entry(conf_tgt, &conf->sc_tgts, sct_list) { + if (strcmp(conf_tgt->sct_name, name) == 0) + return conf_tgt; + } + + if (!create) + return NULL; + + OBD_ALLOC_PTR(conf_tgt); + if (conf_tgt) { + strlcpy(conf_tgt->sct_name, name, sizeof(conf_tgt->sct_name)); + sptlrpc_rule_set_init(&conf_tgt->sct_rset); + list_add(&conf_tgt->sct_list, &conf->sc_tgts); + } + + return conf_tgt; +} + +static +struct sptlrpc_conf *sptlrpc_conf_get(const char *fsname, + int create) +{ + struct sptlrpc_conf *conf; + + list_for_each_entry(conf, &sptlrpc_confs, sc_list) { + if (strcmp(conf->sc_fsname, fsname) == 0) + return conf; + } + + if (!create) + return NULL; + + OBD_ALLOC_PTR(conf); + if (conf == NULL) + return NULL; + + if (strlcpy(conf->sc_fsname, fsname, sizeof(conf->sc_fsname)) >= + sizeof(conf->sc_fsname)) { + OBD_FREE_PTR(conf); + return NULL; + } + sptlrpc_rule_set_init(&conf->sc_rset); + INIT_LIST_HEAD(&conf->sc_tgts); + list_add(&conf->sc_list, &sptlrpc_confs); + + CDEBUG(D_SEC, "create sptlrpc conf %s\n", conf->sc_fsname); + return conf; +} + +/** + * caller must hold conf_lock already. + */ +static int sptlrpc_conf_merge_rule(struct sptlrpc_conf *conf, + const char *target, + struct sptlrpc_rule *rule) +{ + struct sptlrpc_conf_tgt *conf_tgt; + struct sptlrpc_rule_set *rule_set; + + /* fsname == target means general rules for the whole fs */ + if (strcmp(conf->sc_fsname, target) == 0) { + rule_set = &conf->sc_rset; + } else { + conf_tgt = sptlrpc_conf_get_tgt(conf, target, 1); + if (conf_tgt) { + rule_set = &conf_tgt->sct_rset; + } else { + CERROR("out of memory, can't merge rule!\n"); + return -ENOMEM; + } + } + + return sptlrpc_rule_set_merge(rule_set, rule); +} + +/** + * process one LCFG_SPTLRPC_CONF record. if \a conf is NULL, we + * find one through the target name in the record inside conf_lock; + * otherwise means caller already hold conf_lock. */ -struct sptlrpc_conf_log *sptlrpc_conf_log_extract(struct lustre_cfg *lcfg) +static int __sptlrpc_process_config(struct lustre_cfg *lcfg, + struct sptlrpc_conf *conf) { - struct sptlrpc_conf_log *log; - struct sptlrpc_rule *r; - int i; + char *target, *param; + char fsname[MTI_NAME_MAXLEN]; + struct sptlrpc_rule rule; + int rc; ENTRY; - log = lustre_cfg_buf(lcfg, 1); - if (log == NULL) { - CERROR("no sptlrpc config data\n"); - RETURN(ERR_PTR(-EINVAL)); + target = lustre_cfg_string(lcfg, 1); + if (target == NULL) { + CERROR("missing target name\n"); + RETURN(-EINVAL); } - if (lcfg->lcfg_version == __swab32(LUSTRE_CFG_VERSION)) { - __swab32s(&log->scl_max); - __swab32s(&log->scl_nrule); + param = lustre_cfg_string(lcfg, 2); + if (param == NULL) { + CERROR("missing parameter\n"); + RETURN(-EINVAL); } - if (LUSTRE_CFG_BUFLEN(lcfg, 1) < - log->scl_max * sizeof(log->scl_rules[0])) { - CERROR("mal-formed config log\n"); - RETURN(ERR_PTR(-EINVAL)); + CDEBUG(D_SEC, "processing rule: %s.%s\n", target, param); + + /* parse rule to make sure the format is correct */ + if (strncmp(param, PARAM_SRPC_FLVR, sizeof(PARAM_SRPC_FLVR) - 1) != 0) { + CERROR("Invalid sptlrpc parameter: %s\n", param); + RETURN(-EINVAL); } + param += sizeof(PARAM_SRPC_FLVR) - 1; - if (lcfg->lcfg_version == __swab32(LUSTRE_CFG_VERSION)) { - for (i = 0; i < log->scl_nrule; i++) { - r = &log->scl_rules[i]; - __swab32s(&r->sr_netid); - __swab16s(&r->sr_flvr.sf_rpc); - __swab32s(&r->sr_flvr.sf_flags); + rc = sptlrpc_parse_rule(param, &rule); + if (rc) + RETURN(-EINVAL); + + if (conf == NULL) { + target2fsname(target, fsname, sizeof(fsname)); + + mutex_lock(&sptlrpc_conf_lock); + conf = sptlrpc_conf_get(fsname, 0); + if (conf == NULL) { + CERROR("can't find conf\n"); + rc = -ENOMEM; + } else { + rc = sptlrpc_conf_merge_rule(conf, target, &rule); } + mutex_unlock(&sptlrpc_conf_lock); + } else { + LASSERT(mutex_is_locked(&sptlrpc_conf_lock)); + rc = sptlrpc_conf_merge_rule(conf, target, &rule); } - RETURN(log); + if (rc == 0) + conf->sc_modified++; + + RETURN(rc); } -EXPORT_SYMBOL(sptlrpc_conf_log_extract); -void sptlrpc_conf_log_cleanup(struct sptlrpc_conf_log *log) +int sptlrpc_process_config(struct lustre_cfg *lcfg) { - log->scl_nrule = 0; - memset(log->scl_rules, 0, sizeof(log->scl_rules)); + return __sptlrpc_process_config(lcfg, NULL); } -EXPORT_SYMBOL(sptlrpc_conf_log_cleanup); +EXPORT_SYMBOL(sptlrpc_process_config); -void sptlrpc_conf_log_dump(struct sptlrpc_conf_log *log) +static int logname2fsname(const char *logname, char *buf, int buflen) { - struct sptlrpc_rule *r; - int n; + char *ptr; + int len; + + ptr = strrchr(logname, '-'); + if (ptr == NULL || strcmp(ptr, "-sptlrpc")) { + CERROR("%s is not a sptlrpc config log\n", logname); + return -EINVAL; + } - CWARN("max %u, rule# %u part %u\n", - log->scl_max, log->scl_nrule, log->scl_part); + len = min((int) (ptr - logname), buflen - 1); - for (n = 0; n < log->scl_nrule; n++) { - r = &log->scl_rules[n]; - CWARN("<%02d> %x -> %x, net %x, rpc %x\n", n, - r->sr_from, r->sr_to, r->sr_netid, r->sr_flvr.sf_rpc); + memcpy(buf, logname, len); + buf[len] = '\0'; + return 0; +} + +void sptlrpc_conf_log_update_begin(const char *logname) +{ + struct sptlrpc_conf *conf; + char fsname[16]; + + if (logname2fsname(logname, fsname, sizeof(fsname))) + return; + + mutex_lock(&sptlrpc_conf_lock); + + conf = sptlrpc_conf_get(fsname, 0); + if (conf) { + if (conf->sc_local) { + LASSERT(conf->sc_updated == 0); + sptlrpc_conf_free_rsets(conf); + } + conf->sc_modified = 0; + } + + mutex_unlock(&sptlrpc_conf_lock); +} +EXPORT_SYMBOL(sptlrpc_conf_log_update_begin); + +/** + * mark a config log has been updated + */ +void sptlrpc_conf_log_update_end(const char *logname) +{ + struct sptlrpc_conf *conf; + char fsname[16]; + + if (logname2fsname(logname, fsname, sizeof(fsname))) + return; + + mutex_lock(&sptlrpc_conf_lock); + + conf = sptlrpc_conf_get(fsname, 0); + if (conf) { + /* + * if original state is not updated, make sure the + * modified counter > 0 to enforce updating local copy. + */ + if (conf->sc_updated == 0) + conf->sc_modified++; + + conf->sc_updated = 1; } + + mutex_unlock(&sptlrpc_conf_lock); } -EXPORT_SYMBOL(sptlrpc_conf_log_dump); +EXPORT_SYMBOL(sptlrpc_conf_log_update_end); -/* - * caller should guarantee that no concurrent calls to this function +void sptlrpc_conf_log_start(const char *logname) +{ + char fsname[16]; + + if (logname2fsname(logname, fsname, sizeof(fsname))) + return; + + mutex_lock(&sptlrpc_conf_lock); + sptlrpc_conf_get(fsname, 1); + mutex_unlock(&sptlrpc_conf_lock); +} +EXPORT_SYMBOL(sptlrpc_conf_log_start); + +void sptlrpc_conf_log_stop(const char *logname) +{ + struct sptlrpc_conf *conf; + char fsname[16]; + + if (logname2fsname(logname, fsname, sizeof(fsname))) + return; + + mutex_lock(&sptlrpc_conf_lock); + conf = sptlrpc_conf_get(fsname, 0); + if (conf) + sptlrpc_conf_free(conf); + mutex_unlock(&sptlrpc_conf_lock); +} +EXPORT_SYMBOL(sptlrpc_conf_log_stop); + +static void inline flavor_set_flags(struct sptlrpc_flavor *sf, + enum lustre_sec_part from, + enum lustre_sec_part to, + unsigned int fl_udesc) +{ + /* + * null flavor doesn't need to set any flavor, and in fact + * we'd better not do that because everybody share a single sec. + */ + if (sf->sf_rpc == SPTLRPC_FLVR_NULL) + return; + + if (from == LUSTRE_SP_MDT) { + /* MDT->MDT; MDT->OST */ + sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY; + } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_OST) { + /* CLI->OST */ + sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_BULK; + } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_MDT) { + /* CLI->MDT */ + if (fl_udesc && sf->sf_rpc != SPTLRPC_FLVR_NULL) + sf->sf_flags |= PTLRPC_SEC_FL_UDESC; + } +} + +void sptlrpc_conf_choose_flavor(enum lustre_sec_part from, + enum lustre_sec_part to, + struct obd_uuid *target, + lnet_nid_t nid, + struct sptlrpc_flavor *sf) +{ + struct sptlrpc_conf *conf; + struct sptlrpc_conf_tgt *conf_tgt; + char name[MTI_NAME_MAXLEN]; + int len, rc = 0; + + target2fsname(target->uuid, name, sizeof(name)); + + mutex_lock(&sptlrpc_conf_lock); + + conf = sptlrpc_conf_get(name, 0); + if (conf == NULL) + goto out; + + /* convert uuid name (supposed end with _UUID) to target name */ + len = strlen(target->uuid); + LASSERT(len > 5); + memcpy(name, target->uuid, len - 5); + name[len - 5] = '\0'; + + conf_tgt = sptlrpc_conf_get_tgt(conf, name, 0); + if (conf_tgt) { + rc = sptlrpc_rule_set_choose(&conf_tgt->sct_rset, + from, to, nid, sf); + if (rc) + goto out; + } + + rc = sptlrpc_rule_set_choose(&conf->sc_rset, from, to, nid, sf); +out: + mutex_unlock(&sptlrpc_conf_lock); + + if (rc == 0) + get_default_flavor(sf); + + flavor_set_flags(sf, from, to, 1); +} + +/** + * called by target devices, determine the expected flavor from + * certain peer (from, nid). */ +void sptlrpc_target_choose_flavor(struct sptlrpc_rule_set *rset, + enum lustre_sec_part from, + lnet_nid_t nid, + struct sptlrpc_flavor *sf) +{ + if (sptlrpc_rule_set_choose(rset, from, LUSTRE_SP_ANY, nid, sf) == 0) + get_default_flavor(sf); +} + #define SEC_ADAPT_DELAY (10) -int sptlrpc_cliobd_process_config(struct obd_device *obd, - struct lustre_cfg *lcfg) +/** + * called by client devices, notify the sptlrpc config has changed and + * do import_sec_adapt later. + */ +void sptlrpc_conf_client_adapt(struct obd_device *obd) +{ + struct obd_import *imp; + ENTRY; + + LASSERT(strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 || + strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) ==0); + CDEBUG(D_SEC, "obd %s\n", obd->u.cli.cl_target_uuid.uuid); + + /* serialize with connect/disconnect import */ + down_read(&obd->u.cli.cl_sem); + + imp = obd->u.cli.cl_import; + if (imp) { + spin_lock(&imp->imp_lock); + if (imp->imp_sec) + imp->imp_sec_expire = cfs_time_current_sec() + + SEC_ADAPT_DELAY; + spin_unlock(&imp->imp_lock); + } + + up_read(&obd->u.cli.cl_sem); + EXIT; +} +EXPORT_SYMBOL(sptlrpc_conf_client_adapt); + + +static void rule2string(struct sptlrpc_rule *r, char *buf, int buflen) { - struct sptlrpc_conf_log *log; - struct obd_import *imp; - int rc; + char dirbuf[8]; + char net[LNET_NIDSTR_SIZE] = "default"; + char *ptr = buf; + + if (r->sr_netid != LNET_NIDNET(LNET_NID_ANY)) + libcfs_net2str_r(r->sr_netid, net, sizeof(net)); + + if (r->sr_from == LUSTRE_SP_ANY && r->sr_to == LUSTRE_SP_ANY) + dirbuf[0] = '\0'; + else + snprintf(dirbuf, sizeof(dirbuf), ".%s2%s", + sptlrpc_part2name(r->sr_from), + sptlrpc_part2name(r->sr_to)); - log = sptlrpc_conf_log_extract(lcfg); - if (IS_ERR(log)) { - CERROR("extract log error: %ld\n", PTR_ERR(log)); - return PTR_ERR(log); + ptr += snprintf(buf, buflen, "srpc.flavor.%s%s=", net, dirbuf); + + sptlrpc_flavor2name(&r->sr_flvr, ptr, buflen - (ptr - buf)); + buf[buflen - 1] = '\0'; +} + +static int sptlrpc_record_rule_set(struct llog_handle *llh, + char *target, + struct sptlrpc_rule_set *rset) +{ + struct llog_cfg_rec *lcr; + struct lustre_cfg_bufs bufs; + char param[48]; + int i, rc; + + for (i = 0; i < rset->srs_nrule; i++) { + rule2string(&rset->srs_rules[i], param, sizeof(param)); + + lustre_cfg_bufs_reset(&bufs, NULL); + lustre_cfg_bufs_set_string(&bufs, 1, target); + lustre_cfg_bufs_set_string(&bufs, 2, param); + lcr = lustre_cfg_rec_new(LCFG_SPTLRPC_CONF, &bufs); + if (lcr == NULL) + return -ENOMEM; + rc = llog_write(NULL, llh, &lcr->lcr_hdr, LLOG_NEXT_IDX); + lustre_cfg_rec_free(lcr); + if (rc) + return rc; + } + return 0; +} + +static int sptlrpc_record_rules(struct llog_handle *llh, + struct sptlrpc_conf *conf) +{ + struct sptlrpc_conf_tgt *conf_tgt; + + sptlrpc_record_rule_set(llh, conf->sc_fsname, &conf->sc_rset); + + list_for_each_entry(conf_tgt, &conf->sc_tgts, sct_list) { + sptlrpc_record_rule_set(llh, conf_tgt->sct_name, + &conf_tgt->sct_rset); } + return 0; +} - obd->u.cli.cl_sec_part = log->scl_part; +#define LOG_SPTLRPC_TMP "sptlrpc.tmp" +#define LOG_SPTLRPC "sptlrpc" + +static +int sptlrpc_target_local_copy_conf(struct obd_device *obd, + struct sptlrpc_conf *conf) +{ + struct llog_handle *llh = NULL; + struct llog_ctxt *ctxt; + struct lvfs_run_ctxt saved; + struct dentry *dentry; + int rc; + ENTRY; + + ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); + if (ctxt == NULL) + RETURN(-EINVAL); + + push_ctxt(&saved, &obd->obd_lvfs_ctxt); + + dentry = ll_lookup_one_len(MOUNT_CONFIGS_DIR, current->fs->pwd.dentry, + strlen(MOUNT_CONFIGS_DIR)); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); + CERROR("cannot lookup %s directory: rc = %d\n", + MOUNT_CONFIGS_DIR, rc); + GOTO(out_ctx, rc); + } + + /* erase the old tmp log */ + rc = llog_erase(NULL, ctxt, NULL, LOG_SPTLRPC_TMP); + if (rc < 0 && rc != -ENOENT) { + CERROR("%s: cannot erase temporary sptlrpc log: rc = %d\n", + obd->obd_name, rc); + GOTO(out_dput, rc); + } + + /* write temporary log */ + rc = llog_open_create(NULL, ctxt, &llh, NULL, LOG_SPTLRPC_TMP); + if (rc) + GOTO(out_dput, rc); + rc = llog_init_handle(NULL, llh, LLOG_F_IS_PLAIN, NULL); + if (rc) + GOTO(out_close, rc); + + rc = sptlrpc_record_rules(llh, conf); + +out_close: + llog_close(NULL, llh); + if (rc == 0) + rc = lustre_rename(dentry, obd->obd_lvfs_ctxt.pwdmnt, + LOG_SPTLRPC_TMP, LOG_SPTLRPC); +out_dput: + dput(dentry); +out_ctx: + pop_ctxt(&saved, &obd->obd_lvfs_ctxt); + llog_ctxt_put(ctxt); + CDEBUG(D_SEC, "target %s: write local sptlrpc conf: rc = %d\n", + obd->obd_name, rc); + RETURN(rc); +} - rc = sptlrpc_rule_set_from_log(&obd->u.cli.cl_sptlrpc_rset, log); +static int local_read_handler(const struct lu_env *env, + struct llog_handle *llh, + struct llog_rec_hdr *rec, void *data) +{ + struct sptlrpc_conf *conf = (struct sptlrpc_conf *) data; + struct lustre_cfg *lcfg = (struct lustre_cfg *)(rec + 1); + int cfg_len, rc; + ENTRY; + + if (rec->lrh_type != OBD_CFG_REC) { + CERROR("unhandled lrh_type: %#x\n", rec->lrh_type); + RETURN(-EINVAL); + } + + cfg_len = rec->lrh_len - sizeof(struct llog_rec_hdr) - + sizeof(struct llog_rec_tail); + + rc = lustre_cfg_sanity_check(lcfg, cfg_len); if (rc) { - CERROR("failed create rule set: %d\n", rc); - return rc; + CERROR("Insane cfg\n"); + RETURN(rc); } - imp = obd->u.cli.cl_import; - if (imp == NULL) - return 0; + if (lcfg->lcfg_command != LCFG_SPTLRPC_CONF) { + CERROR("invalid command (%x)\n", lcfg->lcfg_command); + RETURN(-EINVAL); + } - /* even if imp_sec_expire is already set, we'll override it to a - * newer (later) time */ - spin_lock(&imp->imp_lock); - if (imp->imp_sec) - imp->imp_sec_expire = cfs_time_current_sec() + SEC_ADAPT_DELAY; - spin_unlock(&imp->imp_lock); - return 0; + RETURN(__sptlrpc_process_config(lcfg, conf)); +} + +static +int sptlrpc_target_local_read_conf(struct obd_device *obd, + struct sptlrpc_conf *conf) +{ + struct llog_handle *llh = NULL; + struct llog_ctxt *ctxt; + struct lvfs_run_ctxt saved; + int rc; + ENTRY; + + LASSERT(conf->sc_updated == 0 && conf->sc_local == 0); + + ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); + if (ctxt == NULL) { + CERROR("missing llog context\n"); + RETURN(-EINVAL); + } + + push_ctxt(&saved, &obd->obd_lvfs_ctxt); + + rc = llog_open(NULL, ctxt, &llh, NULL, LOG_SPTLRPC, LLOG_OPEN_EXISTS); + if (rc < 0) { + if (rc == -ENOENT) + rc = 0; + GOTO(out_pop, rc); + } + + rc = llog_init_handle(NULL, llh, LLOG_F_IS_PLAIN, NULL); + if (rc) + GOTO(out_close, rc); + + if (llog_get_size(llh) <= 1) { + CDEBUG(D_SEC, "no local sptlrpc copy found\n"); + GOTO(out_close, rc = 0); + } + + rc = llog_process(NULL, llh, local_read_handler, (void *)conf, NULL); + + if (rc == 0) { + conf->sc_local = 1; + } else { + sptlrpc_conf_free_rsets(conf); + } + +out_close: + llog_close(NULL, llh); +out_pop: + pop_ctxt(&saved, &obd->obd_lvfs_ctxt); + llog_ctxt_put(ctxt); + CDEBUG(D_SEC, "target %s: read local sptlrpc conf: rc = %d\n", + obd->obd_name, rc); + RETURN(rc); +} + + +/** + * called by target devices, extract sptlrpc rules which applies to + * this target, to be used for future rpc flavor checking. + */ +int sptlrpc_conf_target_get_rules(struct obd_device *obd, + struct sptlrpc_rule_set *rset, + int initial) +{ + struct sptlrpc_conf *conf; + struct sptlrpc_conf_tgt *conf_tgt; + enum lustre_sec_part sp_dst; + char fsname[MTI_NAME_MAXLEN]; + int rc = 0; + ENTRY; + + if (strcmp(obd->obd_type->typ_name, LUSTRE_MDT_NAME) == 0) { + sp_dst = LUSTRE_SP_MDT; + } else if (strcmp(obd->obd_type->typ_name, LUSTRE_OST_NAME) == 0) { + sp_dst = LUSTRE_SP_OST; + } else { + CERROR("unexpected obd type %s\n", obd->obd_type->typ_name); + RETURN(-EINVAL); + } + CDEBUG(D_SEC, "get rules for target %s\n", obd->obd_uuid.uuid); + + target2fsname(obd->obd_uuid.uuid, fsname, sizeof(fsname)); + + mutex_lock(&sptlrpc_conf_lock); + + conf = sptlrpc_conf_get(fsname, 0); + if (conf == NULL) { + CERROR("missing sptlrpc config log\n"); + GOTO(out, rc); + } + + if (conf->sc_updated == 0) { + /* + * always read from local copy. here another option is + * if we already have a local copy (read from another + * target device hosted on the same node) we simply use that. + */ + if (conf->sc_local) + sptlrpc_conf_free_rsets(conf); + + sptlrpc_target_local_read_conf(obd, conf); + } else { + LASSERT(conf->sc_local == 0); + + /* write a local copy */ + if (initial || conf->sc_modified) + sptlrpc_target_local_copy_conf(obd, conf); + else + CDEBUG(D_SEC, "unchanged, skip updating local copy\n"); + } + + /* extract rule set for this target */ + conf_tgt = sptlrpc_conf_get_tgt(conf, obd->obd_name, 0); + + rc = sptlrpc_rule_set_extract(&conf->sc_rset, + conf_tgt ? &conf_tgt->sct_rset: NULL, + LUSTRE_SP_ANY, sp_dst, rset); +out: + mutex_unlock(&sptlrpc_conf_lock); + RETURN(rc); +} + +int sptlrpc_conf_init(void) +{ + INIT_LIST_HEAD(&sptlrpc_confs); + mutex_init(&sptlrpc_conf_lock); + return 0; +} + +void sptlrpc_conf_fini(void) +{ + struct sptlrpc_conf *conf, *conf_next; + + mutex_lock(&sptlrpc_conf_lock); + list_for_each_entry_safe(conf, conf_next, &sptlrpc_confs, sc_list) + sptlrpc_conf_free(conf); + LASSERT(list_empty(&sptlrpc_confs)); + mutex_unlock(&sptlrpc_conf_lock); } -EXPORT_SYMBOL(sptlrpc_cliobd_process_config);