Whamcloud - gitweb
b=15253 add conf_param -d to remove permanent settings
authorNathan Rutman <nathan.rutman@oracle.com>
Thu, 20 May 2010 20:43:09 +0000 (13:43 -0700)
committerJohann Lombardi <johann@sun.com>
Thu, 20 May 2010 21:23:59 +0000 (23:23 +0200)
i=adilger
i=rread

lustre/ChangeLog
lustre/doc/lctl.8
lustre/include/lustre_param.h
lustre/mgs/mgs_handler.c
lustre/mgs/mgs_llog.c
lustre/tests/conf-sanity.sh
lustre/utils/lctl.c
lustre/utils/lustre_cfg.c

index 4ea2cef..075dcfa 100644 (file)
@@ -13,6 +13,14 @@ tbd        Oracle, Inc.
          lock before replay feature (bug 16774) are disabled by default.
 
 Severity   : enhancement
+Bugzilla   : 15253
+Description: add conf-param -d option to remove permanent settings.
+Details    : Add the ability to remove permanent lctl conf_param settings.
+            (Previously conf_param settings could only be changed, not
+            removed.) This also provides a method to change failover
+            nid locations. Improve lctl man page.
+
+Severity   : enhancement
 Bugzilla   : 22455
 Description: add list_param to b1_8 and add "-R" option to list params
             recursively
index 088bd1f..8979471 100644 (file)
@@ -11,14 +11,14 @@ lctl \- Low level Lustre filesystem configuration utility
 .B lctl
 is used to directly control Lustre via an ioctl interface, allowing
 various configuration, maintenance, and debugging features to be accessed.
+
 .B lctl
 can be invoked in interactive mode by issuing lctl command. After that, commands are issued as below. The most common commands in lctl are
 .B dl
 ,
-.B device 
+.B device
 ,
-.B network 
+.B network
 .I <up/down>
 ,
 .B list_nids
@@ -33,12 +33,12 @@ can be invoked in interactive mode by issuing lctl command. After that, commands
 To get a complete listing of available commands, type
 .B help
 at the lctl prompt.  To get basic help on the meaning and syntax of a
-command, type 
-.B help 
+command, type
+.B help
 .I command
-.  Command completion is activated with the TAB key, and command history is available via the up- and down-arrow keys. 
+.  Command completion is activated with the TAB key, and command history is available via the up- and down-arrow keys.
 
-For non-interactive use, one uses the second invocation, which runs command after connecting to the device. 
+For non-interactive use, one uses the second invocation, which runs command after connecting to the device.
 
 .SS Network Configuration
 .TP
@@ -58,52 +58,85 @@ will take place on.
 Check LNET connectivity via an LNET ping. This will use the fabric
 appropriate to the specified NID.
 .TP
-.BI interface_list 
-Print the network interface information for a given 
+.BI interface_list
+Print the network interface information for a given
 .B network
 type.
 .TP
-.BI peer_list 
-Print the known peers for a given 
+.BI peer_list
+Print the known peers for a given
 .B network
 type.
 .TP
-.BI conn_list 
+.BI conn_list
 Print all the connected remote NIDs for a given
 .B network
 type.
 .TP
-.BI active_tx 
+.BI active_tx
 This command should print active transmits, and it is only used for elan network type.
-.TP 
-.BI route_list 
+.TP
+.BI route_list
 Print the complete routing table.
 .PP
 .SS Device Selection
-.TP 
-.BI device " <devname> " 
-This will select the specified OBD device.  All other commands depend on the device being set. 
-.TP 
-.BI device_list 
-Show all the local Lustre OBDs. AKA 
+.TP
+.BI device " <devname> "
+This will select the specified OBD device.  All other commands depend on the device being set.
+.TP
+.BI device_list
+Show all the local Lustre OBDs. AKA
 .B dl
 .PP
 .SS Device Operations
