Whamcloud - gitweb
LU-9772 utils: Enable new ZFS MMP on mkfs
[fs/lustre-release.git] / lustre / utils / mount_utils_zfs.c
index 79c4e94..aacf076 100644 (file)
  * GPL HEADER END
  */
 /*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2016, Intel Corporation.
  * Use is subject to license terms.
- *
  */
 /*
  * Author: Brian Behlendorf <behlendorf1@llnl.gov>
  */
 #include "mount_utils.h"
+#include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 #include <libzfs.h>
-#include <dlfcn.h>
+
+#define HOSTID_PATH "/etc/hostid"
 
 /* Persistent mount data is stored in these user attributes */
-#define LDD_PREFIX                     "lustre:"
-#define LDD_VERSION_PROP               "lustre:version"
-#define LDD_FLAGS_PROP                 "lustre:flags"
-#define LDD_INDEX_PROP                 "lustre:index"
-#define LDD_FSNAME_PROP                        "lustre:fsname"
-#define LDD_SVNAME_PROP                        "lustre:svname"
-#define LDD_UUID_PROP                  "lustre:uuid"
-#define LDD_USERDATA_PROP              "lustre:userdata"
-#define LDD_MOUNTOPTS_PROP             "lustre:mountopts"
+#define LDD_PREFIX             "lustre:"
+#define LDD_VERSION_PROP       LDD_PREFIX "version"
+#define LDD_FLAGS_PROP         LDD_PREFIX "flags"
+#define LDD_INDEX_PROP         LDD_PREFIX "index"
+#define LDD_FSNAME_PROP                LDD_PREFIX "fsname"
+#define LDD_SVNAME_PROP                LDD_PREFIX "svname"
+#define LDD_UUID_PROP          LDD_PREFIX "uuid"
+#define LDD_USERDATA_PROP      LDD_PREFIX "userdata"
+#define LDD_MOUNTOPTS_PROP     LDD_PREFIX "mountopts"
 
 /* This structure is used to help bridge the gap between the ZFS
  * properties Lustre uses and their corresponding internal LDD fields.
@@ -104,84 +105,8 @@ static int osd_zfs_setup = 0;
 
 static libzfs_handle_t *g_zfs;
 
-/* dynamic linking handles for libzfs & libnvpair */
-static void *handle_libzfs;
-static void *handle_nvpair;
-
-/* symbol table looked up with dlsym */
-struct zfs_symbols {
-       libzfs_handle_t *(*libzfs_init)(void);
-       void            (*libzfs_fini)(libzfs_handle_t *);
-       int             (*libzfs_load_module)(char *);
-       zfs_handle_t*   (*zfs_open)(libzfs_handle_t *, const char *, int);
-       int             (*zfs_destroy)(zfs_handle_t *, boolean_t);
-       void            (*zfs_close)(zfs_handle_t *);
-       int     (*zfs_prop_set)(zfs_handle_t*, const char*, const char*);
-       nvlist_t*       (*zfs_get_user_props)  (zfs_handle_t *);
-       int             (*zfs_name_valid)(const char *, zfs_type_t);
-       zpool_handle_t* (*zpool_open)(libzfs_handle_t *, const char *);
-       void            (*zpool_close)(zpool_handle_t *zhp);
-       int             (*nvlist_lookup_string)(nvlist_t*, const char*, char**);
-       int     (*nvlist_lookup_nvlist)(nvlist_t *, const char *, nvlist_t **);
-       nvpair_t *      (*nvlist_next_nvpair)(nvlist_t *, nvpair_t *);
-       char *          (*nvpair_name)(nvpair_t *);
-};
-
-static struct zfs_symbols sym;
 void zfs_fini(void);
 
