Whamcloud - gitweb
LU-7131 utils: add "--erase-param" option to tunefs.lustre 59/16359/17
authorNikitas Angelinas <nikitas.angelinas@seagate.com>
Fri, 30 Oct 2015 04:08:00 +0000 (12:08 +0800)
committerOleg Drokin <oleg.drokin@intel.com>
Thu, 30 Mar 2017 03:53:37 +0000 (03:53 +0000)
This patch is to fix the following issues:

- A new option "--erase-param" is added to tunefs.lustre, which allows
to remove all instances of a specific parameter.

- The behavior of option "--param" is improved a little. It will erase
all previously stored instances of the parameter, and then store all
new values specified in the command line.

- For zfs only, the patch introduces a "null value" rule in the form
of "<key>=" to mark a property to be removed from dataset by
zfs_write_ldd() finally.

- To keep the right semantics, if "--erase-params" is specified, all
old parameters should be erased before any new changes are done.

- test_89 conf-sanity.sh is added to verify these new features.

Seagate-bug-id: MRP-153
Signed-off-by: Nikitas Angelinas <nikitas.angelinas@seagate.com>
Signed-off-by: Emoly Liu <emoly.liu@intel.com>
Change-Id: Ibc8d81227045471b8e5c56c7faf1275ad8bbbf86
Tested-by: Elena Gryaznova <elena.gryaznova@seagate.com>
Reviewed-by: Artem Blagodarenko <artem.blagodarenko@seagate.com>
Reviewed-by: Alexey Lyashkov <alexey.lyashkov@seagate.com>
Reviewed-by: Vitaly Fertman <vitaly.fertman@seagate.com>
Reviewed-on: https://review.whamcloud.com/16359
Reviewed-by: Bobi Jam <bobijam@hotmail.com>
Tested-by: Jenkins
Reviewed-by: Fan Yong <fan.yong@intel.com>
Reviewed-by: James Simmons <uja.ornl@yahoo.com>
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/doc/mkfs.lustre.8
lustre/doc/tunefs.lustre.8
lustre/tests/conf-sanity.sh
lustre/utils/mkfs_lustre.c
lustre/utils/mount_utils.c
lustre/utils/mount_utils.h
lustre/utils/mount_utils_ldiskfs.c
lustre/utils/mount_utils_zfs.c

index 1c90d5b..1569d10 100644 (file)
@@ -111,7 +111,7 @@ Set permanent parameter
 .I key
 to value
 .IR value .
-This option can be repeated as desired.  Typical options might include:
+This option can be repeated as desired. Typical options might include:
 .RS
 .I \--param sys.timeout=40
 .RS
index 83fea7d..cc84df8 100644 (file)
@@ -31,10 +31,14 @@ mounted.
 Set user comment about this disk, ignored by Lustre.
 .TP
 .BI \--dryrun
-Only print what would be done; does not affect the disk
+Only print what would be done; does not affect the disk.
+.TP
+.BI \--erase-param " param"
+Remove all previously stored instances of the parameter \fIparam\fR. Attempts
+to remove a non-existing parameter appear as successful ones.
 .TP
 .BI \--erase-params
-Remove all previous parameter info
+Remove all previously stored parameter info.
 .TP
 .BI \--failnode= nid,...  
 Set the NID(s) of a failover partner. This option can be repeated as desired.
@@ -45,10 +49,10 @@ Set the NID(s) of all service partner. This option treats all nodes as equal
 service nodes. Cannot be used with --failnode.
 .TP
 .BI \--fsname= filesystem_name  
-The Lustre filesystem this service will be part of.  Default is 'lustre'
+The Lustre filesystem this service will be part of.  Default is 'lustre'.
 .TP
 .BI \--index= index
-Force a particular OST or MDT index 
+Force a particular OST or MDT index.
 .TP
 .BI \--mountfsoptions= opts
 Set  the persistent mount options that will be used when Lustre fs.
@@ -67,13 +71,41 @@ Use these options for mounting backing fs while tune.lustre is working.
 Network(s) to restrict this ost/mdt to. This option can be repeated as desired.
 .TP
 .BI \--mgs
