Reassign existing device-mapper maps
authorHannes Reinecke <hare@suse.de>
Wed, 18 May 2011 12:02:00 +0000 (14:02 +0200)
committerHannes Reinecke <hare@suse.de>
Wed, 18 May 2011 12:07:52 +0000 (14:07 +0200)
When a multipath device is created other maps might already be
in place pointing to the same block device. To ensure uninterrupted
access these maps should be reassigned to point to the
multipath devices instead.
This patch also adds a configuration variable 'reassign_maps'
to toggle this behaviour.

Signed-off-by: Hannes Reinecke <hare@suse.de>
14 files changed:
libmultipath/config.c
libmultipath/config.h
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/sysfs.c
libmultipath/sysfs.h
multipath/multipath.conf.5
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/cli_handlers.h
multipathd/main.c

index 0fcd58d..5906a96 100644 (file)
@@ -495,6 +495,7 @@ load_config (char * file)
        conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
        conf->flush_on_last_del = 0;
        conf->attribute_flags = 0;
+       conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
 
        /*
         * preload default hwtable
index 8e08129..595aa79 100644 (file)
@@ -96,6 +96,7 @@ struct config {
        gid_t gid;
        mode_t mode;
        uint32_t cookie;
+       int reassign_maps;
 
        char * dev;
        char * sysfs_dir;
index 71aa6de..294646a 100644 (file)
@@ -14,6 +14,7 @@
 #define DEFAULT_PGTIMEOUT      -PGTIMEOUT_NONE
 #define DEFAULT_USER_FRIENDLY_NAMES    0
 #define DEFAULT_VERBOSITY      2
+#define DEFAULT_REASSIGN_MAPS  1
 
 #define DEFAULT_CHECKINT       5
 #define MAX_CHECKINT(a)                (a << 2)
index 5d126eb..a70ae0a 100644 (file)
@@ -20,6 +20,7 @@
 #include "memory.h"
 #include "devmapper.h"
 #include "config.h"
+#include "sysfs.h"
 
 #include "log_pthread.h"
 #include <sys/types.h>
@@ -1278,6 +1279,131 @@ out:
        return r;
 }
 
+void dm_reassign_deps(char *table, char *dep, char *newdep)
+{
+       char *p, *n;
+       char newtable[PARAMS_SIZE];
+
+       strcpy(newtable, table);
+       p = strstr(newtable, dep);
+       n = table + (p - newtable);
+       strcpy(n, newdep);
+       n += strlen(newdep);
+       p += strlen(dep);
+       strcat(n, p);
+}
+
+int dm_reassign_table(const char *name, char *old, char *new)
+{
+       int r, modified = 0;
+       uint64_t start, length;
+       struct dm_task *dmt, *reload_dmt;
+       char *target, *params = NULL;
+       char buff[PARAMS_SIZE];
+       void *next = NULL;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, name))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+       if (!(reload_dmt = dm_task_create(DM_DEVICE_RELOAD)))
+               goto out;
+       if (!dm_task_set_name(reload_dmt, name))
+               goto out_reload;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target, &params);
+               memset(buff, 0, PARAMS_SIZE);
+               strcpy(buff, params);
+               if (strcmp(target, TGT_MPATH) && strstr(params, old)) {
+                       condlog(3, "%s: replace target %s %s",
+                               name, target, buff);
+                       dm_reassign_deps(buff, old, new);
+                       condlog(3, "%s: with target %s %s",
+                               name, target, buff);
+                       modified++;
+               }
+               dm_task_add_target(reload_dmt, start, length, target, buff);
+       } while (next);
+
+       if (modified) {
+               dm_task_no_open_count(reload_dmt);
+
+               if (!dm_task_run(reload_dmt)) {
+                       condlog(3, "%s: failed to reassign targets", name);
+                       goto out_reload;
+               }
+               dm_simplecmd_noflush(DM_DEVICE_RESUME, name);
+       }
+       r = 1;
+
+out_reload:
+       dm_task_destroy(reload_dmt);
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+
+/*
+ * Reassign existing device-mapper table(s) to not use
+ * the block devices but point to the multipathed
+ * device instead
+ */
+int dm_reassign(const char *mapname)
+{
+       struct dm_deps *deps;
+       struct dm_task *dmt;
+       struct dm_info info;
+       char dev_t[32], dm_dep[32];
+       int r = 0, i;
+
+       if (dm_dev_t(mapname, &dev_t[0], 32)) {
+               condlog(3, "%s: failed to get device number\n", mapname);
+               return 1;
+       }
+
+       if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, mapname))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out;
+
+       if (!(deps = dm_task_get_deps(dmt)))
+               goto out;
+
+       if (!info.exists)
+               goto out;
+
+       for (i = 0; i < deps->count; i++) {
+               sprintf(dm_dep, "%d:%d",
+                       major(deps->device[i]),
+                       minor(deps->device[i]));
+               sysfs_check_holders(dm_dep, dev_t);
+       }
+
+       dm_task_destroy (dmt);
+
+       r = 1;
+out:
+       return r;
+}
+
 int dm_setgeometry(struct multipath *mpp)
 {
        struct dm_task *dmt;
index 47e4790..0c2e03f 100644 (file)
@@ -40,6 +40,8 @@ int dm_get_uuid(char *name, char *uuid);
 int dm_get_info (char * mapname, struct dm_info ** dmi);
 int dm_rename (char * old, char * new);
 char * dm_get_name(char * uuid);
+int dm_reassign(const char * mapname);
+int dm_reassign_table(const char *name, char *old, char *new);
 int dm_setgeometry(struct multipath *mpp);
 void udev_wait(unsigned int c);
 void udev_set_sync_support(int c);
index 8a5813c..641a6a6 100644 (file)
@@ -87,6 +87,22 @@ max_polling_interval_handler(vector strvec)
        return 0;
 }
 
