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 0fcd58d89a9cdbcbf910583dce222a759b53d528..5906a96cbfdaab69ab0d3665daec2fe9fdc0fc3d 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 8e081292f077c0136d101a4df10168048d47126e..595aa794cb250a07ad21265c8e448c322c589789 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 71aa6ded6f120f5d4a814d34d74d74ea2efda004..294646a894ed9d996c98ec2fafc3a4216e6a7439 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 5d126eb9000975b4aa038f6b4c80084ec584cf15..a70ae0ae1d9292cf193562fcb14461c08c9093a2 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 47e47900a63b59d3d43b60359caab0498f854cd7..0c2e03fa967e381262bdd3123a5024172286967e 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 8a5813c575b9fe7ac48326cd02ee1b4cf4a8ff6d..641a6a63aa7e2c6c8bae29d26b00bbaa6af2e3b5 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 9a27e7457c96e7c79f5eff3cd2212d10611063a2..fc6488178e50e0c2bd24f01ac9dcf7fda1753992 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 7e8fff8043f1dc3e4ba41ef445779309277d8fc8..a84857dbd2a4276cf3a4ab789deda6ed70095aba 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 0fa0d3138236c13305065f926110d122a4bfa64b..a0bde42fb6896e4c70e86ec44b4f15b5d5a3eb90 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 4c90ef9ed6801380d98435bbcfb63ab44faa3683..e4d766aa4753bf2313380afce2328157b48043cc 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 7f03bf3fda4602b97fa2559d290f1b84d9dddd24..918800e75adb0983058bd2afcd00e0f1d2508b4f 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 31ca84ab377cfce1be3c473500f57c4be733b51e..ddb80b600cdfec3477db62c7a646c1e0b65789e8 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 eae308d51cb8d271344b5a0c268cc685f744c68c..9ce5e65cd48fdc6209ed91c07f46c1fbead37902 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 bd54a25499cf2a621485310da857ca98193d66f2..775ecd160a1f2f2d1fc386b8017ad580dbcc7d3f 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);