Whamcloud - gitweb
LU-9431 obd: resolve config log sysfs issues 43/30143/8
authorJames Simmons <uja.ornl@yahoo.com>
Tue, 6 Feb 2018 16:43:07 +0000 (11:43 -0500)
committerOleg Drokin <oleg.drokin@intel.com>
Sat, 3 Mar 2018 04:28:31 +0000 (04:28 +0000)
This resolves long standing issues with modifying sysfs settings
on multiple nodes simultaneously by running a single command on
the backend MGS server. Their are two ways to change the settings,
LCFG_PARAM and LCFG_SET_PARAM. For the LCFG_PARAM case we create
a new function class_modify_config() that grabs the attributes
from the passed in kobject. We can use those attributes to
modify the sysfs settings. If we can't find the attribute then
send a uevent to let userland resolve the change. For the
LCFG_SET_PARAM case we handle two class of settings. The function
class_set_global() was modifiy to handle the top lustre sysfs
files since they are not searchable with kset_find_obj.
To make the new version of class_set_global() work both sets of
sysfs attributes for the top level sysfs entries have been merged.
If we can find a kobject with kset_find_obj then we can send
a uevent so userland change manage the change.

Change-Id: I4e7f19c4a232767119355c3c96e5752a10000da8
Signed-off-by: James Simmons <uja.ornl@yahoo.com>
Reviewed-on: https://review.whamcloud.com/30143
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Dmitry Eremin <dmitry.eremin@intel.com>
Reviewed-by: Ben Evans <bevans@cray.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/conf/99-lustre.rules
lustre/include/obd_class.h
lustre/obdclass/Makefile.in
lustre/obdclass/class_obd.c
lustre/obdclass/linux/Makefile.am
lustre/obdclass/linux/linux-module.c
lustre/obdclass/linux/linux-sysctl.c [deleted file]
lustre/obdclass/obd_config.c

index 7a0c6c7..acd5150 100644 (file)
@@ -1 +1,3 @@
 KERNEL=="obd", MODE="0666"
 KERNEL=="obd", MODE="0666"
+# set sysfs values on client
+SUBSYSTEM=="lustre", ACTION=="change", ENV{PARAM}=="?*", RUN+="/usr/sbin/lctl set_param $env{PARAM}=$env{SETTING}"
index 9a93448..9eb9ceb 100644 (file)
@@ -140,6 +140,9 @@ struct lustre_cfg *lustre_cfg_rename(struct lustre_cfg *cfg,
                                     const char *new_name);
 void print_lustre_cfg(struct lustre_cfg *lcfg);
 int class_process_config(struct lustre_cfg *lcfg);
                                     const char *new_name);
 void print_lustre_cfg(struct lustre_cfg *lcfg);
 int class_process_config(struct lustre_cfg *lcfg);
+ssize_t class_set_global(const char *param);
+ssize_t class_modify_config(struct lustre_cfg *lcfg, const char *prefix,
+                           struct kobject *kobj);
 int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
                             struct lustre_cfg *lcfg, void *data);
 int class_attach(struct lustre_cfg *lcfg);
 int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
                             struct lustre_cfg *lcfg, void *data);
 int class_attach(struct lustre_cfg *lcfg);
@@ -1869,10 +1872,6 @@ int lustre_register_fs(void);
 int lustre_unregister_fs(void);
 int lustre_check_exclusion(struct super_block *sb, char *svname);
 
 int lustre_unregister_fs(void);
 int lustre_check_exclusion(struct super_block *sb, char *svname);
 