+static int
+reassign_maps_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+       if (!strcmp(buff, "yes"))
+               conf->reassign_maps = 1;
+       else if (!strcmp(buff, "no"))
+               conf->reassign_maps = 0;
+       else
+               return 1;
+
+       return 0;
+}
+
 static int
 udev_dir_handler(vector strvec)
 {
@@ -2059,6 +2075,15 @@ snprint_def_max_polling_interval (char * buff, int len, void * data)
        return snprintf(buff, len, "%i", conf->max_checkint);
 }
 
+static int
+snprint_reassign_maps (char * buff, int len, void * data)
+{
+       if (conf->reassign_maps == DEFAULT_REASSIGN_MAPS)
+               return 0;
+       return snprintf(buff, len, "%s",
+                       conf->reassign_maps?"yes":"no");
+}
+
 static int
 snprint_def_udev_dir (char * buff, int len, void * data)
 {
@@ -2355,6 +2380,7 @@ init_keywords(void)
        install_keyword("verbosity", &verbosity_handler, &snprint_def_verbosity);
        install_keyword("polling_interval", &polling_interval_handler, &snprint_def_polling_interval);
        install_keyword("max_polling_interval", &max_polling_interval_handler, &snprint_def_max_polling_interval);
+       install_keyword("reassign_maps", &reassign_maps_handler, &snprint_reassign_maps);
        install_keyword("udev_dir", &udev_dir_handler, &snprint_def_udev_dir);
        install_keyword("multipath_dir", &multipath_dir_handler, &snprint_def_multipath_dir);
        install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
index 9a27e74..fc64881 100644 (file)
@@ -26,6 +26,7 @@
 #include <errno.h>
 #include <sys/stat.h>
 #include <string.h>
+#include <dirent.h>
 
 #include "checkers.h"
 #include "vector.h"
@@ -34,6 +35,7 @@
 #include "list.h"
 #include "util.h"
 #include "debug.h"
+#include "devmapper.h"
 
 char sysfs_path[PATH_SIZE];
 
@@ -390,3 +392,55 @@ out:
 
        return size;
 }
+
+int sysfs_check_holders(char * check_devt, char * new_devt)
+{
+       unsigned int major, new_minor, table_minor;
+       char path[PATH_SIZE], check_dev[PATH_SIZE];
+       char * table_name;
+       DIR *dirfd;
+       struct dirent *holder;
+
+       if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) {
+               condlog(1, "invalid device number %s", new_devt);
+               return 0;
+       }
+
+       if (devt2devname(check_dev, PATH_SIZE, check_devt))
+               return 0;
+
+       condlog(3, "%s: checking holder", check_dev);
+
+       snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev);
+       dirfd = opendir(path);
+       if (dirfd == NULL) {
+               condlog(3, "%s: failed to open directory %s (%d)",
+                       check_dev, path, errno);
+               return 0;
+       }
+       while ((holder = readdir(dirfd)) != NULL) {
+               if ((strcmp(holder->d_name,".") == 0) ||
+                   (strcmp(holder->d_name,"..") == 0))
+                       continue;
+
+               if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) {
+                       condlog(3, "%s: %s is not a dm-device",
+                               check_dev, holder->d_name);
+                       continue;
+               }
+               if (table_minor == new_minor) {
+                       condlog(3, "%s: holder already correct", check_dev);
+                       continue;
+               }
+               table_name = dm_mapname(major, table_minor);
+
+               condlog(3, "%s: reassign table %s old %s new %s", check_dev,
+                       table_name, check_devt, new_devt);
+
+               dm_reassign_table(table_name, check_devt, new_devt);
+               FREE(table_name);
+       }
+       closedir(dirfd);
+
+       return 0;
+}
index 7e8fff8..a84857d 100644 (file)
@@ -24,4 +24,5 @@ ssize_t sysfs_attr_set_value(const char *devpath, const char *attr_name,
                             const char *value, int value_len);
 int sysfs_resolve_link(char *path, size_t size);
 int sysfs_get_size (struct sysfs_device * dev, unsigned long long * size);