-#define DLSYM(handle, func)                                        \
-       do {                                                       \
-               sym.func = (typeof(sym.func))dlsym(handle, #func); \
-       } while(0)
-
-/* populate the symbol table after a successful call to dlopen() */
-static int zfs_populate_symbols(void)
-{
-       char *error;
-
-       dlerror(); /* Clear any existing error */
-
-       DLSYM(handle_libzfs, libzfs_init);
-#define libzfs_init (*sym.libzfs_init)
-       DLSYM(handle_libzfs, libzfs_fini);
-#define libzfs_fini (*sym.libzfs_fini)
-       DLSYM(handle_libzfs, libzfs_load_module);
-#define libzfs_load_module (*sym.libzfs_load_module)
-       DLSYM(handle_libzfs, zfs_open);
-#define zfs_open (*sym.zfs_open)
-       DLSYM(handle_libzfs, zfs_destroy);
-#define zfs_destroy (*sym.zfs_destroy)
-       DLSYM(handle_libzfs, zfs_close);
-#define zfs_close (*sym.zfs_close)
-       DLSYM(handle_libzfs, zfs_prop_set);
-#define zfs_prop_set (*sym.zfs_prop_set)
-       DLSYM(handle_libzfs, zfs_get_user_props);
-#define zfs_get_user_props (*sym.zfs_get_user_props)
-       DLSYM(handle_libzfs, zfs_name_valid);
-#define zfs_name_valid (*sym.zfs_name_valid)
-       DLSYM(handle_libzfs, zpool_open);
-#define zpool_open (*sym.zpool_open)
-       DLSYM(handle_libzfs, zpool_close);
-#define zpool_close (*sym.zpool_close)
-       DLSYM(handle_nvpair, nvlist_lookup_string);
-#define nvlist_lookup_string (*sym.nvlist_lookup_string)
-       DLSYM(handle_nvpair, nvlist_lookup_nvlist);
-#define nvlist_lookup_nvlist (*sym.nvlist_lookup_nvlist)
-       DLSYM(handle_nvpair, nvlist_next_nvpair);
-#define nvlist_next_nvpair (*sym.nvlist_next_nvpair)
-       DLSYM(handle_nvpair, nvpair_name);
-#define nvpair_name (*sym.nvpair_name)
-
-       error = dlerror();
-       if (error != NULL) {
-               fatal();
-               fprintf(stderr, "%s\n", error);
-               return EINVAL;
-       }
-       return 0;
-}
-
 static int zfs_set_prop_int(zfs_handle_t *zhp, char *prop, void *val)
 {
        char str[64];
@@ -211,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_MAXNAMELEN];
+       char propname[ZFS_MAXPROPLEN];
        int ret = 0;
 
        params_dup = strdup(params);
@@ -233,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);
        }
@@ -251,6 +233,62 @@ static int zfs_set_prop_params(zfs_handle_t *zhp, char *params)
        return ret;
 }
 