-/* sysctl.c */
-extern int obd_sysctl_init(void);
-extern void obd_sysctl_clean(void);
-
 typedef __u8 class_uuid_t[16];
 static inline void class_uuid_unparse(class_uuid_t uu, struct obd_uuid *out)
 {
 typedef __u8 class_uuid_t[16];
 static inline void class_uuid_unparse(class_uuid_t uu, struct obd_uuid *out)
 {
index b684e3c..4b2ea1e 100644 (file)
@@ -1,6 +1,6 @@
 MODULES := obdclass llog_test
 
 MODULES := obdclass llog_test
 
-obdclass-linux-objs := linux-module.o linux-obdo.o linux-sysctl.o
+obdclass-linux-objs := linux-module.o linux-obdo.o
 obdclass-linux-objs := $(addprefix linux/,$(obdclass-linux-objs))
 
 default: all
 obdclass-linux-objs := $(addprefix linux/,$(obdclass-linux-objs))
 
 default: all
index fbbf3a9..59be17a 100644 (file)
@@ -529,7 +529,6 @@ cleanup_lu_global:
        lu_global_fini();
 
 cleanup_class_procfs:
        lu_global_fini();
 
 cleanup_class_procfs:
-       obd_sysctl_clean();
        class_procfs_clean();
 
 cleanup_caches:
        class_procfs_clean();
 
 cleanup_caches:
@@ -599,7 +598,6 @@ static void __exit obdclass_exit(void)
        lu_global_fini();
 
         obd_cleanup_caches();
        lu_global_fini();
 
         obd_cleanup_caches();
-        obd_sysctl_clean();
 
         class_procfs_clean();
 
 
         class_procfs_clean();
 
index 2998055..7d1d31d 100644 (file)
@@ -1,4 +1,3 @@
 EXTRA_DIST =                   \
        linux-module.c          \
 EXTRA_DIST =                   \
        linux-module.c          \
-       linux-sysctl.c          \
        linux-obdo.c
        linux-obdo.c
index 7fd9aa3..4b2d257 100644 (file)
@@ -273,6 +273,112 @@ struct miscdevice obd_psdev = {
        .fops   = &obd_psdev_fops,
 };
 
        .fops   = &obd_psdev_fops,
 };
 
+struct static_lustre_uintvalue_attr {
+       struct {
+               struct attribute attr;
+               ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+                               char *buf);
+               ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
+                                const char *buf, size_t len);
+       } u;
+       int *value;
+};
+
+static ssize_t static_uintvalue_show(struct kobject *kobj,
+                                    struct attribute *attr,
+                                    char *buf)
+{
+       struct static_lustre_uintvalue_attr *lattr = (void *)attr;
+
+       return sprintf(buf, "%d\n", *lattr->value);
+}
+
+static ssize_t static_uintvalue_store(struct kobject *kobj,
+                                     struct attribute *attr,
+                                     const char *buffer, size_t count)
+{
+       struct static_lustre_uintvalue_attr *lattr = (void *)attr;
+       unsigned int val;
+       int rc;
+
+       rc = kstrtouint(buffer, 10, &val);
+       if (rc)
+               return rc;
+
+       *lattr->value = val;
+
+       return count;
+}
+
+#define LUSTRE_STATIC_UINT_ATTR(name, value)                           \
+static struct static_lustre_uintvalue_attr lustre_sattr_##name =       \
+       { __ATTR(name, 0644, static_uintvalue_show,                     \
+                static_uintvalue_store), value }
+
+LUSTRE_STATIC_UINT_ATTR(timeout, &obd_timeout);
+LUSTRE_STATIC_UINT_ATTR(debug_peer_on_timeout, &obd_debug_peer_on_timeout);
+LUSTRE_STATIC_UINT_ATTR(dump_on_timeout, &obd_dump_on_timeout);
+LUSTRE_STATIC_UINT_ATTR(dump_on_eviction, &obd_dump_on_eviction);
+LUSTRE_STATIC_UINT_ATTR(at_min, &at_min);
+LUSTRE_STATIC_UINT_ATTR(at_max, &at_max);
+LUSTRE_STATIC_UINT_ATTR(at_extra, &at_extra);
+LUSTRE_STATIC_UINT_ATTR(at_early_margin, &at_early_margin);
+LUSTRE_STATIC_UINT_ATTR(at_history, &at_history);
+
+#ifdef HAVE_SERVER_SUPPORT
+LUSTRE_STATIC_UINT_ATTR(ldlm_timeout, &ldlm_timeout);
+LUSTRE_STATIC_UINT_ATTR(bulk_timeout, &bulk_timeout);
+#endif
+
+static ssize_t memused_show(struct kobject *kobj, struct attribute *attr,
+                           char *buf)
+{
+       return sprintf(buf, "%llu\n", obd_memory_sum());
+}
+LUSTRE_RO_ATTR(memused);
+
+static ssize_t memused_max_show(struct kobject *kobj, struct attribute *attr,
+                               char *buf)
+{
+       return sprintf(buf, "%llu\n", obd_memory_max());
+}
+LUSTRE_RO_ATTR(memused_max);
+
+static ssize_t max_dirty_mb_show(struct kobject *kobj, struct attribute *attr,
+                                char *buf)
+{
+       return sprintf(buf, "%lu\n",
+                      obd_max_dirty_pages / (1 << (20 - PAGE_SHIFT)));
+}
+
+static ssize_t max_dirty_mb_store(struct kobject *kobj, struct attribute *attr,
+                                 const char *buffer, size_t count)
+{
+       unsigned long val;
+       int rc;
+
+       rc = kstrtoul(buffer, 10, &val);
+       if (rc)
+               return rc;
+
+       val *= 1 << (20 - PAGE_SHIFT); /* convert to pages */
+
+       if (val > ((totalram_pages / 10) * 9)) {
+               /* Somebody wants to assign too much memory to dirty pages */
+               return -EINVAL;
+       }
+
+       if (val < 4 << (20 - PAGE_SHIFT)) {
+               /* Less than 4 Mb for dirty cache is also bad */
+               return -EINVAL;
+       }
+
+       obd_max_dirty_pages = val;
+
+       return count;
+}
+LUSTRE_RW_ATTR(max_dirty_mb);
+
 static ssize_t version_show(struct kobject *kobj, struct attribute *attr,
                            char *buf)
 {
 static ssize_t version_show(struct kobject *kobj, struct attribute *attr,
                            char *buf)
 {
@@ -431,6 +537,22 @@ static struct attribute *lustre_attrs[] = {
        &lustre_attr_health_check.attr,
        &lustre_attr_jobid_name.attr,
        &lustre_attr_jobid_var.attr,
        &lustre_attr_health_check.attr,
        &lustre_attr_jobid_name.attr,
        &lustre_attr_jobid_var.attr,
+       &lustre_sattr_timeout.u.attr,
+       &lustre_attr_max_dirty_mb.attr,
+       &lustre_sattr_debug_peer_on_timeout.u.attr,
+       &lustre_sattr_dump_on_timeout.u.attr,
+       &lustre_sattr_dump_on_eviction.u.attr,
+       &lustre_sattr_at_min.u.attr,
+       &lustre_sattr_at_max.u.attr,
+       &lustre_sattr_at_extra.u.attr,
+       &lustre_sattr_at_early_margin.u.attr,
+       &lustre_sattr_at_history.u.attr,
+       &lustre_attr_memused_max.attr,
+       &lustre_attr_memused.attr,
+#ifdef HAVE_SERVER_SUPPORT
+       &lustre_sattr_ldlm_timeout.u.attr,
+       &lustre_sattr_bulk_timeout.u.attr,
+#endif
        NULL,
 };
 
        NULL,
 };
 
@@ -518,6 +640,24 @@ static struct attribute_group lustre_attr_group = {
        .attrs = lustre_attrs,
 };
 
        .attrs = lustre_attrs,
 };
 
