* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2016, Intel Corporation.
+ * Copyright (c) 2011, 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
#define DEBUG_SUBSYSTEM S_CLASS
+#include <linux/kobject.h>
#include <linux/string.h>
#include <llog_swab.h>
}
obd = class_newdev(typename, name, uuid);
- if (IS_ERR(obd)) {
- /* Already exists or out of obds */
- rc = PTR_ERR(obd);
+ if (IS_ERR(obd)) { /* Already exists or out of obds */
+ rc = PTR_ERR(obd);
CERROR("Cannot create device %s of type %s : %d\n",
name, typename, rc);
RETURN(rc);
}
- LASSERTF(obd != NULL, "Cannot get obd device %s of type %s\n",
- name, typename);
LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC,
"obd %p obd_magic %08X != %08X\n",
obd, obd->obd_magic, OBD_DEVICE_MAGIC);
exp = class_new_export_self(obd, &obd->obd_uuid);
if (IS_ERR(exp)) {
- /* force free */
- GOTO(out, rc = PTR_ERR(exp));
- RETURN(PTR_ERR(exp));
+ rc = PTR_ERR(exp);
+ class_free_dev(obd);
+ RETURN(rc);
}
obd->obd_self_export = exp;
class_export_put(exp);
rc = class_register_device(obd);
- if (rc != 0)
- GOTO(out, rc);
+ if (rc != 0) {
+ class_decref(obd, "newdev", obd);
+ RETURN(rc);
+ }
obd->obd_attached = 1;
CDEBUG(D_IOCTL, "OBD: dev %d attached type %s with refcount %d\n",
obd->obd_minor, typename, atomic_read(&obd->obd_refcount));
- RETURN(0);
-out:
- class_decref(obd, "newdev", obd);
- class_free_dev(obd);
- RETURN(rc);
+ RETURN(0);
}
EXPORT_SYMBOL(class_attach);
if (exp) {
exp->exp_flags |= exp_flags_from_obd(obd);
- /*
- * note that we'll recurse into class_decref again
- * but it's not a problem because we was last user
- */
class_unlink_export(exp);
}
}
}
EXPORT_SYMBOL(class_del_profiles);
-static int class_set_global(char *ptr, int val, struct lustre_cfg *lcfg)
-{
- ENTRY;
- if (class_match_param(ptr, PARAM_AT_MIN, NULL) == 0)
- at_min = val;
- else if (class_match_param(ptr, PARAM_AT_MAX, NULL) == 0)
- at_max = val;
- else if (class_match_param(ptr, PARAM_AT_EXTRA, NULL) == 0)
- at_extra = val;
- else if (class_match_param(ptr, PARAM_AT_EARLY_MARGIN, NULL) == 0)
- at_early_margin = val;
- else if (class_match_param(ptr, PARAM_AT_HISTORY, NULL) == 0)
- at_history = val;
- else if (class_match_param(ptr, PARAM_JOBID_VAR, NULL) == 0)
- strlcpy(obd_jobid_var, lustre_cfg_string(lcfg, 2),
- JOBSTATS_JOBID_VAR_MAX_LEN + 1);
- else
- RETURN(-EINVAL);
-
- CDEBUG(D_IOCTL, "global %s = %d\n", ptr, val);
- RETURN(0);
-}
-
-
-/* We can't call ll_process_config or lquota_process_config directly because
- * it lives in a module that must be loaded after this one. */
-static int (*client_process_config)(struct lustre_cfg *lcfg) = NULL;
+/* We can't call lquota_process_config directly because
+ * it lives in a module that must be loaded after this one.
+ */
+#ifdef HAVE_SERVER_SUPPORT
static int (*quota_process_config)(struct lustre_cfg *lcfg) = NULL;
-
-void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg))
-{
- client_process_config = cpc;
-}
-EXPORT_SYMBOL(lustre_register_client_process_config);
+#endif /* HAVE_SERVER_SUPPORT */
/**
* Rename the proc parameter in \a cfg with a new name \a new_name.
}
EXPORT_SYMBOL(lustre_cfg_rename);
-static int process_param2_config(struct lustre_cfg *lcfg)
+static ssize_t process_param2_config(struct lustre_cfg *lcfg)
{
char *param = lustre_cfg_string(lcfg, 1);
char *upcall = lustre_cfg_string(lcfg, 2);
+ struct kobject *kobj = NULL;
+ const char *subsys = param;
char *argv[] = {
[0] = "/usr/sbin/lctl",
[1] = "set_param",
};
ktime_t start;
ktime_t end;
- int rc;
+ size_t len;
+ int rc;
+
ENTRY;
+ print_lustre_cfg(lcfg);
+
+ len = strcspn(param, ".=");
+ if (!len)
+ return -EINVAL;
+
+ /* If we find '=' then its the top level sysfs directory */
+ if (param[len] == '=')
+ return class_set_global(param);
+
+ subsys = kstrndup(param, len, GFP_KERNEL);
+ if (!subsys)
+ return -ENOMEM;
+
+ kobj = kset_find_obj(lustre_kset, subsys);
+ kfree(subsys);
+ if (kobj) {
+ char *value = param;
+ char *envp[3];
+ int i;
+
+ param = strsep(&value, "=");
+ envp[0] = kasprintf(GFP_KERNEL, "PARAM=%s", param);
+ envp[1] = kasprintf(GFP_KERNEL, "SETTING=%s", value);
+ envp[2] = NULL;
+
+ rc = kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
+ for (i = 0; i < ARRAY_SIZE(envp); i++)
+ kfree(envp[i]);
+
+ kobject_put(kobj);
+
+ RETURN(rc);
+ }
/* Add upcall processing here. Now only lctl is supported */
if (strcmp(upcall, LCTL_UPCALL) != 0) {
RETURN(rc);
}
+#ifdef HAVE_SERVER_SUPPORT
void lustre_register_quota_process_config(int (*qpc)(struct lustre_cfg *lcfg))
{
quota_process_config = qpc;
}
EXPORT_SYMBOL(lustre_register_quota_process_config);
+#endif /* HAVE_SERVER_SUPPORT */
/** Process configuration commands given in lustre_cfg form.
* These may come from direct calls (e.g. class_manual_cleanup)
}
case LCFG_PARAM: {
char *tmp;
+
/* llite has no obd */
- if ((class_match_param(lustre_cfg_string(lcfg, 1),
- PARAM_LLITE, NULL) == 0) &&
- client_process_config) {
- err = (*client_process_config)(lcfg);
- GOTO(out, err);
+ if (class_match_param(lustre_cfg_string(lcfg, 1),
+ PARAM_LLITE, NULL) == 0) {
+ struct lustre_sb_info *lsi;
+ unsigned long addr;
+ ssize_t count;
+
+ /* The instance name contains the sb:
+ * lustre-client-aacfe000
+ */
+ tmp = strrchr(lustre_cfg_string(lcfg, 0), '-');
+ if (!tmp || !*(++tmp))
+ GOTO(out, err = -EINVAL);
+
+ if (sscanf(tmp, "%lx", &addr) != 1)
+ GOTO(out, err = -EINVAL);
+
+ lsi = s2lsi((struct super_block *)addr);
+ /* This better be a real Lustre superblock! */
+ LASSERT(lsi->lsi_lmd->lmd_magic == LMD_MAGIC);
+
+ count = class_modify_config(lcfg, PARAM_LLITE,
+ lsi->lsi_kobj);
+ err = count < 0 ? count : 0;
+ GOTO(out, err);
} else if ((class_match_param(lustre_cfg_string(lcfg, 1),
PARAM_SYS, &tmp) == 0)) {
/* Global param settings */
- err = class_set_global(tmp, lcfg->lcfg_num, lcfg);
+ err = class_set_global(tmp);
/*
* Client or server should not fail to mount if
* it hits an unknown configuration parameter.
*/
- if (err != 0)
+ if (err < 0)
CWARN("Ignoring unknown param %s\n", tmp);
GOTO(out, err = 0);
+#ifdef HAVE_SERVER_SUPPORT
} else if ((class_match_param(lustre_cfg_string(lcfg, 1),
PARAM_QUOTA, &tmp) == 0) &&
quota_process_config) {
err = (*quota_process_config)(lcfg);
GOTO(out, err);
+#endif /* HAVE_SERVER_SUPPORT */
}
break;
GOTO(out, err = -EINVAL);
}
-
switch(lcfg->lcfg_command) {
case LCFG_SETUP: {
err = class_setup(obd, lcfg);
err = obd_pool_del(obd, lustre_cfg_string(lcfg, 2));
GOTO(out, err = 0);
}
- default: {
- err = obd_process_config(obd, sizeof(*lcfg), lcfg);
- GOTO(out, err);
+ /* Process config log ADD_MDC record twice to add MDC also to LOV
+ * for Data-on-MDT:
+ *
+ * add 0:lustre-clilmv 1:lustre-MDT0000_UUID 2:0 3:1
+ * 4:lustre-MDT0000-mdc_UUID
+ */
+ case LCFG_ADD_MDC: {
+ struct obd_device *lov_obd;
+ char *clilmv;
+
+ err = obd_process_config(obd, sizeof(*lcfg), lcfg);
+ if (err)
+ GOTO(out, err);
+
+ /* make sure this is client LMV log entry */
+ clilmv = strstr(lustre_cfg_string(lcfg, 0), "clilmv");
+ if (!clilmv)
+ GOTO(out, err);
+
+ /* replace 'lmv' with 'lov' name to address LOV device and
+ * process llog record to add MDC there. */
+ clilmv[4] = 'o';
+ lov_obd = class_name2obd(lustre_cfg_string(lcfg, 0));
+ if (lov_obd == NULL) {
+ err = -ENOENT;
+ CERROR("%s: Cannot find LOV by %s name, rc = %d\n",
+ obd->obd_name, lustre_cfg_string(lcfg, 0), err);
+ } else {
+ err = obd_process_config(lov_obd, sizeof(*lcfg), lcfg);
+ }
+ /* restore 'lmv' name */
+ clilmv[4] = 'm';
+ GOTO(out, err);
+ }
+ default: {
+ err = obd_process_config(obd, sizeof(*lcfg), lcfg);
+ GOTO(out, err);
}
}
+ EXIT;
out:
if ((err < 0) && !(lcfg->lcfg_command & LCFG_REQUIRED)) {
CWARN("Ignoring error %d on optional command %#x\n", err,
}
EXPORT_SYMBOL(class_process_config);
+ssize_t class_modify_config(struct lustre_cfg *lcfg, const char *prefix,
+ struct kobject *kobj)
+{
+ struct kobj_type *typ;
+ ssize_t count = 0;
+ int i;
+
+ if (lcfg->lcfg_command != LCFG_PARAM) {
+ CERROR("Unknown command: %d\n", lcfg->lcfg_command);
+ return -EINVAL;
+ }
+
+ typ = get_ktype(kobj);
+ if (!typ || !typ->default_attrs)
+ return -ENODEV;
+
+ print_lustre_cfg(lcfg);
+
+ /*
+ * e.g. tunefs.lustre --param mdt.group_upcall=foo /r/tmp/lustre-mdt
+ * or lctl conf_param lustre-MDT0000.mdt.group_upcall=bar
+ * or lctl conf_param lustre-OST0000.osc.max_dirty_mb=36
+ */
+ for (i = 1; i < lcfg->lcfg_bufcount; i++) {
+ struct attribute *attr;
+ size_t keylen;
+ char *value;
+ char *key;
+ int j;
+
+ key = lustre_cfg_buf(lcfg, i);
+ /* Strip off prefix */
+ if (class_match_param(key, prefix, &key))
+ /* If the prefix doesn't match, return error so we
+ * can pass it down the stack
+ */
+ return -EINVAL;
+
+ value = strchr(key, '=');
+ if (!value || *(value + 1) == 0) {
+ CERROR("%s: can't parse param '%s' (missing '=')\n",
+ lustre_cfg_string(lcfg, 0),
+ lustre_cfg_string(lcfg, i));
+ /* continue parsing other params */
+ continue;
+ }
+ keylen = value - key;
+ value++;
+
+ attr = NULL;
+ for (j = 0; typ->default_attrs[j]; j++) {
+ if (!strncmp(typ->default_attrs[j]->name, key,
+ keylen)) {
+ attr = typ->default_attrs[j];
+ break;
+ }
+ }
+
+ if (!attr) {
+ char *envp[3];
+
+ envp[0] = kasprintf(GFP_KERNEL, "PARAM=%s.%s.%.*s",
+ kobject_name(kobj->parent),
+ kobject_name(kobj),
+ (int) keylen, key);
+ envp[1] = kasprintf(GFP_KERNEL, "SETTING=%s", value);
+ envp[2] = NULL;
+
+ if (kobject_uevent_env(kobj, KOBJ_CHANGE, envp)) {
+ CERROR("%s: failed to send uevent %s\n",
+ kobject_name(kobj), key);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(envp); i++)
+ kfree(envp[i]);
+ } else {
+ count += lustre_attr_store(kobj, attr, value,
+ strlen(value));
+ }
+ }
+ return count;
+}
+EXPORT_SYMBOL(class_modify_config);
+
int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
struct lustre_cfg *lcfg, void *data)
{
/* rc = -EINVAL; continue parsing other params */
skip++;
} else if (rc < 0) {
- CERROR("%s: error writing proc '%s'='%s': rc = %d\n",
- lustre_cfg_string(lcfg, 0), key, sval, rc);
+ CERROR("%s: error writing parameter '%s': rc = %d\n",
+ lustre_cfg_string(lcfg, 0), key, rc);
rc = 0;
} else {
- CDEBUG(D_CONFIG, "%s: Set parameter '%s'='%s'\n",
- lustre_cfg_string(lcfg, 0), key, sval);
+ CDEBUG(D_CONFIG, "%s: set parameter '%s'\n",
+ lustre_cfg_string(lcfg, 0), key);
}
}
}
}
/* A config command without a start marker before it is
- illegal (post 146) */
- if (!(cfg->cfg_flags & CFG_F_COMPAT146) &&
- !(cfg->cfg_flags & CFG_F_MARKER) &&
+ * illegal
+ */
+ if (!(cfg->cfg_flags & CFG_F_MARKER) &&
(lcfg->lcfg_command != LCFG_MARKER)) {
CWARN("Config not inside markers, ignoring! "
"(inst: %p, uuid: %s, flags: %#x)\n",
lustre_cfg_bufs_init(&bufs, lcfg);
if (cfg->cfg_instance &&
+ lcfg->lcfg_command != LCFG_SPTLRPC_CONF &&
LUSTRE_CFG_BUFLEN(lcfg, 0) > 0) {
inst_len = LUSTRE_CFG_BUFLEN(lcfg, 0) +
sizeof(cfg->cfg_instance) * 2 + 4;
* moving them to index [1] and [2], and insert MGC's
* obdname at index [0].
*/
- if (cfg->cfg_instance == NULL &&
+ if (cfg->cfg_instance &&
lcfg->lcfg_command == LCFG_SPTLRPC_CONF) {
+ struct obd_device *obd = cfg->cfg_instance;
+
lustre_cfg_bufs_set(&bufs, 2, bufs.lcfg_buf[1],
bufs.lcfg_buflen[1]);
lustre_cfg_bufs_set(&bufs, 1, bufs.lcfg_buf[0],
bufs.lcfg_buflen[0]);
lustre_cfg_bufs_set_string(&bufs, 0,
- cfg->cfg_obdname);
+ obd->obd_name);
}
/* Add net info to setup command
{ LCFG_DEL_CONN, "del_conn", { "1", "2", "3", "4" } },
{ LCFG_LOV_ADD_OBD, "add_osc", { "ost", "index", "gen", "UUID" } },
{ LCFG_LOV_DEL_OBD, "del_osc", { "1", "2", "3", "4" } },
- { LCFG_PARAM, "set_param", { "parameter", "value", "3", "4" } },
+ { LCFG_PARAM, "conf_param", { "parameter", "value", "3", "4" } },
{ LCFG_MARKER, "marker", { "1", "2", "3", "4" } },
{ LCFG_LOG_START, "log_start", { "1", "2", "3", "4" } },
{ LCFG_LOG_END, "log_end", { "1", "2", "3", "4" } },
{ LCFG_POOL_DEL, "del_pool", { "fsname", "pool", "3", "4" } },
{ LCFG_SET_LDLM_TIMEOUT, "set_ldlm_timeout",
{ "parameter", "2", "3", "4" } },
+ { LCFG_SET_PARAM, "set_param", { "parameter", "value", "3", "4" } },
{ 0, NULL, { NULL, NULL, NULL, NULL } }
};
ptr += snprintf(ptr, end - ptr, ", device: %s",
lustre_cfg_string(lcfg, 0));
+ if (lcfg->lcfg_command == LCFG_SET_PARAM) {
+ /*
+ * set_param -P parameters have param=val here, separate
+ * them through pointer magic and print them out in
+ * native yamlese
+ */
+ char *cfg_str = lustre_cfg_string(lcfg, 1);
+ char *tmp = strchr(cfg_str, '=');
+ size_t len;
+
+ if (tmp == NULL)
+ return -ENOTTY;
+
+ ptr += snprintf(ptr, end - ptr, ", %s: ", ldata->ltd_bufs[0]);
+ len = tmp - cfg_str + 1;
+ snprintf(ptr, len, "%s", cfg_str);
+ ptr += len - 1;
+
+ ptr += snprintf(ptr, end - ptr, ", %s: ", ldata->ltd_bufs[1]);
+ ptr += snprintf(ptr, end - ptr, "%s", tmp + 1);
+
+ goto out_done;
+ }
+
for (i = 1; i < lcfg->lcfg_bufcount; i++) {
if (LUSTRE_CFG_BUFLEN(lcfg, i) > 0)
ptr += snprintf(ptr, end - ptr, ", %s: %s",
lustre_cfg_string(lcfg, i));
}
+out_done:
ptr += snprintf(ptr, end - ptr, " }\n");
/* return consumed bytes */
rc = ptr - buf;