+static int zfs_check_hostid(struct mkfs_opts *mop)
+{
+       FILE *f;
+       unsigned long hostid;
+       int rc;
+
+       if (strstr(mop->mo_ldd.ldd_params, PARAM_FAILNODE) == NULL)
+               return 0;
+
+       f = fopen("/sys/module/spl/parameters/spl_hostid", "r");
+       if (f == NULL) {
+               fatal();
+               fprintf(stderr, "Failed to open spl_hostid: %s\n",
+                       strerror(errno));
+               return errno;
+       }
+       rc = fscanf(f, "%li", &hostid);
+       fclose(f);
+       if (rc != 1) {
+               fatal();
+               fprintf(stderr, "Failed to read spl_hostid: %d\n", rc);
+               return rc;
+       }
+
+       if (hostid != 0)
+               return 0;
+
+       f = fopen(HOSTID_PATH, "r");
+       if (f == NULL)
+               goto out;
+
+       rc = fread(&hostid, sizeof(uint32_t), 1, f);
+       fclose(f);
+
+       if (rc != 1) {
+               fprintf(stderr, "Failed to read "HOSTID_PATH": %d\n",
+                      rc);
+               hostid = 0;
+       }
+
+out:
+       if (hostid == 0) {
+               if (mop->mo_flags & MO_NOHOSTID_CHECK) {
+                       fprintf(stderr, "WARNING: spl_hostid not set. ZFS has "
+                               "no zpool import protection\n");
+               } else {
+                       fatal();
+                       fprintf(stderr, "spl_hostid not set. See %s(8)",
+                               progname);
+                       return EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int osd_check_zfs_setup(void)
 {
        if (osd_zfs_setup == 0) {
@@ -272,7 +310,7 @@ int zfs_write_ldd(struct mkfs_opts *mop)
        int i, ret = EINVAL;
 
        if (osd_check_zfs_setup() == 0)
-               return EINVAL;
+               goto out;
 
        zhp = zfs_open(g_zfs, ds, ZFS_TYPE_FILESYSTEM);
        if (zhp == NULL) {
@@ -280,8 +318,16 @@ int zfs_write_ldd(struct mkfs_opts *mop)
                goto out;
        }
 
+       ret = zfs_check_hostid(mop);
+       if (ret != 0)
+               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,
@@ -290,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;
@@ -350,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_MAXNAMELEN];
-       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);
@@ -379,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;
 }
 
@@ -404,8 +455,11 @@ int zfs_read_ldd(char *ds,  struct lustre_disk_data *ldd)
                return EINVAL;
 
        zhp = zfs_open(g_zfs, ds, ZFS_TYPE_FILESYSTEM);
-       if (zhp == NULL)
-               goto out;
+       if (!zhp) {
+               zhp = zfs_open(g_zfs, ds, ZFS_TYPE_SNAPSHOT);
+               if (!zhp)
+                       goto out;
+       }
 
        for (i = 0; special_ldd_prop_params[i].zlpb_prop_name != NULL; i++) {
                bridge = &special_ldd_prop_params[i];
@@ -415,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;
 
@@ -427,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;
@@ -471,7 +561,7 @@ static int zfs_create_vdev(struct mkfs_opts *mop, char *vdev)
 
        /*
         * Verify a file exists at the provided absolute path.  If it doesn't
-        * and mo_device_sz is set attempt to create a file vdev to be used.
+        * and mo_device_kb is set attempt to create a file vdev to be used.
         * Relative paths will be passed directly to 'zpool create' which
         * will check multiple multiple locations under /dev/.
         */
@@ -488,14 +578,14 @@ static int zfs_create_vdev(struct mkfs_opts *mop, char *vdev)
                        return ret;
                }
 
-               if (mop->mo_device_sz == 0) {
+               if (mop->mo_device_kb == 0) {
                        fatal();
                        fprintf(stderr, "Unable to create vdev due to "
                                "missing --device-size=#N(KB) parameter\n");
                        return EINVAL;
                }
 
-               ret = file_create(vdev, mop->mo_device_sz);
+               ret = file_create(vdev, mop->mo_device_kb);
                if (ret) {
                        fatal();
                        fprintf(stderr, "Unable to create vdev %s (%d)\n",
@@ -528,6 +618,10 @@ int zfs_make_lustre(struct mkfs_opts *mop)
                return EINVAL;
        }
 
+       ret = zfs_check_hostid(mop);
+       if (ret != 0)
+               goto out;
+
        pool = strdup(ds);
        if (pool == NULL)
                return ENOMEM;
@@ -603,15 +697,30 @@ int zfs_make_lustre(struct mkfs_opts *mop)
        }
 
        /*
+        * Set Options on ZPOOL
+        *
+        * ALL   - canmount=off
+        * 0.7.0 - multihost=on
+        */
+       php = zpool_open(g_zfs, pool);
+       if (php) {
+               if (pool_exists)
+                       zpool_set_prop(php, "canmount", "off");
+
+               zpool_set_prop(php, "multihost", "on");
+
+               zpool_close(php);
+       }
+
+       /*
         * Create the ZFS filesystem with any required mkfs options:
         * - canmount=off is set to prevent zfs automounting
-        * - version=4 is set because SA are not yet handled by the osd
+        * - xattr=sa is set to use system attribute based xattrs
         */
        memset(mkfs_cmd, 0, PATH_MAX);
        snprintf(mkfs_cmd, PATH_MAX,
                 "zfs create -o canmount=off -o xattr=sa%s %s",
-                zfs_mkfs_opts(mop, mkfs_tmp, PATH_MAX),
-                ds);
+                zfs_mkfs_opts(mop, mkfs_tmp, PATH_MAX), ds);
 
        vprint("mkfs_cmd = %s\n", mkfs_cmd);
        ret = run_command(mkfs_cmd, PATH_MAX);
@@ -635,9 +744,14 @@ out:
        return ret;
 }
 
+int zfs_enable_quota(struct mkfs_opts *mop)
+{
+       fprintf(stderr, "this option is not only valid for zfs\n");
+       return ENOSYS;
+}
+
 int zfs_prepare_lustre(struct mkfs_opts *mop,
-               char *default_mountopts, int default_len,
-               char *always_mountopts, int always_len)
+                      char *wanted_mountopts, size_t len)
 {
        if (osd_check_zfs_setup() == 0)
                return EINVAL;
@@ -684,43 +798,93 @@ int zfs_label_lustre(struct mount_opts *mop)
        return ret;
 }
 