+ssize_t class_set_global(const char *param)
+{
+       const char *value = strchr(param, '=') + 1;
+       size_t off = value - param - 1;
+       ssize_t count = -ENOENT;
+       int i;
+
+       for (i = 0; lustre_attrs[i]; i++) {
+               if (!strncmp(lustre_attrs[i]->name, param, off)) {
+                       count = lustre_attr_store(&lustre_kset->kobj,
+                                                 lustre_attrs[i], value,
+                                                 strlen(value));
+                       break;
+               }
+       }
+       return count;
+}
+
 int class_procfs_init(void)
 {
        struct proc_dir_entry *entry;
 int class_procfs_init(void)
 {
        struct proc_dir_entry *entry;
@@ -536,12 +676,6 @@ int class_procfs_init(void)
                goto out;
        }
 
                goto out;
        }
 
-       rc = obd_sysctl_init();
-       if (rc) {
-               kset_unregister(lustre_kset);
-               goto out;
-       }
-
        rc = jobid_cache_init();
        if (rc) {
                kset_unregister(lustre_kset);
        rc = jobid_cache_init();
        if (rc) {
                kset_unregister(lustre_kset);
diff --git a/lustre/obdclass/linux/linux-sysctl.c b/lustre/obdclass/linux/linux-sysctl.c
deleted file mode 100644 (file)
index 7d59b26..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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.
- *
- * 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).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2011, 2017, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/ctype.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-#include <linux/utsname.h>
-
-#define DEBUG_SUBSYSTEM S_CLASS
-
-#include <obd_support.h>
-#include <lprocfs_status.h>
-#include <obd_class.h>
-
-struct static_lustre_uintvalue_attr {
-       struct {
-               struct attribute attr;
-               ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
-                               char *buf);
-               ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
-                                const char *buf, size_t len);
-       } u;
-       int *value;
-};
-
-static ssize_t static_uintvalue_show(struct kobject *kobj,
-                                    struct attribute *attr,
-                                    char *buf)
-{
-       struct static_lustre_uintvalue_attr *lattr = (void *)attr;
-
-       return sprintf(buf, "%d\n", *lattr->value);
-}
-
-static ssize_t static_uintvalue_store(struct kobject *kobj,
-                                     struct attribute *attr,
-                                     const char *buffer, size_t count)
-{
-       struct static_lustre_uintvalue_attr *lattr  = (void *)attr;
-       unsigned int val;
-       int rc;
-
-       rc = kstrtouint(buffer, 10, &val);
-       if (rc)
-               return rc;
-
-       *lattr->value = val;
-
-       return count;
-}
-
-#define LUSTRE_STATIC_UINT_ATTR(name, value) \
-static struct static_lustre_uintvalue_attr lustre_sattr_##name =       \
-                                       {__ATTR(name, 0644,             \
-                                               static_uintvalue_show,  \
-                                               static_uintvalue_store),\
-                                         value }
-
-LUSTRE_STATIC_UINT_ATTR(timeout, &obd_timeout);
-
-static ssize_t max_dirty_mb_show(struct kobject *kobj, struct attribute *attr,
-                                char *buf)
-{
-       return sprintf(buf, "%lu\n",
-                      obd_max_dirty_pages / (1 << (20 - PAGE_SHIFT)));
-}
-
-static ssize_t max_dirty_mb_store(struct kobject *kobj, struct attribute *attr,
-                                 const char *buffer, size_t count)
-{
-       unsigned long val;
-       int rc;
-
-       rc = kstrtoul(buffer, 10, &val);
-       if (rc)
-               return rc;
-
-       val *= 1 << (20 - PAGE_SHIFT); /* convert to pages */
-
-       if (val > ((totalram_pages / 10) * 9)) {
-               /* Somebody wants to assign too much memory to dirty pages */
-               return -EINVAL;
-       }
-
-       if (val < 4 << (20 - PAGE_SHIFT)) {
-               /* Less than 4 Mb for dirty cache is also bad */
-               return -EINVAL;
-       }
-
-       obd_max_dirty_pages = val;
-
-       return count;
-}
-LUSTRE_RW_ATTR(max_dirty_mb);
-
-LUSTRE_STATIC_UINT_ATTR(debug_peer_on_timeout, &obd_debug_peer_on_timeout);
-LUSTRE_STATIC_UINT_ATTR(dump_on_timeout, &obd_dump_on_timeout);
-LUSTRE_STATIC_UINT_ATTR(dump_on_eviction, &obd_dump_on_eviction);
-LUSTRE_STATIC_UINT_ATTR(at_min, &at_min);
-LUSTRE_STATIC_UINT_ATTR(at_max, &at_max);
-LUSTRE_STATIC_UINT_ATTR(at_extra, &at_extra);
-LUSTRE_STATIC_UINT_ATTR(at_early_margin, &at_early_margin);
-LUSTRE_STATIC_UINT_ATTR(at_history, &at_history);
-
-#ifdef HAVE_SERVER_SUPPORT
-LUSTRE_STATIC_UINT_ATTR(ldlm_timeout, &ldlm_timeout);
-LUSTRE_STATIC_UINT_ATTR(bulk_timeout, &bulk_timeout);
-#endif
-
-static ssize_t memused_show(struct kobject *kobj, struct attribute *attr,
-                           char *buf)
-{
-       return sprintf(buf, "%llu\n", obd_memory_sum());
-}
-LUSTRE_RO_ATTR(memused);
-
-static ssize_t memused_max_show(struct kobject *kobj, struct attribute *attr,
-                               char *buf)
-{
-       return sprintf(buf, "%llu\n", obd_memory_max());
-}
-LUSTRE_RO_ATTR(memused_max);
-
-static struct attribute *lustre_attrs[] = {
-       &lustre_sattr_timeout.u.attr,
-       &lustre_attr_max_dirty_mb.attr,
-       &lustre_sattr_debug_peer_on_timeout.u.attr,
-       &lustre_sattr_dump_on_timeout.u.attr,
-       &lustre_sattr_dump_on_eviction.u.attr,
-       &lustre_sattr_at_min.u.attr,
-       &lustre_sattr_at_max.u.attr,
-       &lustre_sattr_at_extra.u.attr,
-       &lustre_sattr_at_early_margin.u.attr,
-       &lustre_sattr_at_history.u.attr,
-       &lustre_attr_memused_max.attr,
-       &lustre_attr_memused.attr,
-#ifdef HAVE_SERVER_SUPPORT
-       &lustre_sattr_ldlm_timeout.u.attr,
-       &lustre_sattr_bulk_timeout.u.attr,
-#endif
-       NULL,
-};
-
-static struct attribute_group lustre_attr_group = {
-       .attrs = lustre_attrs,
-};
-
-int obd_sysctl_init(void)
-{
-       return sysfs_create_group(&lustre_kset->kobj, &lustre_attr_group);
-}
-
-void obd_sysctl_clean(void)
-{
-       sysfs_remove_group(&lustre_kset->kobj, &lustre_attr_group);
-}
index 3ebf941..aaab911 100644 (file)
@@ -36,6 +36,7 @@
 
 #define DEBUG_SUBSYSTEM S_CLASS
 
 
 #define DEBUG_SUBSYSTEM S_CLASS
 