-Add a configuration management service to this target
+Add a configuration management service to this target.
 .TP
 .BI \--mgsnode= nid,...  
 Set the NID(s) of the MGS node, required for all targets other than the MGS.
 .TP
 .BI \--nomgs
-Remove a configuration management service to this target
+Remove a configuration management service to this target.
+.TP
+.BI \--param " key=value"
+Set permanent parameter
+.I key
+to value
+.IR value .
+This option can be repeated as desired. All previously stored instances of
+.IR key
+are removed, and new instances specified in the command line are added.
+Typical options might include:
+.RS
+.I \--param sys.timeout=40
+.RS
+System obd timeout
+.RE
+.I \--param lov.stripesize=2M
+.RS
+Default stripe size
+.RE
+.I \--param lov.stripecount=2
+.RS
+Default stripe count
+.RE
+.I \--param failover.mode=failout
+.RS
+Return errors instead of waiting for recovery
+.RE
+.RE
 .TP
 .BI \--force-nohostid
 Ignore unset hostid for ZFS import protection. To set hostid either set
@@ -92,13 +124,13 @@ No clients should be started until all targets have restarted.
 .br
 Correct order of operations is:
 .br
-* Unmount all clients of this filesystem
+* Unmount all clients of this filesystem.
 .br
-* Unmount MDT and all OSTs of this filesystem
+* Unmount MDT and all OSTs of this filesystem.
 .br
-* Run \fBtunefs.lustre --writeconf <device>\fR on every server
+* Run \fBtunefs.lustre --writeconf <device>\fR on every server.
 .br
-* Mount MDT and OSTs
+* Mount MDT and OSTs.
 .br
 * Mount clients
 .TP
@@ -107,12 +139,16 @@ Enable space accounting on old 2.x devices.
 
 .SH EXAMPLES
 .TP
-.B tunefs.lustre --erase-param --mgsnode=<new_nid> --writeconf /dev/sda
+.B tunefs.lustre --erase-params --mgsnode=<new_nid> --writeconf /dev/sda
 Change the MGS NID address. (This should be done on every target disk,
 since they should all be contacting the same MGS.)
 .TP
-.B tunefs.lustre --param="failover.node=192.168.0.13@tcp0" /dev/sda
-Add a failover NID location for this target
+.B tunefs.lustre --param "failover.node=192.168.0.13@tcp0" /dev/sda
++Remove all previous failover NID locations for this target, and add a new
++failover NID location for this target.
++.TP
++.B tunefs.lustre --erase-param failover.node /dev/sda
++Remove all failover NID locations for this target.
 .TP
 .B tunefs.lustre --mgs --mdt --fsname=testfs /dev/sda
 Upgrade an old 1.4.X Lustre MDT to 1.6. The new filesystem name is "testfs". 
index 6ea18f8..59c1c9b 100755 (executable)
@@ -6161,6 +6161,81 @@ test_88() {
 }
 run_test 88 "check the default mount options can be overridden"
 