+int sysfs_check_holders(char * check_devt, char * new_devt);
 #endif
index 0fa0d31..a0bde42 100644 (file)
@@ -87,6 +87,13 @@ default verbosity. Higher values increase the verbosity level. Valid
 levels are between 0 and 6; default is
 .I 2
 .TP
+.B reassign_maps
+enable reassigning of device-mapper maps. With this option multipathd
+will remap existing device-mapper maps to always point to multipath
+device, not the underlying block devices. Possible values are
+\fIyes\fR and \fIno\fR. Default is
+.I yes
+.TP
 .B path_selector
 The default path selector algorithm to use; they are offered by the
 kernel multipath target. There are three selector algorithms.
index 4c90ef9..e4d766a 100644 (file)
@@ -160,6 +160,7 @@ load_keys (void)
        r += add_key(keys, "reinstate", REINSTATE, 0);
        r += add_key(keys, "fail", FAIL, 0);
        r += add_key(keys, "resize", RESIZE, 0);
+       r += add_key(keys, "reset", RESET, 0);
        r += add_key(keys, "disablequeueing", DISABLEQ, 0);
        r += add_key(keys, "restorequeueing", RESTOREQ, 0);
        r += add_key(keys, "paths", PATHS, 0);
@@ -441,6 +442,7 @@ cli_init (void) {
        add_handler(SUSPEND+MAP, NULL);
        add_handler(RESUME+MAP, NULL);
        add_handler(RESIZE+MAP, NULL);
+       add_handler(RESET+MAP, NULL);
        add_handler(DISABLEQ+MAP, NULL);
        add_handler(RESTOREQ+MAP, NULL);
        add_handler(DISABLEQ+MAPS, NULL);
index 7f03bf3..918800e 100644 (file)
@@ -8,6 +8,7 @@ enum {
        __REINSTATE,
        __FAIL,
        __RESIZE,
+       __RESET,
        __DISABLEQ,
        __RESTOREQ,
        __PATHS,
@@ -38,6 +39,7 @@ enum {
 #define REINSTATE      (1 << __REINSTATE)
 #define FAIL           (1 << __FAIL)
 #define RESIZE         (1 << __RESIZE)
+#define RESET          (1 << __RESET)
 #define DISABLEQ       (1 << __DISABLEQ)
 #define RESTOREQ       (1 << __RESTOREQ)
 #define PATHS          (1 << __PATHS)
index 31ca84a..ddb80b6 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "main.h"
 #include "cli.h"
+#include "uevent.h"
 
 #define REALLOC_REPLY(r, a, m)                                 \
        do {                                                    \
@@ -738,6 +739,17 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
        return dm_reinstate_path(pp->mpp->alias, pp->dev_t);
 }
 
+int
+cli_reassign (void * v, char ** reply, int * len, void * data)
+{
+       char * param = get_keyparam(v, MAP);
+
+       condlog(3, "%s: reset devices (operator)", param);
+
+       dm_reassign(param);
+       return 0;
+}
+
 int
 cli_fail(void * v, char ** reply, int * len, void * data)
 {
index eae308d..9ce5e65 100644 (file)
@@ -29,3 +29,4 @@ int cli_reinstate(void * v, char ** reply, int * len, void * data);
 int cli_fail(void * v, char ** reply, int * len, void * data);
 int cli_quit(void * v, char ** reply, int * len, void * data);
 int cli_shutdown(void * v, char ** reply, int * len, void * data);
+int cli_reassign (void * v, char ** reply, int * len, void * data);
index bd54a25..775ecd1 100644 (file)
@@ -147,6 +147,10 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
                                dm_lib_release();
                                condlog(2, "%s devmap removed", ompp->alias);
                        }
+               } else if (conf->reassign_maps) {
+                       condlog(3, "%s: Reassign existing device-mapper"
+                               " devices", ompp->alias);
+                       dm_reassign(ompp->alias);
                }
        }
        return 0;
@@ -263,7 +267,12 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs)
                 * if we create a multipath mapped device as a result
                 * of uev_add_path
                 */
-               condlog(0, "%s: devmap already registered", dev);
+               if (conf->reassign_maps) {
+                       condlog(3, "%s: Reassign existing device-mapper devices",
+                               alias);
+                       dm_reassign(alias);
+               }
+               FREE(alias);
                return 0;
        }
 
@@ -802,6 +811,7 @@ uxlsnrloop (void * ap)
        set_handler_callback(SUSPEND+MAP, cli_suspend);
        set_handler_callback(RESUME+MAP, cli_resume);
        set_handler_callback(RESIZE+MAP, cli_resize);
+       set_handler_callback(RESET+MAP, cli_reassign);
        set_handler_callback(REINSTATE+PATH, cli_reinstate);
        set_handler_callback(FAIL+PATH, cli_fail);
        set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);