+#include <linux/kobject.h>
 #include <linux/string.h>
 
 #include <llog_swab.h>
 #include <linux/string.h>
 
 #include <llog_swab.h>
@@ -947,34 +948,13 @@ void class_del_profiles(void)
 }
 EXPORT_SYMBOL(class_del_profiles);
 
 }
 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
 /* 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. */
+ * it lives in a module that must be loaded after this one.
+ */
 static int (*client_process_config)(struct lustre_cfg *lcfg) = NULL;
 static int (*client_process_config)(struct lustre_cfg *lcfg) = NULL;
+#ifdef HAVE_SERVER_SUPPORT
 static int (*quota_process_config)(struct lustre_cfg *lcfg) = NULL;
 static int (*quota_process_config)(struct lustre_cfg *lcfg) = NULL;
+#endif /* HAVE_SERVER_SUPPORT */
 
 void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg))
 {
 
 void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg))
 {
@@ -1057,10 +1037,12 @@ out_nocfg:
 }
 EXPORT_SYMBOL(lustre_cfg_rename);
 
 }
 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);
 {
        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",
        char *argv[] = {
                [0] = "/usr/sbin/lctl",
                [1] = "set_param",
@@ -1069,8 +1051,44 @@ static int process_param2_config(struct lustre_cfg *lcfg)
        };
        ktime_t start;
        ktime_t end;
        };
        ktime_t start;
        ktime_t end;
-       int             rc;
+       size_t len;
+       int rc;
+
        ENTRY;
        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) {
 
        /* Add upcall processing here. Now only lctl is supported */
        if (strcmp(upcall, LCTL_UPCALL) != 0) {
@@ -1096,11 +1114,13 @@ static int process_param2_config(struct lustre_cfg *lcfg)
        RETURN(rc);
 }
 
        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);
 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)
 
 /** Process configuration commands given in lustre_cfg form.
  * These may come from direct calls (e.g. class_manual_cleanup)
@@ -1196,20 +1216,22 @@ int class_process_config(struct lustre_cfg *lcfg)
                 } else if ((class_match_param(lustre_cfg_string(lcfg, 1),
                                               PARAM_SYS, &tmp) == 0)) {
                         /* Global param settings */
                 } 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.
                         */
                        /*
                         * 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);
                                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);
                } 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;
                }
 
                break;
@@ -1320,6 +1342,90 @@ out:
 }
 EXPORT_SYMBOL(class_process_config);
 
 }
 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 -ENOSYS;
+
+               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)
 {
 int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
                             struct lustre_cfg *lcfg, void *data)
 {