-.TP 
-.BI conf_param " <device> <parameter>"
+.TP
+.BI conf_param " [-d] <device|fsname>.<parameter>=<value>"
 Set a permanent configuration parameter for any device via the MGS.  This
-command must be run on the MGS node. 
-.TP 
-.BI activate 
-Reactivate an import after deactivating, below.
-.TP 
-.BI deactivate 
+command must be run on the MGS node.
+.br
+.B -d <device|fsname>.<parameter>
+Delete a parameter setting (use the default value at the next restart).  A null value for <value> also deletes the parameter setting.
+.br
+.B Parameters:
+.br
+All of the writable parameters under
+.B lctl list_param
+(e.g.
+.I lctl list_param -F osc.*.* | grep =
+) can be permanently set using
+.B lctl conf_param
+, but the format is slightly different.  For conf_param, the device is specified first, then the obdtype. (See examples below.)  Wildcards are not supported.
+.br
+Additionally, failover nodes may be added (or removed), and some system-wide parameters may be set as well (sys.at_max, sys.at_min, sys.at_extra, sys.at_early_margin, sys.at_history, sys.timeout, sys.ldlm_timeout.)  <device> is ignored for system wide parameters.
+.br
+.B Examples:
+.br
+# lctl conf_param testfs.sys.at_max=1200
+.br
+# lctl conf_param testfs.llite.max_read_ahead_mb=16
+.br
+# lctl conf_param testfs-MDT0000.lov.stripesize=2M
+.br
+# lctl conf_param lustre-OST0001.osc.active=0
+.br
+# lctl conf_param testfs-OST0000.osc.max_dirty_mb=29.15
+.br
+# lctl conf_param testfs-OST0000.ost.client_cache_seconds=15
+.br
+# lctl conf_param testfs-OST0000.failover.node=1.2.3.4@tcp1
+.TP
+.BI activate
+Reactivate an import after deactivating, below.  This setting is only effective until the next restart (see
+.B conf_param
+).
+.TP
+.BI deactivate
 Deactivate an import, in particular meaning do not assign new file stripes
 to an OSC.  This command should be used on the OSC in the MDT LOV
 corresponding to a failed OST device, to prevent further attempts at
 communication with the failed OST.
-.TP 
-.BI abort_recovery 
+.TP
+.BI abort_recovery
 Abort the recovery process on a restarting MDT or OST device
 .PP
 .SS Virtual Block Device Operation
@@ -119,45 +152,45 @@ Detach the virtual block device.
 Acquire which lustre file was attached to the device node.
 .PP
 .SS Debug
-.TP 
-.BI debug_daemon 
+.TP
+.BI debug_daemon
 Start and stop the debug daemon, and control the output filename and size.
-.TP 
-.BI debug_kernel " [file] [raw]" 
+.TP
+.BI debug_kernel " [file] [raw]"
 Dump the kernel debug buffer to stdout or file.
-.TP 
+.TP
 .BI debug_file " <input> [output]"
 Convert kernel-dumped debug log from binary to plain text format.
-.TP 
-.BI clear 
+.TP
+.BI clear
 Clear the kernel debug buffer.
-.TP 
-.BI mark " <text>" 
+.TP
+.BI mark " <text>"
 Insert marker text in the kernel debug buffer.
-.TP 
-.BI filter " <subsystem id/debug mask>" 
+.TP
+.BI filter " <subsystem id/debug mask>"
 Filter kernel debug messages by subsystem or mask.
-.TP 
-.BI show " <subsystem id/debug mask>" 
+.TP
+.BI show " <subsystem id/debug mask>"
 Show specific type of messages.
-.TP 
-.BI debug_list " <subs/types>" 
+.TP
+.BI debug_list " <subs/types>"
 List all the subsystem and debug types.
 .TP
-.BI modules " <path>" 
+.BI modules " <path>"
 Provide gdb-friendly module information.
 
 .SH OPTIONS
-The following options can be used to invoke lctl. 
+The following options can be used to invoke lctl.
 .TP