+test_89() { # LU-7131
+       [[ $(lustre_version_code $SINGLEMDS) -ge $(version_code 2.9.54) ]] ||
+               { skip "Need MDT version at least 2.9.54" && return 0; }
+
+       local key=failover.node
+       local val1=192.0.2.254@tcp0 # Reserved IPs, see RFC 5735
+       local val2=192.0.2.255@tcp0
+       local mdsdev=$(mdsdevname 1)
+       local params
+
+       stopall
+
+       [ $(facet_fstype mds1) == zfs ] && import_zpool mds1
+       # Check that parameters are added correctly
+       echo "tunefs --param $key=$val1"
+       do_facet mds "$TUNEFS --param $key=$val1 $mdsdev >/dev/null" ||
+               error "tunefs --param $key=$val1 failed"
+       params=$(do_facet mds $TUNEFS --dryrun $mdsdev) ||
+               error "tunefs --dryrun failed"
+       params=${params##*Parameters:}
+       params=${params%%exiting*}
+       [ $(echo $params | tr ' ' '\n' | grep -c $key=$val1) = "1" ] ||
+               error "on-disk parameter not added correctly via tunefs"
+
+       # Check that parameters replace existing instances when added
+       echo "tunefs --param $key=$val2"
+       do_facet mds "$TUNEFS --param $key=$val2 $mdsdev >/dev/null" ||
+               error "tunefs --param $key=$val2 failed"
+       params=$(do_facet mds $TUNEFS --dryrun $mdsdev) ||
+               error "tunefs --dryrun failed"
+       params=${params##*Parameters:}
+       params=${params%%exiting*}
+       [ $(echo $params | tr ' ' '\n' | grep -c $key=) = "1" ] ||
+               error "on-disk parameter not replaced via tunefs"
+       [ $(echo $params | tr ' ' '\n' | grep -c $key=$val2) = "1" ] ||
+               error "on-disk parameter not replaced correctly via tunefs"
+
+       # Check that a parameter is erased properly
+       echo "tunefs --erase-param $key"
+       do_facet mds "$TUNEFS --erase-param $key $mdsdev >/dev/null" ||
+               error "tunefs --erase-param $key failed"
+       params=$(do_facet mds $TUNEFS --dryrun $mdsdev) ||
+               error "tunefs --dryrun failed"
+       params=${params##*Parameters:}
+       params=${params%%exiting*}
+       [ $(echo $params | tr ' ' '\n' | grep -c $key=) = "0" ] ||
+               error "on-disk parameter not erased correctly via tunefs"
+
+       # Check that all the parameters are erased
+       echo "tunefs --erase-params"
+       do_facet mds "$TUNEFS --erase-params $mdsdev >/dev/null" ||
+               error "tunefs --erase-params failed"
+       params=$(do_facet mds $TUNEFS --dryrun $mdsdev) ||
+               error "tunefs --dryrun failed"
+       params=${params##*Parameters:}
+       params=${params%%exiting*}
+       [ -z $params ] ||
+               error "all on-disk parameters not erased correctly via tunefs"
+
+       # Check the order of options --erase-params and --param
+       echo "tunefs --param $key=$val1 --erase-params"
+       do_facet mds \
+               "$TUNEFS --param $key=$val1 --erase-params $mdsdev >/dev/null"||
+               error "tunefs --param $key=$val1 --erase-params failed"
+       params=$(do_facet mds $TUNEFS --dryrun $mdsdev) ||
+               error "tunefs --dryrun failed"
+       params=${params##*Parameters:}
+       params=${params%%exiting*}
+       [ $(echo $params | tr ' ' '\n') == "$key=$val1" ] ||
+               error "on-disk param not added correctly with --erase-params"
+
+       reformat
+}
+run_test 89 "check tunefs --param and --erase-param{s} options"
+
 # $1 test directory
 # $2 (optional) value of max_mod_rpcs_in_flight to set
 check_max_mod_rpcs_in_flight() {
index ab813fa..098e607 100644 (file)
@@ -154,6 +154,7 @@ void usage(FILE *out)
                "\t\t--replace: replace an old target with the same index\n"
                "\t\t--stripe-count-hint=#N: for optimizing MDT inode size\n"
 #else
+               "\t\t--erase-param <key>: erase all instances of a parameter\n"
                "\t\t--erase-params: erase all old parameter settings\n"
                "\t\t--writeconf: erase all config logs for this fs.\n"
                "\t\t--quota: enable space accounting on old 2.x device.\n"
@@ -173,8 +174,10 @@ void usage(FILE *out)
 
 /* ==================== Lustre config functions =============*/
 
-void print_ldd(char *str, struct lustre_disk_data *ldd)
+void print_ldd(char *str, struct mkfs_opts *mop)
 {
+       struct lustre_disk_data *ldd = &mop->mo_ldd;
+
         printf("\n   %s:\n", str);
         printf("Target:     %s\n", ldd->ldd_svname);
         if (ldd->ldd_svindex == INDEX_UNASSIGNED)
@@ -197,7 +200,7 @@ void print_ldd(char *str, struct lustre_disk_data *ldd)
                ldd->ldd_flags & LDD_F_NO_PRIMNODE? "no_primnode ":"",
                ldd->ldd_flags & LDD_F_UPGRADE14  ? "upgrade1.4 ":"");
         printf("Persistent mount opts: %s\n", ldd->ldd_mount_opts);
-        printf("Parameters:%s\n", ldd->ldd_params);
+       osd_print_ldd_params(mop);
         if (ldd->ldd_userdata[0])
                 printf("Comment: %s\n", ldd->ldd_userdata);
         printf("\n");
@@ -226,6 +229,71 @@ static inline void badopt(const char *opt, char *type)
         usage(stderr);
 }
 
+#ifdef TUNEFS
+/**
+ * Removes all existing instances of the parameter passed in \a param,
+ * which are in the form of "key=<value>", from the buffer at \a buf.
+ *
+ * The parameter can be either in the form of "key" when passed by option
+ * "--erase-param", or in the form of "key=<value>" when passed by option
+ * "--param".
+ *
+ * \param buf    the buffer holding on-disk server parameters.
+ * \param param          the parameter whose instances are to be removed from \a buf.
+ * \param withval true means the parameter is in the form of "key=<value>"
+ *               false means the parameter is in the form of "key"
+ *
+ * \retval 0     success, parameter was erased,
+ * \retval 1     success, parameter was not found, don't need to do erase_ldd,
+ * \retval EINVAL failure, invalid input parameter.
+ */
+static int erase_param(const char *const buf, const char *const param,
+                      bool withval)
+{
+       char    search[PARAM_MAX + 1] = "";
+       char    *buffer = (char *)buf;
+       bool    found = false;
+
+       if (strlen(param) > PARAM_MAX) {
+               fprintf(stderr, "%s: param to erase is too long-\n%s\n",
+                       progname, param);
+               return EINVAL;
+       }
+
+       /* add_param() writes a space as the first character in ldd_params */
+       search[0] = ' ';
+
+       /* "key" or "key=<value>" */
+       if (withval) {
+               char *keyend;
+
+               keyend = strchr(param, '=');
+               if (!keyend)
+                       return EINVAL;
+               strncpy(&search[1], param, keyend - param + 1);
+       } else {
+               strncpy(&search[1], param, strlen(param));
+               strncat(search, "=", 1);
+       }
+
+       while (1) {
+               char    *space;
+
+               buffer = strstr(buffer, search);
+               if (!buffer)
+                       return found == true ? 0 : 1;
+               found = true;
+               space = strchr(buffer + 1, ' ');
+               if (space) {
+                       memmove(buffer, space, strlen(space) + 1);
+               } else {
+                       *buffer = '\0';
+                       return 0;
+               }
+       }
+}
+#endif
+
 /* from mount_lustre */
 /* Get rid of symbolic hostnames for tcp, since kernel can't do lookups */
 #define MAXNIDSTR 1024
@@ -308,6 +376,7 @@ int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
                { "reformat",           no_argument,            NULL, 'r' },
                { "replace",            no_argument,            NULL, 'R' },
 #else
+               { "erase-param",        required_argument,      NULL, 'E' },
                { "erase-params",       no_argument,            NULL, 'e' },
                { "quota",              no_argument,            NULL, 'Q' },
                { "rename",             optional_argument,      NULL, 'R' },
@@ -319,7 +388,7 @@ int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
 #ifndef TUNEFS
                          "b:c:d:k:MOrR";
 #else
-                         "eQR::w";
+                         "E:eQR::w";
 #endif
        struct lustre_disk_data *ldd = &mop->mo_ldd;
        char new_fsname[16] = { 0 };
@@ -329,6 +398,26 @@ int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
        int replace = 0;
        bool index_option = false;
 
+#ifdef TUNEFS
+       /* For the right semantics, if '-e'/'--erase-params' is specified,
+        * it must be picked out and all old parameters should be erased
+        * before any other changes are done. */
+       while ((opt = getopt_long(argc, argv, optstring, long_opt, &longidx)) !=
+              EOF) {
+               switch (opt) {
+               case 'e':
+                       ldd->ldd_params[0] = '\0';
+                       mop->mo_flags |= MO_ERASE_ALL;
+                       ldd->ldd_flags |= LDD_F_UPDATE;
+                       break;
+               default:
+                       break;
+               }
+               if (mop->mo_flags & MO_ERASE_ALL)
+                       break;
+       }
+       optind = 0;
+#endif
        while ((opt = getopt_long(argc, argv, optstring, long_opt, &longidx)) !=
               EOF) {
                switch (opt) {
@@ -446,10 +535,17 @@ int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
                        *mountopts = optarg;
                        break;
                case 'p':
+#ifdef TUNEFS
+                       /* Removes all existing instances of the parameter
+                        * before adding new values.
+                        */
+                       rc = erase_param(ldd->ldd_params, optarg, true);
+                       if (rc > 1)
+                               return rc;
+#endif
                        rc = add_param(ldd->ldd_params, NULL, optarg);
                        if (rc != 0)
                                return rc;
-
                        /* Must update the mgs logs */
                        ldd->ldd_flags |= LDD_F_UPDATE;
                        break;
@@ -539,12 +635,24 @@ int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
                case 'R':
                        replace = 1;
                        break;
-#else /* !TUNEFS */
-               case 'e':
-                       ldd->ldd_params[0] = '\0';
+#else /* TUNEFS */
+               case 'E':
+                       rc = erase_param(ldd->ldd_params, optarg, false);
+                       /* (rc == 1) means not found, so don't need to
+                        * call osd_erase_ldd(). */
+                       if (rc > 1)
+                               return rc;
+                       if (!rc) {
+                               rc = osd_erase_ldd(mop, optarg);
+                               if (rc)
+                                       return rc;
+                       }
                        /* Must update the mgs logs */
                        ldd->ldd_flags |= LDD_F_UPDATE;
                        break;
+               case 'e':
+                       /* Already done in the beginning */
+                       break;
                case 'Q':
                        mop->mo_flags |= MO_QUOTA;
                        break;
@@ -730,7 +838,7 @@ int main(int argc, char *const argv[])
                mop.mo_mgs_failnodes++;
 
        if (verbose > 0)
-               print_ldd("Read previous values", ldd);
+               print_ldd("Read previous values", &mop);
 #endif /* TUNEFS */
 
        ret = parse_opts(argc, argv, &mop, &mountopts, old_fsname);
@@ -782,7 +890,14 @@ int main(int argc, char *const argv[])
                ldd->ldd_flags &= ~LDD_F_NEED_INDEX;
                ldd->ldd_svindex = 0;
        }
+#ifndef TUNEFS
        if (!IS_MGS(ldd) && (mop.mo_mgs_failnodes == 0)) {
+#else
+       /* Don't check --mgs or --mgsnode if print_only is set or
+        * --erase-params is set. */
+       if (!IS_MGS(ldd) && (mop.mo_mgs_failnodes == 0) && !print_only &&
+           !(mop.mo_flags & MO_ERASE_ALL)) {
+#endif
                fatal();
                if (IS_MDT(ldd))
                        fprintf(stderr, "Must specify --mgs or --mgsnode\n");
@@ -843,7 +958,7 @@ int main(int argc, char *const argv[])
        }
 
        if (verbose >= 0)
-               print_ldd("Permanent disk data", ldd);
+               print_ldd("Permanent disk data", &mop);
 
        if (print_only) {
                printf("exiting before disk write.\n");
@@ -928,7 +1043,6 @@ int main(int argc, char *const argv[])
                fprintf(stderr, "failed to write local files\n");
                goto out;
        }
-
 out:
        osd_fini();
        ret2 = loop_cleanup(&mop);
index db05359..7c7539c 100644 (file)
@@ -586,6 +586,8 @@ struct module_backfs_ops *load_backfs_module(enum ldd_mount_type mount_type)
        DLSYM(name, ops, fini);
        DLSYM(name, ops, read_ldd);
        DLSYM(name, ops, write_ldd);
+       DLSYM(name, ops, erase_ldd);
+       DLSYM(name, ops, print_ldd_params);
        DLSYM(name, ops, is_lustre);
        DLSYM(name, ops, make_lustre);
        DLSYM(name, ops, prepare_lustre);
@@ -666,6 +668,29 @@ int osd_read_ldd(char *dev, struct lustre_disk_data *ldd)
        return ret;
 }
 
+/* Erase param from the server config files */
+int osd_erase_ldd(struct mkfs_opts *mop, char *param)
+{
+       struct lustre_disk_data *ldd = &mop->mo_ldd;
+       int ret;
+
+       if (backfs_mount_type_okay(ldd->ldd_mount_type))
+               ret = backfs_ops[ldd->ldd_mount_type]->erase_ldd(mop, param);
+       else
+               ret = EINVAL;
+
+       return ret;
+}
+
+/* Print ldd_params */
+void osd_print_ldd_params(struct mkfs_opts *mop)
+{
+       struct lustre_disk_data *ldd = &mop->mo_ldd;
+
+       if (backfs_mount_type_okay(ldd->ldd_mount_type))
+               backfs_ops[ldd->ldd_mount_type]->print_ldd_params(mop);
+}
+
 /* Was this device formatted for Lustre */
 int osd_is_lustre(char *dev, unsigned *mount_type)
 {
index 2b1ac8f..c0c4526 100644 (file)
@@ -73,10 +73,14 @@ extern int failover;
 #define MO_QUOTA               0x10
 #define MO_NOHOSTID_CHECK      0x20
 #define MO_RENAME              0x40
+#define MO_ERASE_ALL           0x80
 
 #define MAX_LOOP_DEVICES       16
 #define INDEX_UNASSIGNED       0xFFFF
 
+/* Maximum length of on-disk parameters in the form key=<value> */
+#define PARAM_MAX              4096
+
 /* used to describe the options to format the lustre disk, not persistent */
 struct mkfs_opts {
        struct lustre_disk_data mo_ldd; /* to be written in MOUNT_DATA_FILE */
@@ -154,6 +158,8 @@ int loop_cleanup(struct mkfs_opts *mop);
 /* generic target support */
 int osd_write_ldd(struct mkfs_opts *mop);
 int osd_read_ldd(char *dev, struct lustre_disk_data *ldd);
+int osd_erase_ldd(struct mkfs_opts *mop, char *param);
+void osd_print_ldd_params(struct mkfs_opts *mop);
 int osd_is_lustre(char *dev, unsigned *mount_type);
 int osd_make_lustre(struct mkfs_opts *mop);
 int osd_prepare_lustre(struct mkfs_opts *mop,
@@ -171,6 +177,8 @@ struct module_backfs_ops {
        void    (*fini)(void);
        int     (*read_ldd)(char *ds,  struct lustre_disk_data *ldd);
        int     (*write_ldd)(struct mkfs_opts *mop);
+       int     (*erase_ldd)(struct mkfs_opts *mop, char *param);
+       void    (*print_ldd_params)(struct mkfs_opts *mop);
        int     (*is_lustre)(char *dev, enum ldd_mount_type *mount_type);
        int     (*make_lustre)(struct mkfs_opts *mop);
        int     (*prepare_lustre)(struct mkfs_opts *mop,
index b1edf54..2c41df3 100644 (file)
@@ -389,6 +389,15 @@ int ldiskfs_read_ldd(char *dev, struct lustre_disk_data *mo_ldd)
        return ret;
 }
 
+int ldiskfs_erase_ldd(struct mkfs_opts *mop, char *param)
+{
+       return 0;
+}
+
+void ldiskfs_print_ldd_params(struct mkfs_opts *mop)
+{
+       printf("Parameters:%s\n", mop->mo_ldd.ldd_params);
+}
 
 /* Display the need for the latest e2fsprogs to be installed. make_backfs
  * indicates if the caller is make_lustre_backfs() or not. */
index 72dbb69..6b33101 100644 (file)
@@ -136,15 +136,67 @@ static int zfs_set_prop_str(zfs_handle_t *zhp, char *prop, void *val)
 }
 
 /*
+ * Remove a property from zfs property dataset
+ */
+static int zfs_remove_prop(zfs_handle_t *zhp, nvlist_t *nvl, char *propname)
+{
+       nvlist_remove_all(nvl, propname);
+       /* XXX: please replace zfs_prop_inherit() if there is a better function
+        * to call zfs_ioctl() to update data on-disk.
+        */
+       return zfs_prop_inherit(zhp, propname, false);
+}
+
+static int zfs_erase_prop(zfs_handle_t *zhp, char *param)
+{
+       nvlist_t *nvl;
+       char propname[ZFS_MAXPROPLEN];
+       int len = strlen(param) + strlen(LDD_PREFIX);
+
+       if (len > ZFS_MAXPROPLEN) {
+               fprintf(stderr, "%s: zfs prop to erase is too long-\n%s\n",
+                       progname, param);
+               return EINVAL;
+       }
+
+       nvl = zfs_get_user_props(zhp);
+       if (!nvl)
+               return ENOENT;
+
+       snprintf(propname, len + 1, "%s%s", LDD_PREFIX, param);
+       return zfs_remove_prop(zhp, nvl, propname);
+}
+
+static int zfs_erase_allprops(zfs_handle_t *zhp)
+{
+       nvlist_t *nvl;
+       nvpair_t *curr = NULL;
+
+       nvl = zfs_get_user_props(zhp);
+       if (!nvl)
+               return ENOENT;
+
+       curr = nvlist_next_nvpair(nvl, curr);
+       while (curr) {
+               nvpair_t *next = nvlist_next_nvpair(nvl, curr);
+
+               zfs_remove_prop(zhp, nvl, nvpair_name(curr));
+               curr = next;
+       }
+
+       return 0;
+}
+
+/*
  * Map '<key>=<value> ...' pairs in the passed string to dataset properties
- * of the form 'lustre:<key>=<value>'.  Malformed <key>=<value> pairs will
- * be skipped.
+ * of the form 'lustre:<key>=<value>'. "<key>=" means to remove this key
+ * from the dataset.
  */
 static int zfs_set_prop_params(zfs_handle_t *zhp, char *params)
 {
        char *params_dup, *token, *key, *value;
        char *save_token = NULL;
-       char prop_name[ZFS_MAXPROPLEN];
+       char propname[ZFS_MAXPROPLEN];
        int ret = 0;
 
        params_dup = strdup(params);
@@ -158,15 +210,20 @@ static int zfs_set_prop_params(zfs_handle_t *zhp, char *params)
                        continue;
 
                value = strtok(NULL, "=");
-               if (value == NULL)
-                       continue;
-
-               sprintf(prop_name, "%s%s", LDD_PREFIX, key);
-               vprint("  %s=%s\n", prop_name, value);
+               if (!value) {
+                       /* remove this prop when its value is null */
+                       ret = zfs_erase_prop(zhp, key);
+                       if (ret)
+                               break;
+               } else {
+                       snprintf(propname, strlen(LDD_PREFIX) + strlen(key) + 1,
+                                "%s%s", LDD_PREFIX, key);
+                       vprint("  %s=%s\n", propname, value);
 
-               ret = zfs_prop_set(zhp, prop_name, value);
-               if (ret)
-                       break;
+                       ret = zfs_prop_set(zhp, propname, value);
+                       if (ret)
+                               break;
+               }
 
                token = strtok_r(NULL, " ", &save_token);
        }
@@ -263,10 +320,14 @@ int zfs_write_ldd(struct mkfs_opts *mop)
 
        ret = zfs_check_hostid(mop);
        if (ret != 0)
-               goto out;
+               goto out_close;
 
        vprint("Writing %s properties\n", ds);
 
+       if (mop->mo_flags & MO_ERASE_ALL)
+               ret = zfs_erase_allprops(zhp);
+       ret = zfs_set_prop_params(zhp, ldd->ldd_params);
+
        for (i = 0; special_ldd_prop_params[i].zlpb_prop_name != NULL; i++) {
                bridge = &special_ldd_prop_params[i];
                ret = bridge->zlpb_set_prop_fn(zhp, bridge->zlpb_prop_name,
@@ -275,14 +336,26 @@ int zfs_write_ldd(struct mkfs_opts *mop)
                        goto out_close;
        }
 
-       ret = zfs_set_prop_params(zhp, ldd->ldd_params);
-
 out_close:
        zfs_close(zhp);
 out:
        return ret;
 }
 
+/* Mark a property to be removed by the form of "key=" */
+int zfs_erase_ldd(struct mkfs_opts *mop, char *param)
+{
+       char key[ZFS_MAXPROPLEN] = "";
+
+       if (strlen(LDD_PREFIX) + strlen(param) > ZFS_MAXPROPLEN) {
+               fprintf(stderr, "%s: zfs prop to erase is too long-\n%s\n",
+                       progname, param);
+               return EINVAL;
+       }
+       snprintf(key, strlen(param) + 2, "%s=", param);
+       return add_param(mop->mo_ldd.ldd_params, key, "");
+}
+
 static int zfs_get_prop_int(zfs_handle_t *zhp, char *prop, void *val)
 {
        nvlist_t *propval;
@@ -335,22 +408,18 @@ static int zfs_is_special_ldd_prop_param(char *name)
        return 0;
 }
 
-static int zfs_get_prop_params(zfs_handle_t *zhp, char *param, int len)
+static int zfs_get_prop_params(zfs_handle_t *zhp, char *param)
 {
        nvlist_t *props;
        nvpair_t *nvp;
-       char key[ZFS_MAXPROPLEN];
-       char *value;
+       char key[ZFS_MAXPROPLEN] = "";
+       char value[PARAM_MAX] = "";
        int ret = 0;
 
        props = zfs_get_user_props(zhp);
        if (props == NULL)
                return ENOENT;
 
-       value = malloc(len);
-       if (value == NULL)
-               return ENOMEM;
-
        nvp = NULL;
        while (nvp = nvlist_next_nvpair(props, nvp), nvp) {
                ret = zfs_get_prop_str(zhp, nvpair_name(nvp), value);
@@ -364,14 +433,11 @@ static int zfs_get_prop_params(zfs_handle_t *zhp, char *param, int len)
                        continue;
 
                sprintf(key, "%s=",  nvpair_name(nvp) + strlen(LDD_PREFIX));
-
                ret = add_param(param, key, value);
                if (ret)
                        break;
        }
 
-       free(value);
-
        return ret;
 }
 
@@ -403,7 +469,7 @@ int zfs_read_ldd(char *ds,  struct lustre_disk_data *ldd)
                        goto out_close;
        }
 
-       ret = zfs_get_prop_params(zhp, ldd->ldd_params, 4096);
+       ret = zfs_get_prop_params(zhp, ldd->ldd_params);
        if (ret && (ret != ENOENT))
                goto out_close;
 
@@ -415,6 +481,42 @@ out:
        return ret;
 }
 
+/* Print ldd params */
+void zfs_print_ldd_params(struct mkfs_opts *mop)
+{
+       char *from = mop->mo_ldd.ldd_params;
+       char *to;
+       int len;
+
+       vprint("Parameters:");
+       while (from) {
+               /* skip those keys to be removed in the form of "key=" */
+               to = strstr(from, "= ");
+               if (!to)
+                       /* "key=" may be in the end */
+                       if (*(from + strlen(from) - 1) == '=')
+                               to = from + strlen(from) - 1;
+
+               /* find " " inward */
+               len = strlen(from);
+               if (to) {
+                       len = strlen(from) - strlen(to);
+                       while ((*(from + len) != ' ') && len)
+                               len--;
+               }
+               if (len)
+                       /* no space in the end */
+                       vprint("%*.*s", len, len, from);
+
+               /* If there is no "key=" or "key=" is in the end, stop. */
+               if (!to || strlen(to) == 1)
+                       break;
+
+               /* skip "=" */
+               from = to + 1;
+       }
+}
+
 int zfs_is_lustre(char *ds, unsigned *mount_type)
 {
        struct lustre_disk_data tmp_ldd;