-int zfs_init(void)
+int zfs_rename_fsname(struct mkfs_opts *mop, const char *oldname)
 {
-       int ret = 0;
-
-       /* If the ZFS libs are not installed, don't print an error to avoid
-        * spamming ldiskfs users. An error message will still be printed if
-        * someone tries to do some real work involving a ZFS backend */
+       struct mount_opts opts;
+       char mntpt[] = "/tmp/mntXXXXXX";
+       char *cmd_buf;
+       int ret;
 
-       handle_libzfs = dlopen("libzfs.so", RTLD_LAZY);
-       if (handle_libzfs == NULL)
-               return EINVAL;
+       /* Change the filesystem label. */
+       opts.mo_ldd = mop->mo_ldd;
+       opts.mo_source = mop->mo_device;
+       ret = zfs_label_lustre(&opts);
+       if (ret) {
+               if (errno != 0)
+                       ret = errno;
+               fprintf(stderr, "Can't change filesystem label: %s\n",
+                       strerror(ret));
+               return ret;
+       }
 
-       handle_nvpair = dlopen("libnvpair.so", RTLD_LAZY);
-       if (handle_nvpair == NULL) {
-               ret = EINVAL;
-               goto out;
+       /* Mount this device temporarily in order to write these files */
+       if (mkdtemp(mntpt) == NULL) {
+               if (errno != 0)
+                       ret = errno;
+               fprintf(stderr, "Can't create temp mount point %s: %s\n",
+                       mntpt, strerror(ret));
+               return ret;
        }
 
-       ret = zfs_populate_symbols();
-       if (ret)
-               goto out;
+       cmd_buf = malloc(PATH_MAX);
+       if (!cmd_buf) {
+               ret = ENOMEM;
+               goto out_rmdir;
+       }
 
-       if (libzfs_load_module("zfs") != 0) {
-               /* The ZFS modules are not installed */
-               ret = EINVAL;
-               goto out;
+       memset(cmd_buf, 0, PATH_MAX);
+       snprintf(cmd_buf, PATH_MAX - 1, "zfs set mountpoint=%s %s && "
+                "zfs set canmount=on %s && zfs mount %s",
+                mntpt, mop->mo_device, mop->mo_device, mop->mo_device);
+       ret = run_command(cmd_buf, PATH_MAX);
+       if (ret) {
+               if (errno != 0)
+                       ret = errno;
+               fprintf(stderr, "Unable to mount %s (%s)\n",
+                       mop->mo_device, strerror(ret));
+               if (ret == ENODEV)
+                       fprintf(stderr, "Is the %s module available?\n",
+                               MT_STR(&mop->mo_ldd));
+               goto out_free;
        }
 
+       ret = lustre_rename_fsname(mop, mntpt, oldname);
+       memset(cmd_buf, 0, PATH_MAX);
+       snprintf(cmd_buf, PATH_MAX - 1, "zfs umount %s && "
+                "zfs set canmount=off %s && zfs set mountpoint=none %s",
+                mop->mo_device, mop->mo_device, mop->mo_device);
+       run_command(cmd_buf, PATH_MAX);
+
+out_free:
+       free(cmd_buf);
+out_rmdir:
+       rmdir(mntpt);
+       return ret;
+}
+
+int zfs_init(void)
+{
+       int ret = 0;
+
        g_zfs = libzfs_init();
        if (g_zfs == NULL) {
-               fprintf(stderr, "Failed to initialize ZFS library\n");
-               ret = EINVAL;
+               /* Try to load zfs.ko and retry libzfs_init() */
+
+               ret = system("/sbin/modprobe -q zfs");
+
+               if (ret == 0) {
+                       g_zfs = libzfs_init();
+                       if (g_zfs == NULL)
+                               ret = EINVAL;
+               }
        }
-out:
-       osd_zfs_setup = 1;
-       if (ret)
-               zfs_fini();
+
+       if (ret == 0)
+               osd_zfs_setup = 1;
+
+       else
+               fprintf(stderr, "Failed to initialize ZFS library: %d\n", ret);
+
        return ret;
 }
 
@@ -730,14 +894,5 @@ void zfs_fini(void)
                libzfs_fini(g_zfs);
                g_zfs = NULL;
        }
-       if (handle_nvpair) {
-               dlclose(handle_nvpair);
-               handle_nvpair = NULL;
-       }
-       if (handle_libzfs) {
-               dlclose(handle_libzfs);
-               handle_libzfs = NULL;
-       }
-
        osd_zfs_setup = 0;
 }