-.B --device 
+.B --device
 The device to be used for the operation. This can be specified by name or
-number. See 
+number. See
 .B device_list
 .TP
-.B --ignore_errors | ignore_errors 
+.B --ignore_errors | ignore_errors
 Ignore errors during script processing
-.TP
+
 .SH EXAMPLES
 # lctl
 .br
@@ -170,15 +203,13 @@ lctl > dk /tmp/log
 Debug log: 87 lines, 87 kept, 0 dropped.
 .br
 lctl > quit
-.PP
-# lctl conf_param testfs-MDT0000 sys.timeout=40
 
 .SH BUGS
 Please report all bugs to Sun Microsystems, Inc. http://bugzilla.lustre.org/
 .SH AVAILABILITY
 .B lctl
-is part of the 
-.BR Lustre (7) 
+is part of the
+.BR Lustre (7)
 filesystem package and is available from Sun Microsystems, Inc.
 .br
 http://www.sun.com/software/products/lustre/index.xml
index 56552ec..133be5a 100644 (file)
@@ -63,7 +63,9 @@ int do_lcfg(char *cfgname, lnet_nid_t nid, int cmd,
                     ... testfs.llite.max_read_ahead_mb=16
 */
 
-/* System global or special params not handled in obd's proc */
+/* System global or special params not handled in obd's proc
+ * See mgs_write_log_sys()
+ */
 #define PARAM_TIMEOUT              "timeout="          /* global */
 #define PARAM_LDLM_TIMEOUT         "ldlm_timeout="     /* global */
 #define PARAM_AT_MIN               "at_min="           /* global */
@@ -71,10 +73,10 @@ int do_lcfg(char *cfgname, lnet_nid_t nid, int cmd,
 #define PARAM_AT_EXTRA             "at_extra="         /* global */
 #define PARAM_AT_EARLY_MARGIN      "at_early_margin="  /* global */
 #define PARAM_AT_HISTORY           "at_history="       /* global */
-#define PARAM_MGSNODE              "mgsnode="          /* during mount */
-#define PARAM_FAILNODE             "failover.node="    /* llog generation */
-#define PARAM_FAILMODE             "failover.mode="    /* llog generation */
-#define PARAM_ACTIVE               "active="           /* llog generation */
+#define PARAM_MGSNODE              "mgsnode="          /* only at mounttime */
+#define PARAM_FAILNODE             "failover.node="    /* add failover nid */
+#define PARAM_FAILMODE             "failover.mode="    /* initial mount only */
+#define PARAM_ACTIVE               "active="           /* activate/deactivate */
 #define PARAM_MDT_UPCALL           "mdt.group_upcall=" /* mds group upcall */
 
 /* Prefixes for parameters handled by obd's proc methods (XXX_process_config) */
index 5c16241..43db526 100644 (file)
@@ -42,7 +42,7 @@
 # define EXPORT_SYMTAB
 #endif
 #define DEBUG_SUBSYSTEM S_MGS
-#define D_MGS D_CONFIG/*|D_WARNING*/
+#define D_MGS D_CONFIG
 
 #ifdef __KERNEL__
 # include <linux/module.h>
index 665831d..06ebced 100644 (file)
@@ -44,7 +44,7 @@
 #define EXPORT_SYMTAB
 #endif
 #define DEBUG_SUBSYSTEM S_MGS
-#define D_MGS D_CONFIG /*|D_WARNING*/
+#define D_MGS D_CONFIG
 
 #ifdef __KERNEL__
 #include <linux/module.h>
@@ -583,7 +583,8 @@ static int mgs_modify(struct obd_device *obd, struct fs_db *fsdb,
         int rc, rc2;
         ENTRY;
 
-        CDEBUG(D_MGS, "modify %s/%s/%s\n", logname, devname, comment);
+        CDEBUG(D_MGS, "modify %s/%s/%s fl=%x\n", logname, devname, comment,
+               flags);
 
         push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
@@ -930,11 +931,15 @@ int mgs_write_log_direct_all(struct obd_device *obd, struct fs_db *fsdb,
                         mgs_modify(obd, fsdb, mti, dirent->lld_name, devname,
                                    comment, CM_SKIP);
                         /* Write the new one */
-                        rc = mgs_write_log_direct(obd, fsdb, dirent->lld_name,
-                                                  lcfg, devname, comment);
-                        if (rc)
-                                CERROR("err %d writing log %s\n", rc,
-                                       dirent->lld_name);
+                        if (lcfg) {
+                                rc = mgs_write_log_direct(obd, fsdb,
+                                                          dirent->lld_name,
+                                                          lcfg, devname,
+                                                          comment);
+                                if (rc)
+                                        CERROR("err %d writing log %s\n", rc,
+                                               dirent->lld_name);
+                        }
                 }
                 OBD_FREE(dirent, sizeof(*dirent));
         }
@@ -1292,6 +1297,45 @@ static int mgs_write_log_ost(struct obd_device *obd, struct fs_db *fsdb,
         RETURN(rc);
 }
 
+static __inline__ int mgs_param_empty(char *ptr)
+{
+        char *tmp;
+
+        if ((tmp = strchr(ptr, '=')) && (*(++tmp) == '\0'))
+                return 1;
+        return 0;
+}
+
+static int mgs_write_log_failnid_internal(struct obd_device *obd,
+                                          struct fs_db *fsdb,
+                                          struct mgs_target_info *mti,
+                                          char *logname, char *cliname)
+{
+        int rc;
+        struct llog_handle *llh = NULL;
+
+        if (mgs_param_empty(mti->mti_params)) {
+                /* Remove _all_ failnids */
+                rc = mgs_modify(obd, fsdb, mti, logname,
+                                mti->mti_svname, "add failnid", CM_SKIP);
+                return rc;
+        }
+
+        /* Otherwise failover nids are additive */
+        rc = record_start_log(obd, &llh, logname);
+        if (!rc) {
+                /* FIXME this should be a single journal transaction */
+                rc = record_marker(obd, llh, fsdb, CM_START,
+                                   mti->mti_svname, "add failnid");
+                rc = mgs_write_log_failnids(obd, mti, llh, cliname);
+                rc = record_marker(obd, llh, fsdb, CM_END,
+                                   mti->mti_svname, "add failnid");
+                rc = record_end_log(obd, &llh);
+        }
+
+        return rc;
+}
+
 /* Add additional failnids to an existing log.
    The mdc/osc must have been added to logs first */
 /* tcp nids must be in dotted-quad ascii -
@@ -1300,15 +1344,12 @@ static int mgs_write_log_add_failnid(struct obd_device *obd, struct fs_db *fsdb,
                                      struct mgs_target_info *mti)
 {
         char *logname, *cliname;
-        struct llog_handle *llh = NULL;
         int rc;
         ENTRY;
 
-        /* FIXME how do we delete a failnid? Currently --writeconf is the
-           only way.  Maybe make --erase-params pass a flag to really
-           erase all params from logs - except it can't erase the failnids
-           given when a target first registers, since they aren't processed
-           as params... */
+        /* FIXME we currently can't erase the failnids
+         * given when a target first registers, since they aren't part of
+         * an "add uuid" stanza */
 
         /* Verify that we know about this target */
         if (mgs_log_is_empty(obd, mti->mti_svname)) {
@@ -1340,32 +1381,16 @@ static int mgs_write_log_add_failnid(struct obd_device *obd, struct fs_db *fsdb,
                 RETURN(-EINVAL);
         }
 
-        /* Add failover nids to client log */
+        /* Add failover nids to the client log */
         name_create(&logname, mti->mti_fsname, "-client");
-        rc = record_start_log(obd, &llh, logname);
-        if (!rc) {
-                /* FIXME this fn should be a single journal transaction */
-                rc = record_marker(obd, llh, fsdb, CM_START, mti->mti_svname,
-                                   "add failnid");
-                rc = mgs_write_log_failnids(obd, mti, llh, cliname);
-                rc = record_marker(obd, llh, fsdb, CM_END, mti->mti_svname,
-                                   "add failnid");
-                rc = record_end_log(obd, &llh);
-        }
+        rc = mgs_write_log_failnid_internal(obd, fsdb, mti, logname, cliname);
         name_destroy(&logname);
 
         if (mti->mti_flags & LDD_F_SV_TYPE_OST) {
                 /* Add OST failover nids to the MDT log as well */
                 name_create(&logname, mti->mti_fsname, "-MDT0000");
-                rc = record_start_log(obd, &llh, logname);
-                if (!rc) {
-                        rc = record_marker(obd, llh, fsdb, CM_START,
-                                           mti->mti_svname, "add failnid");
-                        rc = mgs_write_log_failnids(obd, mti, llh, cliname);
-                        rc = record_marker(obd, llh, fsdb, CM_END,
-                                           mti->mti_svname, "add failnid");
-                        rc = record_end_log(obd, &llh);
-                }
+                rc = mgs_write_log_failnid_internal(obd, fsdb, mti, logname,
+                                                    cliname);
                 name_destroy(&logname);
         }
 
@@ -1381,7 +1406,7 @@ static int mgs_wlp_lcfg(struct obd_device *obd, struct fs_db *fsdb,
         char comment[MTI_NAME_MAXLEN];
         char *tmp;
         struct lustre_cfg *lcfg;
-        int rc;
+        int rc, del;
 
         /* Erase any old settings of this same parameter */
         memcpy(comment, ptr, MTI_NAME_MAXLEN);
@@ -1391,8 +1416,12 @@ static int mgs_wlp_lcfg(struct obd_device *obd, struct fs_db *fsdb,
             *tmp = 0;
         /* FIXME we should skip settings that are the same as old values */
         rc = mgs_modify(obd, fsdb, mti, logname, tgtname, comment, CM_SKIP);
-        LCONSOLE_INFO("%sing parameter %s.%s in log %s\n", rc ?
+        del = mgs_param_empty(ptr);
+
+        LCONSOLE_INFO("%sing parameter %s.%s in log %s\n", del ? "Disabl" : rc ?
                       "Sett" : "Modify", tgtname, comment, logname);
+        if (del)
+                return rc;
 
         lustre_cfg_bufs_reset(bufs, tgtname);
         lustre_cfg_bufs_set_string(bufs, 1, ptr);
@@ -1411,6 +1440,7 @@ static int mgs_write_log_sys(struct obd_device *obd, struct fs_db *fsdb,
         struct lustre_cfg_bufs bufs;
         struct lustre_cfg *lcfg;
         char *tmp;
+        char sep;
         int cmd, val;
         int rc;
 
@@ -1428,16 +1458,26 @@ static int mgs_write_log_sys(struct obd_device *obd, struct fs_db *fsdb,
         else
                 return -EINVAL;
 
+        /* separate the value */
         val = simple_strtoul(tmp, NULL, 0);
-        CDEBUG(D_MGS, "global %s = %d\n", ptr, val);
+        if (*tmp == '\0')
+                CDEBUG(D_MGS, "global '%s' removed\n", sys);
+        else
+                CDEBUG(D_MGS, "global '%s' val=%d\n", sys, val);
 
         lustre_cfg_bufs_reset(&bufs, NULL);
         lustre_cfg_bufs_set_string(&bufs, 1, sys);
         lcfg = lustre_cfg_new(cmd, &bufs);
         lcfg->lcfg_num = val;
+        /* truncate the comment to the parameter name */
+        ptr = tmp - 1;
+        sep = *ptr;
+        *ptr = '\0';
         /* modify all servers and clients */
-        rc = mgs_write_log_direct_all(obd, fsdb, mti, lcfg, mti->mti_fsname,
-                                      ptr);
+        rc = mgs_write_log_direct_all(obd, fsdb, mti,
+                                      *tmp == '\0' ? NULL : lcfg,
+                                      mti->mti_fsname, sys);
+        *ptr = sep;
         lustre_cfg_free(lcfg);
         return rc;
 }
@@ -1788,6 +1828,11 @@ out_up:
 }
 
 /* COMPAT_146 */
+/* Permanent settings of all parameters by writing into the appropriate
+ * configuration logs.
+ * A parameter with null value ("<param>='\0'") means to erase it out of
+ * the logs.
+ */
 /* verify that we can handle the old config logs */
 int mgs_upgrade_sv_14(struct obd_device *obd, struct mgs_target_info *mti)
 {
@@ -1954,7 +1999,10 @@ static void print_lustre_cfg(struct lustre_cfg *lcfg)
         EXIT;
 }
 
-/* Set a permanent (config log) param for a target or fs */
+/* Set a permanent (config log) param for a target or fs
+ * \param lcfg buf0 may contain the device (testfs-MDT0000) name
+ *             buf1 contains the single parameter
+ */
 int mgs_setparam(struct obd_device *obd, struct lustre_cfg *lcfg, char *fsname)
 {
         struct fs_db *fsdb;
@@ -1999,7 +2047,7 @@ int mgs_setparam(struct obd_device *obd, struct lustre_cfg *lcfg, char *fsname)
                 strncpy(fsname, devname, MTI_NAME_MAXLEN);
         }
         fsname[MTI_NAME_MAXLEN - 1] = 0;
-        CDEBUG(D_MGS, "setparam on fs %s device %s\n", fsname, devname);
+        CDEBUG(D_MGS, "setparam fs='%s' device='%s'\n", fsname, devname);
 
         rc = mgs_find_or_make_fsdb(obd, fsname, &fsdb);
         if (rc)
index 95b827f..17d817e 100644 (file)
@@ -1022,6 +1022,7 @@ run_test 29 "permanently remove an OST"
 test_30() {
        setup
 
+       echo Big config llog
        TEST="lctl get_param -n llite.$FSNAME-*.max_read_ahead_whole_mb"
        ORIG=$($TEST)
        LIST=(1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 1 2 3 4 5)
@@ -1032,10 +1033,20 @@ test_30() {
        umount_client $MOUNT
        mount_client $MOUNT || return 4
        [ "$($TEST)" -ne "$i" ] && return 5
-       set_and_check client "$TEST" "$FSNAME.llite.max_read_ahead_whole_mb" $ORIG || return 6
+       pass
+
+       echo Erase parameter setting
+       do_facet mgs "$LCTL conf_param -d $FSNAME.llite.max_read_ahead_whole_mb" || return 6
+       umount_client $MOUNT
+       mount_client $MOUNT || return 6
+       FINAL=$($TEST)
+       echo "deleted (default) value=$FINAL, orig=$ORIG"
+       # assumes this parameter started at the default value
+       [ "$FINAL" -eq "$ORIG" ] || fail "Deleted value=$FINAL, orig=$ORIG"
+
        cleanup
 }
-run_test 30 "Big config llog"
+run_test 30 "Big config llog and conf_param deletion"
 
 test_31() { # bug 10734
         # ipaddr must not exist
@@ -1186,9 +1197,11 @@ test_32b() {
        echo OST uuid $UUID
        [ "$UUID" == "lustre-OST0000_UUID" ] || error "UUID is wrong: $UUID"
 
+       local NID=$($LCTL list_nids | head -1)
+
        echo "OSC changes should succeed:"
-       $LCTL conf_param lustre-OST0000.osc.max_dirty_mb=15 || return 7
-       $LCTL conf_param lustre-OST0000.failover.node=$NID || return 8
+       $LCTL conf_param lustre-OST0000.osc.max_dirty_mb=15 || error "OSC conf_param failed"
+       $LCTL conf_param lustre-OST0000.failover.node=$NID || error "add failover nid=$NID failed"
        echo "ok."
        echo "MDC changes should succeed:"
        $LCTL conf_param lustre-MDT0000.mdc.max_rpcs_in_flight=9 || return 9
index 17efa93..15343a0 100644 (file)
@@ -131,11 +131,12 @@ command_t cmdlist[] = {
          "abort recovery on a restarting MDT or OST device\n"},
         {"set_timeout", jt_lcfg_set_timeout, 0,
          "usage: conf_param obd_timeout=<secs>\n"},
-        {"conf_param", jt_lcfg_mgsparam, 0, "set a permanent config param. "
+        {"conf_param", jt_lcfg_mgsparam, 0,"set a permanent config parameter.\n"
          "This command must be run on the MGS node\n"
-         "usage: conf_param <target.keyword=val> ...\n"},
+         "usage: conf_param [-d] <target.keyword=val>\n"
+         "  -d  Remove the permanent setting."},
         {"local_param", jt_lcfg_param, 0, "set a temporary, local param\n"
-         "usage: local_param <target.keyword=val> ...\n"},
+         "usage: local_param <target.keyword=val>\n"},
         {"get_param", jt_lcfg_getparam, 0, "get the Lustre or LNET parameter\n"
          "usage: get_param [-n | -N | -F] <param_path1 param_path2 ...> \n"
          "Get the value of Lustre or LNET parameter from the specified path.\n"
index 0ff48bd..ffc2e85 100644 (file)
@@ -469,20 +469,49 @@ int jt_lcfg_param(int argc, char **argv)
 }
 
 /* Param set in config log on MGS */
-/* conf_param key1=value1 [key2=value2...] */
+/* conf_param key=value */
+/* Note we can actually send mgc conf_params from clients, but currently
+ * that's only done for default file striping (see ll_send_mgc_param),
+ * and not here. */
+/* After removal of a parameter (-d) Lustre will use the default
+ * AT NEXT REBOOT, not immediately. */
 int jt_lcfg_mgsparam(int argc, char **argv)
 {
-        int i, rc;
+        int rc;
+        int del = 0;
         struct lustre_cfg_bufs bufs;
         struct lustre_cfg *lcfg;
+        char *buf = NULL;
 
-        if ((argc >= LUSTRE_CFG_MAX_BUFCOUNT) || (argc <= 1))
+        /* mgs_setparam processes only lctl buf #1 */
+        if ((argc > 3) || (argc <= 1))
                 return CMD_HELP;
 
+        while ((rc = getopt(argc, argv, "d")) != -1) {
+                switch (rc) {
+                case 'd':
+                        del = 1;
+                        break;
+                default:
+                        return CMD_HELP;
+                }
+        }
+
         lustre_cfg_bufs_reset(&bufs, NULL);
 
-        for (i = 1; i < argc; i++) {
-                lustre_cfg_bufs_set_string(&bufs, i, argv[i]);
+        if (del) {
+                char *ptr;
+
+                /* for delete, make it "<param>=\0" */
+                buf = malloc(strlen(argv[optind]) + 2);
+                /* put an '=' on the end in case it doesn't have one */
+                sprintf(buf, "%s=", argv[optind]);
+                /* then truncate after the first '=' */
+                ptr = strchr(buf, '=');
+                *(++ptr) = '\0';
+                lustre_cfg_bufs_set_string(&bufs, 1, buf);
+        } else {
+                lustre_cfg_bufs_set_string(&bufs, 1, argv[optind]);
         }
 
         /* We could put other opcodes here. */
@@ -490,6 +519,8 @@ int jt_lcfg_mgsparam(int argc, char **argv)
 
         rc = lcfg_mgs_ioctl(argv[0], OBD_DEV_ID, lcfg);
         lustre_cfg_free(lcfg);
+        if (buf)
+                free(buf);
         if (rc < 0) {
                 fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
                         strerror(rc = errno));