[multipathd] add "show wildcards" cli command
[platform/upstream/multipath-tools.git] / multipathd / main.c
index 0a4759f..84fb5c4 100644 (file)
 #include <sys/types.h>
 #include <fcntl.h>
 #include <errno.h>
-
-/*
- * libsysfs
- */
-#include <sysfs/libsysfs.h>
-#include <sysfs/dlist.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 
 /*
  * libcheckers
  */
 #include <checkers.h>
-#include <path_state.h>
 
 /*
  * libmultipath
 #include <vector.h>
 #include <memory.h>
 #include <config.h>
-#include <callout.h>
 #include <util.h>
-#include <blacklist.h>
 #include <hwtable.h>
 #include <defaults.h>
 #include <structs.h>
+#include <callout.h>
+#include <blacklist.h>
 #include <structs_vec.h>
 #include <dmparser.h>
 #include <devmapper.h>
+#include <sysfs.h>
 #include <dict.h>
 #include <discovery.h>
 #include <debug.h>
 #include <propsel.h>
 #include <uevent.h>
 #include <switchgroup.h>
-#include <path_state.h>
 #include <print.h>
 #include <configure.h>
+#include <prio.h>
 
 #include "main.h"
 #include "pidfile.h"
 #include "uxclnt.h"
 #include "cli.h"
 #include "cli_handlers.h"
+#include "lock.h"
+#include "waiter.h"
 
 #define FILE_NAME_SIZE 256
 #define CMDSIZE 160
 
 #define LOG_MSG(a,b) \
-       if (strlen(b)) { \
-               condlog(a, "%s: %s", pp->dev_t, b); \
-               memset(b, 0, MAX_CHECKER_MSG_SIZE); \
-       }
-
-#ifdef LCKDBG
-#define lock(a) \
-       fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
-       pthread_mutex_lock(a)
-#define unlock(a) \
-       fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
-       pthread_mutex_unlock(a)
-#define lock_cleanup_pop(a) \
-       fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
-       pthread_cleanup_pop(1);
-#else
-#define lock(a) pthread_mutex_lock(a)
-#define unlock(a) pthread_mutex_unlock(a)
-#define lock_cleanup_pop(a) pthread_cleanup_pop(1);
-#endif
+       if (strlen(b)) condlog(a, "%s: %s", pp->dev, b);
 
 pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
 pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-/*
- * structs
- */
-struct vectors * gvecs; /* global copy of vecs for use in sig handlers */
-
-static struct event_thread *
-alloc_waiter (void)
-{
-
-       struct event_thread * wp;
-
-       wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
-
-       return wp;
-}
-
-static void
-free_waiter (void * data)
-{
-       struct event_thread * wp = (struct event_thread *)data;
-
-       /*
-        * indicate in mpp that the wp is already freed storage
-        */
-       lock(wp->vecs->lock);
-
-       if (wp->mpp)
-               /*
-                * be careful, mpp may already be freed -- null if so
-                */
-               wp->mpp->waiter = NULL;
-       else
-               condlog(3, "free_waiter, mpp freed before wp=%p,", wp);
-
-       unlock(wp->vecs->lock);
-
-       if (wp->dmt)
-               dm_task_destroy(wp->dmt);
-
-       FREE(wp);
-}
-
-static void
-stop_waiter_thread (struct multipath * mpp, struct vectors * vecs)
-{
-       struct event_thread * wp = (struct event_thread *)mpp->waiter;
-       
-       if (!wp) {
-               condlog(3, "%s: no waiter thread", mpp->alias);
-               return;
-       }
-       condlog(2, "%s: stop event checker thread", wp->mapname);
-       pthread_kill((pthread_t)wp->thread, SIGUSR1);
-}
-
-static void
-cleanup_lock (void * data)
-{
-       unlock((pthread_mutex_t *)data);
-}
+int logsink;
 
 /*
- * mpp->no_path_retry:
- *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
- *   -1 (FAIL)  : fail_if_no_path
- *    0 (UNDEF) : nothing
- *   >0         : queue_if_no_path enabled, turned off after polling n times
+ * global copy of vecs for use in sig handlers
  */
-static void
-update_queue_mode_del_path(struct multipath *mpp)
-{
-       if (--mpp->nr_active == 0 && mpp->no_path_retry > 0) {
-               /*
-                * Enter retry mode.
-                * meaning of +1: retry_tick may be decremented in
-                *                checkerloop before starting retry.
-                */
-               mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
-               condlog(1, "%s: Entering recovery mode: max_retries=%d",
-                       mpp->alias, mpp->no_path_retry);
-       }
-       condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
-}
-
-static void
-update_queue_mode_add_path(struct multipath *mpp)
-{
-       if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
-               /* come back to normal mode from retry mode */
-               mpp->retry_tick = 0;
-               dm_queue_if_no_path(mpp->alias, 1);
-               condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
-               condlog(1, "%s: Recovered to normal mode", mpp->alias);
-       }
-       condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
-}
+struct vectors * gvecs;
 
 static int
 need_switch_pathgroup (struct multipath * mpp, int refresh)
 {
        struct pathgroup * pgp;
        struct path * pp;
-       int i, j;
+       unsigned int i, j;
 
        if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL)
                return 0;
@@ -212,6 +101,7 @@ need_switch_pathgroup (struct multipath * mpp, int refresh)
 static void
 switch_pathgroup (struct multipath * mpp)
 {
+       mpp->stat_switchgroup++;
        dm_switchgroup(mpp->alias, mpp->bestpg);
        condlog(2, "%s: switch to path group #%i",
                 mpp->alias, mpp->bestpg);
@@ -222,10 +112,8 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
 {
        struct multipath * ompp;
        vector ompv = vecs->mpvec;
-       int i, j;
-
-       condlog(3, "coalesce_maps vs = %u", VECTOR_SIZE(ompv));
-       condlog(3, "coalesce_maps vs = %u", VECTOR_SIZE(nmpv));
+       unsigned int i;
+       int j;
 
        vector_foreach_slot (ompv, ompp, i) {
                if (!find_mp_by_wwid(nmpv, ompp->wwid)) {
@@ -233,7 +121,7 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
                         * remove all current maps not allowed by the
                         * current configuration
                         */
-                       if (dm_flush_map(ompp->alias, DEFAULT_TARGET)) {
+                       if (dm_flush_map(ompp->alias)) {
                                condlog(0, "%s: unable to flush devmap",
                                        ompp->alias);
                                /*
@@ -250,346 +138,224 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
 
                                continue;
                        }
-                       else
-                               condlog(3, "%s devmap removed", ompp->alias);
+                       else {
+                               dm_lib_release();
+                               condlog(2, "%s devmap removed", ompp->alias);
+                       }
                }
        }
        return 0;
 }
 
-static int
-update_multipath (struct vectors *vecs, char *mapname)
+static void
+sync_map_state(struct multipath *mpp)
 {
-       struct multipath *mpp;
-       struct pathgroup  *pgp;
+       struct pathgroup *pgp;
        struct path *pp;
-       int i, j;
-       int r = 1;
-
-       mpp = find_mp_by_alias(vecs->mpvec, mapname);
-
-       if (!mpp)
-               goto out;
+       unsigned int i, j;
 
-       free_pgvec(mpp->pg, KEEP_PATHS);
-       mpp->pg = NULL;
-
-       if (setup_multipath(vecs, mpp))
-               goto out; /* mpp freed in setup_multipath */
+       if (!mpp->pg)
+               return;
 
-       /*
-        * compare checkers states with DM states
-        */
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               vector_foreach_slot (pgp->paths, pp, j) {
-                       if (pp->dmstate != PSTATE_FAILED)
+       vector_foreach_slot (mpp->pg, pgp, i){
+               vector_foreach_slot (pgp->paths, pp, j){
+                       if (pp->state == PATH_UNCHECKED || 
+                           pp->state == PATH_WILD)
                                continue;
-
-                       if (pp->state != PATH_DOWN) {
-                               condlog(2, "%s: mark as failed", pp->dev_t);
-                               pp->state = PATH_DOWN;
-                               update_queue_mode_del_path(mpp);
-
-                               /*
-                                * if opportune,
-                                * schedule the next check earlier
-                                */
-                               if (pp->tick > conf->checkint)
-                                       pp->tick = conf->checkint;
-                       }
+                       if ((pp->dmstate == PSTATE_FAILED ||
+                            pp->dmstate == PSTATE_UNDEF) &&
+                           (pp->state == PATH_UP || pp->state == PATH_GHOST))
+                               dm_reinstate_path(mpp->alias, pp->dev_t);
+                       else if ((pp->dmstate == PSTATE_ACTIVE ||
+                                 pp->dmstate == PSTATE_UNDEF) &&
+                                (pp->state == PATH_DOWN ||
+                                 pp->state == PATH_SHAKY))
+                               dm_fail_path(mpp->alias, pp->dev_t);
                }
        }
-       r = 0;
-out:
-       if (r)
-               condlog(0, "failed to update multipath");
-
-       return r;
-}
-
-static sigset_t unblock_signals(void)
-{
-       sigset_t set, old;
-
-       sigemptyset(&set);
-       sigaddset(&set, SIGHUP);
-       sigaddset(&set, SIGUSR1);
-       pthread_sigmask(SIG_UNBLOCK, &set, &old);
-       return old;
-}
-
-/*
- * returns the reschedule delay
- * negative means *stop*
- */
-static int
-waiteventloop (struct event_thread * waiter)
-{
-       sigset_t set;
-       int event_nr;
-       int r;
-
-       if (!waiter->event_nr)
-               waiter->event_nr = dm_geteventnr(waiter->mapname);
-
-       if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT))) {
-               condlog(0, "%s: devmap event #%i dm_task_create error",
-                               waiter->mapname, waiter->event_nr);
-               return 1;
-       }
-
-       if (!dm_task_set_name(waiter->dmt, waiter->mapname)) {
-               condlog(0, "%s: devmap event #%i dm_task_set_name error",
-                               waiter->mapname, waiter->event_nr);
-               dm_task_destroy(waiter->dmt);
-               return 1;
-       }
-
-       if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
-                                                     waiter->event_nr)) {
-               condlog(0, "%s: devmap event #%i dm_task_set_event_nr error",
-                               waiter->mapname, waiter->event_nr);
-               dm_task_destroy(waiter->dmt);
-               return 1;
-       }
-
-       dm_task_no_open_count(waiter->dmt);
-       
-       /* accept wait interruption */
-       set = unblock_signals();
-
-       /* interruption spits messages */
-       dm_shut_log();
-
-       /* wait */
-       r = dm_task_run(waiter->dmt);
-
-       /* wait is over : event or interrupt */
-       pthread_sigmask(SIG_SETMASK, &set, NULL);
-       //dm_restore_log();
-
-       if (!r) /* wait interrupted by signal */
-               return -1;
-
-       dm_task_destroy(waiter->dmt);
-       waiter->dmt = NULL;
-       waiter->event_nr++;
-
-       /*
-        * upon event ...
-        */
-       while (1) {
-               condlog(3, "%s: devmap event #%i",
-                               waiter->mapname, waiter->event_nr);
-
-               /*
-                * event might be :
-                *
-                * 1) a table reload, which means our mpp structure is
-                *    obsolete : refresh it through update_multipath()
-                * 2) a path failed by DM : mark as such through
-                *    update_multipath()
-                * 3) map has gone away : stop the thread.
-                * 4) a path reinstate : nothing to do
-                * 5) a switch group : nothing to do
-                */
-               pthread_cleanup_push(cleanup_lock, waiter->vecs->lock);
-               lock(waiter->vecs->lock);
-               r = update_multipath(waiter->vecs, waiter->mapname);
-               lock_cleanup_pop(waiter->vecs->lock);
-
-               if (r)
-                       return -1; /* stop the thread */
-
-               event_nr = dm_geteventnr(waiter->mapname);
-
-               if (waiter->event_nr == event_nr)
-                       return 1; /* upon problem reschedule 1s later */
-
-               waiter->event_nr = event_nr;
-       }
-       return -1; /* never reach there */
 }
 
-static void *
-waitevent (void * et)
-{
-       int r;
-       struct event_thread *waiter;
-
-       mlockall(MCL_CURRENT | MCL_FUTURE);
-
-       waiter = (struct event_thread *)et;
-       pthread_cleanup_push(free_waiter, et);
-
-       while (1) {
-               r = waiteventloop(waiter);
-
-               if (r < 0)
-                       break;
-
-               sleep(r);
-       }
-
-       pthread_cleanup_pop(1);
-       return NULL;
-}
-
-static int
-start_waiter_thread (struct multipath * mpp, struct vectors * vecs)
+static void
+sync_maps_state(vector mpvec)
 {
-       pthread_attr_t attr;
-       struct event_thread * wp;
-
-       if (!mpp)
-               return 0;
-
-       if (pthread_attr_init(&attr))
-               goto out;
-
-       pthread_attr_setstacksize(&attr, 32 * 1024);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-       wp = alloc_waiter();
-
-       if (!wp)
-               goto out;
-
-       mpp->waiter = (void *)wp;
-       strncpy(wp->mapname, mpp->alias, WWID_SIZE);
-       wp->vecs = vecs;
-       wp->mpp = mpp;
-
-       if (pthread_create(&wp->thread, &attr, waitevent, wp)) {
-               condlog(0, "%s: cannot create event checker", wp->mapname);
-               goto out1;
-       }
-       condlog(2, "%s: event checker started", wp->mapname);
+       unsigned int i;
+       struct multipath *mpp;
 
-       return 0;
-out1:
-       free_waiter(wp);
-       mpp->waiter = NULL;
-out:
-       condlog(0, "failed to start waiter thread");
-       return 1;
+       vector_foreach_slot (mpvec, mpp, i)
+               sync_map_state(mpp);
 }
 
 static int
-flush_map(struct multipath * mpp, char * devname, struct vectors * vecs)
+flush_map(struct multipath * mpp, struct vectors * vecs)
 {
        /*
         * clear references to this map before flushing so we can ignore
         * the spurious uevent we may generate with the dm_flush_map call below
         */
-       if (dm_flush_map(mpp->alias, DEFAULT_TARGET)) {
+       if (dm_flush_map(mpp->alias)) {
                /*
                 * May not really be an error -- if the map was already flushed
                 * from the device mapper by dmsetup(8) for instance.
                 */
-               condlog(0, "%s: can't flush", devname);
+               condlog(0, "%s: can't flush", mpp->alias);
                return 1;
        }
-       else
-               condlog(3, "%s: devmap %s removed", devname, mpp->alias);
+       else {
+               dm_lib_release();
+               condlog(2, "%s: devmap removed", mpp->alias);
+       }
 
        orphan_paths(vecs->pathvec, mpp);
-       remove_map(mpp, vecs, stop_waiter_thread, 1);
+       remove_map_and_stop_waiter(mpp, vecs, 1);
 
        return 0;
 }
 
+static int
+uev_add_map (struct sysfs_device * dev, struct vectors * vecs)
+{
+       condlog(2, "%s: add map (uevent)", dev->kernel);
+       return ev_add_map(dev, vecs);
+}
+
 int
-uev_add_map (char * devname, struct vectors * vecs)
+ev_add_map (struct sysfs_device * dev, struct vectors * vecs)
 {
-       int major, minor;
-       char dev_t[BLK_DEV_SIZE];
        char * alias;
+       char *dev_t;
+       int major, minor;
+       char * refwwid;
        struct multipath * mpp;
+       int map_present;
+       int r = 1;
+
+       dev_t = sysfs_attr_get_value(dev->devpath, "dev");
+
+       if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2)
+               return 1;
+
+       alias = dm_mapname(major, minor);
 
-       if (sscanf(devname, "dm-%d", &minor) == 1 &&
-           !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) &&
-           sscanf(dev_t, "%d:%d", &major, &minor) == 2)
-               alias = dm_mapname(major, minor);
-       else
-               alias = STRDUP(devname);
-               
        if (!alias)
                return 1;
-       
-       if (dm_type(alias, DEFAULT_TARGET) <= 0) {
+
+       map_present = dm_map_present(alias);
+
+       if (map_present && dm_type(alias, TGT_MPATH) <= 0) {
                condlog(4, "%s: not a multipath map", alias);
-               FREE(alias);
                return 0;
        }
 
        mpp = find_mp_by_alias(vecs->mpvec, alias);
 
        if (mpp) {
-                /*
+               /*
                 * Not really an error -- we generate our own uevent
                 * if we create a multipath mapped device as a result
                 * of uev_add_path
                 */
-               condlog(0, "%s: spurious uevent, devmap already registered",
-                       devname);
-               FREE(alias);
+               condlog(0, "%s: devmap already registered",
+                       dev->kernel);
                return 0;
        }
 
        /*
         * now we can register the map
         */
-       if ((mpp = add_map_without_path(vecs, minor, alias,
-                                       start_waiter_thread))) {
-               condlog(3, "%s devmap %s added", devname, alias);
+       if (map_present && (mpp = add_map_without_path(vecs, minor, alias))) {
+               sync_map_state(mpp);
+               condlog(2, "%s: devmap %s added", alias, dev->kernel);
                return 0;
        }
+       refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec);
 
-       condlog(0, "%s: uev_add_map failed", alias);
-       return 1;
+       if (refwwid) {
+               r = coalesce_paths(vecs, NULL, refwwid, 0);
+               dm_lib_release();
+       }
+
+       if (!r)
+               condlog(2, "%s: devmap %s added", alias, dev->kernel);
+       else
+               condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel);
+
+       FREE(refwwid);
+       return r;
+}
+
+static int
+uev_remove_map (struct sysfs_device * dev, struct vectors * vecs)
+{
+       condlog(2, "%s: remove map (uevent)", dev->kernel);
+       return ev_remove_map(dev->kernel, vecs);
 }
 
 int
-uev_remove_map (char * devname, struct vectors * vecs)
+ev_remove_map (char * devname, struct vectors * vecs)
 {
-       int minor;
        struct multipath * mpp;
 
-       if (sscanf(devname, "dm-%d", &minor) == 1)
-               mpp = find_mp_by_minor(vecs->mpvec, minor);
-       else
-               mpp = find_mp_by_alias(vecs->mpvec, devname);
+       mpp = find_mp_by_str(vecs->mpvec, devname);
 
        if (!mpp) {
-               condlog(3, "%s: devmap not registered, can't remove",
+               condlog(2, "%s: devmap not registered, can't remove",
                        devname);
                return 0;
        }
+       flush_map(mpp, vecs);
 
-       condlog(2, "remove %s devmap", mpp->alias);
-       remove_map(mpp, vecs, stop_waiter_thread, 1);
+       return 0;
+}
+
+static int
+uev_umount_map (struct sysfs_device * dev, struct vectors * vecs)
+{
+       struct multipath * mpp;
+
+       condlog(2, "%s: umount map (uevent)", dev->kernel);
+
+       mpp = find_mp_by_str(vecs->mpvec, dev->kernel);
+
+       if (!mpp)
+               return 0;
+
+       update_mpp_paths(mpp, vecs->pathvec);
+       verify_paths(mpp, vecs, NULL);
+
+       if (!VECTOR_SIZE(mpp->paths))
+               flush_map(mpp, vecs);
 
        return 0;
 }
 
+static int
+uev_add_path (struct sysfs_device * dev, struct vectors * vecs)
+{
+       condlog(2, "%s: add path (uevent)", dev->kernel);
+       return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1;
+}
+
+
+/*
+ * returns:
+ * 0: added
+ * 1: error
+ * 2: blacklisted
+ */
 int
-uev_add_path (char * devname, struct vectors * vecs)
+ev_add_path (char * devname, struct vectors * vecs)
 {
        struct multipath * mpp;
        struct path * pp;
        char empty_buff[WWID_SIZE] = {0};
 
-       condlog(3, "%s: uev_add_path", devname);
-
        pp = find_path_by_dev(vecs->pathvec, devname);
+
        if (pp) {
-               condlog(0, "%s: spurious uevent, path already in pathvec, %p",
-                       devname, pp->mpp);
-               /*
-                * allow reconfig of orphaned path here
-                */
-               if (pp->mpp) return 1;
+               condlog(0, "%s: spurious uevent, path already in pathvec",
+                       devname);
+               if (pp->mpp)
+                       return 0;
        }
        else {
                /*
@@ -610,7 +376,13 @@ uev_add_path (char * devname, struct vectors * vecs)
                condlog(0, "%s: failed to get path uid", devname);
                return 1; /* leave path added to pathvec */
        }
-
+       if (filter_path(conf, pp) > 0){
+               int i = find_slot(vecs->pathvec, (void *)pp);
+               if (i != -1)
+                       vector_del_slot(vecs->pathvec, i);
+               free_path(pp);
+               return 2;
+       }
        mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);
 rescan:
        if (mpp) {
@@ -618,7 +390,6 @@ rescan:
                        return 1; /* leave path added to pathvec */
 
                verify_paths(mpp, vecs, NULL);
-               condlog(0, "%s: ownership set to %s", pp->dev_t, mpp->alias);
                mpp->action = ACT_RELOAD;
        }
        else {
@@ -643,17 +414,18 @@ rescan:
                condlog(0, "%s: failed in domap for addition of new "
                        "path %s", mpp->alias, devname);
                /*
-                * deal with asynchronous uevents :((
-                */
+                * deal with asynchronous uevents :((
+                */
                if (mpp->action == ACT_RELOAD) {
                        condlog(0, "%s: uev_add_path sleep", mpp->alias);
                        sleep(1);
-                       update_mpp_paths(mpp);
+                       update_mpp_paths(mpp, vecs->pathvec);
                        goto rescan;
                }
                else
                        goto out;
        }
+       dm_lib_release();
 
        /*
         * update our state from kernel regardless of create or reload
@@ -661,266 +433,133 @@ rescan:
        if (setup_multipath(vecs, mpp))
                goto out;
 
+       sync_map_state(mpp);
+
        if (mpp->action == ACT_CREATE &&
            start_waiter_thread(mpp, vecs))
                        goto out;
 
-       condlog(3, "%s path added to devmap %s", devname, mpp->alias);
+       condlog(2, "%s path added to devmap %s", devname, mpp->alias);
        return 0;
 
 out:
-       remove_map(mpp, vecs, NULL, 1);
+       remove_map(mpp, vecs, 1);
        return 1;
 }
 
+static int
+uev_remove_path (struct sysfs_device * dev, struct vectors * vecs)
+{
+       int retval;
+
+       condlog(2, "%s: remove path (uevent)", dev->kernel);
+       retval = ev_remove_path(dev->kernel, vecs);
+       if (!retval)
+               sysfs_device_put(dev);
+
+       return retval;
+}
+
 int
-uev_remove_path (char * devname, struct vectors * vecs)
+ev_remove_path (char * devname, struct vectors * vecs)
 {
        struct multipath * mpp;
        struct path * pp;
-       int redo, i;
-       int rm_path = 1;
-
-       condlog(3, "%s: uev_remove_path", devname);
+       int i, retval = 0;
 
        pp = find_path_by_dev(vecs->pathvec, devname);
+
        if (!pp) {
                condlog(0, "%s: spurious uevent, path not in pathvec", devname);
                return 1;
        }
 
        /*
-        * avoid referring to the map of an orphanned path
+        * avoid referring to the map of an orphaned path
         */
        if ((mpp = pp->mpp)) {
+               /*
+                * transform the mp->pg vector of vectors of paths
+                * into a mp->params string to feed the device-mapper
+                */
+               if (update_mpp_paths(mpp, vecs->pathvec)) {
+                       condlog(0, "%s: failed to update paths",
+                               mpp->alias);
+                       goto out;
+               }
+               if ((i = find_slot(mpp->paths, (void *)pp)) != -1)
+                       vector_del_slot(mpp->paths, i);
 
                /*
                 * remove the map IFF removing the last path
                 */
-               if (pathcount(mpp, PATH_WILD) > 1) {
-                       vector rpvec = vector_alloc();
-                       struct path * mypp;
-                       int j;
-
-                       /*
-                        * transform the mp->pg vector of vectors of paths
-                        * into a mp->params string to feed the device-mapper
-                        */
-                       update_mpp_paths(mpp);
-                       if ((i = find_slot(mpp->paths, (void *)pp)) != -1)
-                               vector_del_slot(mpp->paths, i);
-
-rescan:
-                       redo = verify_paths(mpp, vecs, rpvec);
-                       if (VECTOR_SIZE(mpp->paths) == 0) {
-                               char alias[WWID_SIZE];
-
-                               /*
-                                * flush_map will fail if the device is open
-                                */
-                               strncpy(alias, mpp->alias, WWID_SIZE);
-                               if (flush_map(mpp, devname, vecs)) {
-                                       rm_path = 0;
-                                       vector_foreach_slot(rpvec, mypp, i)
-                                               if (store_path(mpp->paths, mypp))
-                                                       goto out;       
-                               }
-                               else {
-                                       vector_foreach_slot(rpvec, mypp, i) {
-                                               if ((j = find_slot(vecs->pathvec,
-                                                                  (void *)mypp)) != -1) {
-                                                       vector_del_slot(vecs->pathvec, j);
-                                                       free_path(mypp);
-                                               }
-                                       }
-                                       condlog(3, "%s: removed map after removing"
-                                               " multiple paths", alias);
-                               }
-                       }
-                       else {
-                               vector_foreach_slot(rpvec, mypp, i) {
-                                       if ((j = find_slot(vecs->pathvec,
-                                                                  (void *)mypp)) != -1) {
-                                               vector_del_slot(vecs->pathvec, j);
-                                               free_path(mypp);
-                                       }
-                               }
-                                       
-                               if (setup_map(mpp)) {
-                                       condlog(0, "%s: failed to setup map for"
-                                               " removal of path %s", mpp->alias, devname);
-                                       free_pathvec(rpvec, KEEP_PATHS);
-                                       goto out;
-                               }
-                               /*
-                                * reload the map
-                                */
-                               mpp->action = ACT_RELOAD;
-                               if (domap(mpp) <= 0) {
-                                       condlog(0, "%s: failed in domap for "
-                                               "removal of path %s",
-                                               mpp->alias, devname);
-                                       /*
-                                        * deal with asynchronous uevents :((
-                                        */
-                                       if (!redo) {
-                                               condlog(3, "%s: uev_remove_path sleep",
-                                                       mpp->alias);
-                                               sleep(1);
-                                       }
-                                       update_mpp_paths(mpp);
-                                       free_pathvec(rpvec, KEEP_PATHS);
-                                       rpvec = vector_alloc();
-                                       goto rescan;
-                               }
-                               /*
-                                * update our state from kernel
-                                */
-                               if (setup_multipath(vecs, mpp)) {
-                                       free_pathvec(rpvec, KEEP_PATHS);
-                                       goto out;
-                               }
-
-                               condlog(3, "%s path removed from devmap %s",
-                                       devname, mpp->alias);
-                       }
-                       free_pathvec(rpvec, KEEP_PATHS);
-               }
-               else {
+               if (VECTOR_SIZE(mpp->paths) == 0) {
                        char alias[WWID_SIZE];
 
                        /*
                         * flush_map will fail if the device is open
                         */
                        strncpy(alias, mpp->alias, WWID_SIZE);
-                       if (flush_map(mpp, devname, vecs)) {
-                               rm_path = 0;
+                       if (!flush_map(mpp, vecs)) {
+                               condlog(2, "%s: removed map after"
+                                       " removing all paths",
+                                       alias);
+                               free_path(pp);
+                               return 0;
                        }
-                       else
-                               condlog(3, "%s: removed map", alias);
+                       /*
+                        * Not an error, continue
+                        */
                }
-       }
-
-       if (rm_path) {
-               if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
-                       vector_del_slot(vecs->pathvec, i);
-               free_path(pp);
-       }
-
-       return 0;
-
-out:
-       remove_map(mpp, vecs, stop_waiter_thread, 1);
-       return 1;
-}
-
-int
-show_paths (char ** r, int * len, struct vectors * vecs)
-{
-       int i;
-       struct path * pp;
-       char * c;
-       char * reply;
-       int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
-
-       get_path_layout(vecs->pathvec);
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-
-               if (VECTOR_SIZE(vecs->pathvec) > 0)
-                       c += snprint_path_header(c, reply + maxlen - c,
-                                                PRINT_PATH_CHECKER);
-
-               vector_foreach_slot(vecs->pathvec, pp, i)
-                       c += snprint_path(c, reply + maxlen - c,
-                                         PRINT_PATH_CHECKER, pp);
-
-               again = ((c - reply) == (maxlen - 1));
-
-               if (again)
-                       reply = REALLOC(reply, maxlen *= 2);
-
-       }
-       *r = reply;
-       *len = (int)(c - reply + 1);
-       return 0;
-}
-
-int
-show_maps (char ** r, int *len, struct vectors * vecs)
-{
-       int i;
-       struct multipath * mpp;
-       char * c;
-       char * reply;
-       int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
-
-       get_map_layout(vecs->mpvec);
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-               if (VECTOR_SIZE(vecs->mpvec) > 0)
-                       c += snprint_map_header(c, reply + maxlen - c,
-                                               PRINT_MAP_FAILBACK);
-
-               vector_foreach_slot(vecs->mpvec, mpp, i)
-                       c += snprint_map(c, reply + maxlen - c,
-                                        PRINT_MAP_FAILBACK, mpp);
 
-               again = ((c - reply) == (maxlen - 1));
+               if (setup_map(mpp)) {
+                       condlog(0, "%s: failed to setup map for"
+                               " removal of path %s", mpp->alias,
+                               devname);
+                       goto out;
+               }
+               /*
+                * reload the map
+                */
+               mpp->action = ACT_RELOAD;
+               if (domap(mpp) <= 0) {
+                       condlog(0, "%s: failed in domap for "
+                               "removal of path %s",
+                               mpp->alias, devname);
+                       retval = 1;
+               } else {
+                       /*
+                        * update our state from kernel
+                        */
+                       if (setup_multipath(vecs, mpp)) {
+                               goto out;
+                       }
+                       sync_map_state(mpp);
 
-               if (again)
-                       reply = REALLOC(reply, maxlen *= 2);
+                       condlog(2, "%s: path removed from map %s",
+                               devname, mpp->alias);
+               }
        }
-       *r = reply;
-       *len = (int)(c - reply + 1);
-       return 0;
-}
 
-int
-dump_pathvec (char ** r, int * len, struct vectors * vecs)
-{
-       int i;
-       struct path * pp;
-       char * reply;
-       char * p;
+       if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
+               vector_del_slot(vecs->pathvec, i);
 
-       *len = VECTOR_SIZE(vecs->pathvec) * sizeof(struct path);
-       reply = (char *)MALLOC(*len);
-       *r = reply;
+       free_path(pp);
 
-       if (!reply)
-               return 1;
-
-       p = reply;
+       return retval;
 
-       vector_foreach_slot (vecs->pathvec, pp, i) {
-               memcpy((void *)p, pp, sizeof(struct path));
-               p += sizeof(struct path);
-       }
-
-       /* return negative to hint caller not to add "ok" to the dump */
-       return -1;
+out:
+       remove_map_and_stop_waiter(mpp, vecs, 1);
+       return 1;
 }
 
 static int
 map_discovery (struct vectors * vecs)
 {
-       int i;
        struct multipath * mpp;
+       unsigned int i;
 
-       if (dm_get_maps(vecs->mpvec, "multipath"))
+       if (dm_get_maps(vecs->mpvec))
                return 1;
 
        vector_foreach_slot (vecs->mpvec, mpp, i)
@@ -935,7 +574,7 @@ uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
 {
        struct vectors * vecs;
        int r;
-       
+
        *reply = NULL;
        *len = 0;
        vecs = (struct vectors *)trigger_data;
@@ -977,11 +616,11 @@ uev_discard(char * devpath)
        return 0;
 }
 
-int 
+int
 uev_trigger (struct uevent * uev, void * trigger_data)
 {
        int r = 0;
-       char devname[32];
+       struct sysfs_device *sysdev;
        struct vectors * vecs;
 
        vecs = (struct vectors *)trigger_data;
@@ -989,36 +628,46 @@ uev_trigger (struct uevent * uev, void * trigger_data)
        if (uev_discard(uev->devpath))
                return 0;
 
-       basename(uev->devpath, devname);
+       sysdev = sysfs_device_get(uev->devpath);
+       if(!sysdev)
+               return 0;
+
        lock(vecs->lock);
 
        /*
-        * device map add/remove event
+        * device map event
+        * Add events are ignored here as the tables
+        * are not fully initialised then.
         */
-       if (!strncmp(devname, "dm-", 3)) {
-               if (!strncmp(uev->action, "add", 3)) {
-                       r = uev_add_map(devname, vecs);
+       if (!strncmp(sysdev->kernel, "dm-", 3)) {
+               if (!strncmp(uev->action, "change", 6)) {
+                       r = uev_add_map(sysdev, vecs);
                        goto out;
                }
                if (!strncmp(uev->action, "remove", 6)) {
-                       r = uev_remove_map(devname, vecs);
+                       r = uev_remove_map(sysdev, vecs);
+                       goto out;
+               }
+               if (!strncmp(uev->action, "umount", 6)) {
+                       r = uev_umount_map(sysdev, vecs);
                        goto out;
                }
                goto out;
        }
-       
+
        /*
         * path add/remove event
         */
-       if (blacklist(conf->blist, devname))
+       if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
+                          sysdev->kernel) > 0)
                goto out;
 
        if (!strncmp(uev->action, "add", 3)) {
-               r = uev_add_path(devname, vecs);
+               r = uev_add_path(sysdev, vecs);
                goto out;
        }
        if (!strncmp(uev->action, "remove", 6)) {
-               r = uev_remove_path(devname, vecs);
+               r = uev_remove_path(sysdev, vecs);
                goto out;
        }
 
@@ -1032,32 +681,39 @@ ueventloop (void * ap)
 {
        if (uevent_listen(&uev_trigger, ap))
                fprintf(stderr, "error starting uevent listener");
-               
+
        return NULL;
 }
 
 static void *
 uxlsnrloop (void * ap)
 {
-       if (load_keys())
-               return NULL;
-       
-       if (alloc_handlers())
+       if (cli_init())
                return NULL;
 
-       add_handler(LIST+PATHS, cli_list_paths);
-       add_handler(LIST+MAPS, cli_list_maps);
-       add_handler(ADD+PATH, cli_add_path);
-       add_handler(DEL+PATH, cli_del_path);
-       add_handler(ADD+MAP, cli_add_map);
-       add_handler(DEL+MAP, cli_del_map);
-       add_handler(SWITCH+MAP+GROUP, cli_switch_group);
-       add_handler(DUMP+PATHVEC, cli_dump_pathvec);
-       add_handler(RECONFIGURE, cli_reconfigure);
-       add_handler(SUSPEND+MAP, cli_suspend);
-       add_handler(RESUME+MAP, cli_resume);
-       add_handler(REINSTATE+PATH, cli_reinstate);
-       add_handler(FAIL+PATH, cli_fail);
+       set_handler_callback(LIST+PATHS, cli_list_paths);
+       set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
+       set_handler_callback(LIST+MAPS, cli_list_maps);
+       set_handler_callback(LIST+STATUS, cli_list_status);
+       set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
+       set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
+       set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
+       set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
+       set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
+       set_handler_callback(LIST+CONFIG, cli_list_config);
+       set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
+       set_handler_callback(LIST+DEVICES, cli_list_devices);
+       set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
+       set_handler_callback(ADD+PATH, cli_add_path);
+       set_handler_callback(DEL+PATH, cli_del_path);
+       set_handler_callback(ADD+MAP, cli_add_map);
+       set_handler_callback(DEL+MAP, cli_del_map);
+       set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
+       set_handler_callback(RECONFIGURE, cli_reconfigure);
+       set_handler_callback(SUSPEND+MAP, cli_suspend);
+       set_handler_callback(RESUME+MAP, cli_resume);
+       set_handler_callback(REINSTATE+PATH, cli_reinstate);
+       set_handler_callback(FAIL+PATH, cli_fail);
 
        uxsock_listen(&uxsock_trigger, ap);
 
@@ -1081,7 +737,7 @@ exit_daemon (int status)
 }
 
 static void
-fail_path (struct path * pp)
+fail_path (struct path * pp, int del_active)
 {
        if (!pp->mpp)
                return;
@@ -1090,14 +746,15 @@ fail_path (struct path * pp)
                 pp->dev_t, pp->mpp->alias);
 
        dm_fail_path(pp->mpp->alias, pp->dev_t);
-       update_queue_mode_del_path(pp->mpp);
+       if (del_active)
+               update_queue_mode_del_path(pp->mpp);
 }
 
 /*
  * caller must have locked the path list before calling that function
  */
 static void
-reinstate_path (struct path * pp)
+reinstate_path (struct path * pp, int add_active)
 {
        if (!pp->mpp)
                return;
@@ -1106,7 +763,8 @@ reinstate_path (struct path * pp)
                condlog(0, "%s: reinstate failed", pp->dev_t);
        else {
                condlog(2, "%s: reinstated", pp->dev_t);
-               update_queue_mode_add_path(pp->mpp);
+               if (add_active)
+                       update_queue_mode_add_path(pp->mpp);
        }
 }
 
@@ -1121,12 +779,12 @@ enable_group(struct path * pp)
         *
         * we can safely return here, because upon map reload, all
         * PG will be enabled.
-        */
+        */
        if (!pp->mpp->pg || !pp->pgindex)
                return;
 
        pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1);
-       
+
        if (pgp->status == PGSTATE_DISABLED) {
                condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex);
                dm_enablegroup(pp->mpp->alias, pp->pgindex);
@@ -1137,12 +795,12 @@ static void
 mpvec_garbage_collector (struct vectors * vecs)
 {
        struct multipath * mpp;
-       int i;
+       unsigned int i;
 
        vector_foreach_slot (vecs->mpvec, mpp, i) {
                if (mpp && mpp->alias && !dm_map_present(mpp->alias)) {
                        condlog(2, "%s: remove dead map", mpp->alias);
-                       remove_map(mpp, vecs, stop_waiter_thread, 1);
+                       remove_map_and_stop_waiter(mpp, vecs, 1);
                        i--;
                }
        }
@@ -1152,7 +810,7 @@ static void
 defered_failback_tick (vector mpvec)
 {
        struct multipath * mpp;
-       int i;
+       unsigned int i;
 
        vector_foreach_slot (mpvec, mpp, i) {
                /*
@@ -1171,10 +829,11 @@ static void
 retry_count_tick(vector mpvec)
 {
        struct multipath *mpp;
-       int i;
+       unsigned int i;
 
        vector_foreach_slot (mpvec, mpp, i) {
                if (mpp->retry_tick) {
+                       mpp->stat_total_queueing_time++;
                        condlog(4, "%s: Retrying.. No active path", mpp->alias);
                        if(--mpp->retry_tick == 0) {
                                dm_queue_if_no_path(mpp->alias, 0);
@@ -1184,150 +843,182 @@ retry_count_tick(vector mpvec)
        }
 }
 
-static void *
-checkerloop (void *ap)
+void
+check_path (struct vectors * vecs, struct path * pp)
 {
-       struct vectors *vecs;
-       struct path *pp;
-       int i, count = 0;
        int newstate;
-       char checker_msg[MAX_CHECKER_MSG_SIZE];
-
-       mlockall(MCL_CURRENT | MCL_FUTURE);
 
-       memset(checker_msg, 0, MAX_CHECKER_MSG_SIZE);
-       vecs = (struct vectors *)ap;
+       if (!pp->mpp)
+               return;
 
-       condlog(2, "path checkers start up");
+       if (pp->tick && --pp->tick)
+               return; /* don't check this path yet */
 
        /*
-        * init the path check interval
+        * provision a next check soonest,
+        * in case we exit abnormaly from here
         */
-       vector_foreach_slot (vecs->pathvec, pp, i) {
-               pp->checkint = conf->checkint;
+       pp->tick = conf->checkint;
+
+       if (!checker_selected(&pp->checker)) {
+               pathinfo(pp, conf->hwtable, DI_SYSFS);
+               select_checker(pp);
+       }
+       if (!checker_selected(&pp->checker)) {
+               condlog(0, "%s: checker is not set", pp->dev);
+               return;
        }
+       /*
+        * Set checker in async mode.
+        * Honored only by checker implementing the said mode.
+        */
+       checker_set_async(&pp->checker);
 
-       while (1) {
-               pthread_cleanup_push(cleanup_lock, vecs->lock);
-               lock(vecs->lock);
-               condlog(4, "tick");
+       newstate = checker_check(&pp->checker);
 
-               vector_foreach_slot (vecs->pathvec, pp, i) {
-                       if (!pp->mpp)
-                               continue;
+       if (newstate < 0) {
+               condlog(2, "%s: unusable path", pp->dev);
+               pathinfo(pp, conf->hwtable, 0);
+               return;
+       }
+       /*
+        * Async IO in flight. Keep the previous path state
+        * and reschedule as soon as possible
+        */
+       if (newstate == PATH_PENDING) {
+               pp->tick = 1;
+               return;
+       }
+       if (newstate != pp->state) {
+               int oldstate = pp->state;
+               pp->state = newstate;
+               LOG_MSG(1, checker_message(&pp->checker));
 
-                       if (pp->tick && --pp->tick)
-                               continue; /* don't check this path yet */
+               /*
+                * upon state change, reset the checkint
+                * to the shortest delay
+                */
+               pp->checkint = conf->checkint;
 
+               if (newstate == PATH_DOWN || newstate == PATH_SHAKY ||
+                   update_multipath_strings(pp->mpp, vecs->pathvec)) {
                        /*
-                        * provision a next check soonest,
-                        * in case we exit abnormaly from here
+                        * proactively fail path in the DM
                         */
-                       pp->tick = conf->checkint;
-                       
-                       if (!pp->checkfn) {
-                               pathinfo(pp, conf->hwtable, DI_SYSFS);
-                               select_checkfn(pp);
-                       }
+                       if (oldstate == PATH_UP ||
+                           oldstate == PATH_GHOST)
+                               fail_path(pp, 1);
+                       else
+                               fail_path(pp, 0);
 
-                       if (!pp->checkfn) {
-                               condlog(0, "%s: checkfn is void", pp->dev);
-                               continue;
-                       }
-                       newstate = pp->checkfn(pp->fd, checker_msg,
-                                              &pp->checker_context);
-                       
-                       if (newstate < 0) {
-                               condlog(2, "%s: unusable path", pp->dev);
-                               pathinfo(pp, conf->hwtable, 0);
-                               continue;
-                       }
+                       /*
+                        * cancel scheduled failback
+                        */
+                       pp->mpp->failback_tick = 0;
 
-                       if (newstate != pp->state) {
-                               pp->state = newstate;
-                               LOG_MSG(1, checker_msg);
+                       pp->mpp->stat_path_failures++;
+                       return;
+               }
 
-                               /*
-                                * upon state change, reset the checkint
-                                * to the shortest delay
-                                */
-                               pp->checkint = conf->checkint;
+               /*
+                * reinstate this path
+                */
+               if (oldstate != PATH_UP &&
+                   oldstate != PATH_GHOST)
+                       reinstate_path(pp, 1);
+               else
+                       reinstate_path(pp, 0);
 
-                               if (newstate == PATH_DOWN ||
-                                   newstate == PATH_SHAKY ||
-                                   update_multipath_strings(pp->mpp,
-                                                            vecs->pathvec)) {
-                                       /*
-                                        * proactively fail path in the DM
-                                        */
-                                       fail_path(pp);
+               /*
+                * schedule [defered] failback
+                */
+               if (pp->mpp->pgfailback > 0)
+                       pp->mpp->failback_tick =
+                               pp->mpp->pgfailback + 1;
+               else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE &&
+                   need_switch_pathgroup(pp->mpp, 1))
+                       switch_pathgroup(pp->mpp);
 
-                                       /*
-                                        * cancel scheduled failback
-                                        */
-                                       pp->mpp->failback_tick = 0;
+               /*
+                * if at least one path is up in a group, and
+                * the group is disabled, re-enable it
+                */
+               if (newstate == PATH_UP)
+                       enable_group(pp);
+       }
+       else if (newstate == PATH_UP || newstate == PATH_GHOST) {
+               LOG_MSG(4, checker_message(&pp->checker));
+               /*
+                * double the next check delay.
+                * max at conf->max_checkint
+                */
+               if (pp->checkint < (conf->max_checkint / 2))
+                       pp->checkint = 2 * pp->checkint;
+               else
+                       pp->checkint = conf->max_checkint;
 
-                                       continue;
-                               }
+               pp->tick = pp->checkint;
+               condlog(4, "%s: delay next check %is",
+                               pp->dev_t, pp->tick);
+       }
+       else if (newstate == PATH_DOWN)
+               LOG_MSG(2, checker_message(&pp->checker));
 
-                               /*
-                                * reinstate this path
-                                */
-                               reinstate_path(pp);
+       pp->state = newstate;
 
-                               /*
-                                * schedule [defered] failback
-                                */
-                               if (pp->mpp->pgfailback > 0)
-                                       pp->mpp->failback_tick =
-                                               pp->mpp->pgfailback + 1;
-                               else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE &&
-                                   need_switch_pathgroup(pp->mpp, 1))
-                                       switch_pathgroup(pp->mpp);
+       /*
+        * path prio refreshing
+        */
+       condlog(4, "path prio refresh");
+       pathinfo(pp, conf->hwtable, DI_PRIO);
 
-                               /*
-                                * if at least one path is up in a group, and
-                                * the group is disabled, re-enable it
-                                */
-                               if (newstate == PATH_UP)
-                                       enable_group(pp);
-                       }
-                       else if (newstate == PATH_UP || newstate == PATH_GHOST) {
-                               LOG_MSG(4, checker_msg);
-                               /*
-                                * double the next check delay.
-                                * max at conf->max_checkint
-                                */
-                               if (pp->checkint < (conf->max_checkint / 2))
-                                       pp->checkint = 2 * pp->checkint;
-                               else
-                                       pp->checkint = conf->max_checkint;
+       /*
+        * pathgroup failback policy
+        */
+       if (need_switch_pathgroup(pp->mpp, 0)) {
+               if (pp->mpp->pgfailback > 0 &&
+                   pp->mpp->failback_tick <= 0)
+                       pp->mpp->failback_tick =
+                               pp->mpp->pgfailback + 1;
+               else if (pp->mpp->pgfailback ==
+                               -FAILBACK_IMMEDIATE)
+                       switch_pathgroup(pp->mpp);
+       }
+}
 
-                               pp->tick = pp->checkint;
-                               condlog(4, "%s: delay next check %is",
-                                               pp->dev_t, pp->tick);
+static void *
+checkerloop (void *ap)
+{
+       struct vectors *vecs;
+       struct path *pp;
+       int count = 0;
+       unsigned int i;
 
-                       }
-                       pp->state = newstate;
+       mlockall(MCL_CURRENT | MCL_FUTURE);
+       vecs = (struct vectors *)ap;
+       condlog(2, "path checkers start up");
 
-                       /*
-                        * path prio refreshing
-                        */
-                       condlog(4, "path prio refresh");
-                       pathinfo(pp, conf->hwtable, DI_PRIO);
-
-                       if (need_switch_pathgroup(pp->mpp, 0)) {
-                               if (pp->mpp->pgfailback > 0)
-                                       pp->mpp->failback_tick =
-                                               pp->mpp->pgfailback + 1;
-                               else if (pp->mpp->pgfailback ==
-                                               -FAILBACK_IMMEDIATE)
-                                       switch_pathgroup(pp->mpp);
+       /*
+        * init the path check interval
+        */
+       vector_foreach_slot (vecs->pathvec, pp, i) {
+               pp->checkint = conf->checkint;
+       }
+
+       while (1) {
+               pthread_cleanup_push(cleanup_lock, vecs->lock);
+               lock(vecs->lock);
+               condlog(4, "tick");
+
+               if (vecs->pathvec) {
+                       vector_foreach_slot (vecs->pathvec, pp, i) {
+                               check_path(vecs, pp);
                        }
                }
-               defered_failback_tick(vecs->mpvec);
-               retry_count_tick(vecs->mpvec);
-
+               if (vecs->mpvec) {
+                       defered_failback_tick(vecs->mpvec);
+                       retry_count_tick(vecs->mpvec);
+               }
                if (count)
                        count--;
                else {
@@ -1335,7 +1026,7 @@ checkerloop (void *ap)
                        mpvec_garbage_collector(vecs);
                        count = MAPGCINT;
                }
-               
+
                lock_cleanup_pop(vecs->lock);
                sleep(1);
        }
@@ -1350,12 +1041,12 @@ configure (struct vectors * vecs, int start_waiters)
        vector mpvec;
        int i;
 
-       if (!(vecs->pathvec = vector_alloc()))
+       if (!vecs->pathvec && !(vecs->pathvec = vector_alloc()))
                return 1;
-       
-       if (!(vecs->mpvec = vector_alloc()))
+
+       if (!vecs->mpvec && !(vecs->mpvec = vector_alloc()))
                return 1;
-       
+
        if (!(mpvec = vector_alloc()))
                return 1;
 
@@ -1364,16 +1055,22 @@ configure (struct vectors * vecs, int start_waiters)
         */
        path_discovery(vecs->pathvec, conf, DI_ALL);
 
-       vector_foreach_slot (vecs->pathvec, pp, i)
-               pp->checkint = conf->checkint;
-
+       vector_foreach_slot (vecs->pathvec, pp, i){
+               if (filter_path(conf, pp) > 0){
+                       vector_del_slot(vecs->pathvec, i);
+                       free_path(pp);
+                       i--;
+               }
+               else
+                       pp->checkint = conf->checkint;
+       }
        if (map_discovery(vecs))
                return 1;
 
        /*
         * create new set of maps & push changed ones into dm
         */
-       if (coalesce_paths(vecs, mpvec))
+       if (coalesce_paths(vecs, mpvec, NULL, 0))
                return 1;
 
        /*
@@ -1383,18 +1080,19 @@ configure (struct vectors * vecs, int start_waiters)
        if (coalesce_maps(vecs, mpvec))
                return 1;
 
-       if (conf->verbosity > 2)
-               vector_foreach_slot(mpvec, mpp, i)
-                       print_map(mpp);
+       dm_lib_release();
+
+       sync_maps_state(mpvec);
 
        /*
         * purge dm of old maps
         */
-       remove_maps(vecs, NULL);
+       remove_maps(vecs);
 
        /*
         * save new set of maps formed by considering current path state
         */
+       vector_free(vecs->mpvec);
        vecs->mpvec = mpvec;
 
        /*
@@ -1415,17 +1113,16 @@ reconfigure (struct vectors * vecs)
 {
        struct config * old = conf;
 
-       condlog(0, "reconfigure");
-
        /*
         * free old map and path vectors ... they use old conf state
         */
        if (VECTOR_SIZE(vecs->mpvec))
-               remove_maps(vecs, stop_waiter_thread);
+               remove_maps_and_stop_waiters(vecs);
 
        if (VECTOR_SIZE(vecs->pathvec))
                free_pathvec(vecs->pathvec, FREE_PATHS);
 
+       vecs->pathvec = NULL;
        conf = NULL;
 
        if (load_config(DEFAULT_CONFIGFILE))
@@ -1434,8 +1131,8 @@ reconfigure (struct vectors * vecs)
        conf->verbosity = old->verbosity;
 
        if (!conf->checkint) {
-               conf->checkint = CHECKINT;
-               conf->max_checkint = MAX_CHECKINT;
+               conf->checkint = DEFAULT_CHECKINT;
+               conf->max_checkint = MAX_CHECKINT(conf->checkint);
        }
        configure(vecs, 1);
        free_config(old);
@@ -1452,30 +1149,16 @@ init_vecs (void)
        if (!vecs)
                return NULL;
 
-       vecs->lock = 
+       vecs->lock =
                (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
 
        if (!vecs->lock)
                goto out;
 
-       vecs->pathvec = vector_alloc();
-
-       if (!vecs->pathvec)
-               goto out1;
-               
-       vecs->mpvec = vector_alloc();
-
-       if (!vecs->mpvec)
-               goto out2;
-       
        pthread_mutex_init(vecs->lock, NULL);
 
        return vecs;
 
-out2:
-       vector_free(vecs->pathvec);
-out1:
-       FREE(vecs->lock);
 out:
        FREE(vecs);
        condlog(0, "failed to init paths");
@@ -1504,7 +1187,7 @@ signal_set(int signo, void (*func) (int))
 static void
 sighup (int sig)
 {
-       condlog(3, "SIGHUP received");
+       condlog(2, "reconfigure (SIGHUP)");
 
        lock(gvecs->lock);
        reconfigure(gvecs);
@@ -1534,21 +1217,21 @@ signal_init(void)
        signal_set(SIGUSR1, sigusr1);
        signal_set(SIGINT, sigend);
        signal_set(SIGTERM, sigend);
-       signal_set(SIGKILL, sigend);
+       signal(SIGPIPE, SIG_IGN);
 }
 
 static void
 setscheduler (void)
 {
-        int res;
+       int res;
        static struct sched_param sched_param = {
-               sched_priority: 99
+               .sched_priority = 99
        };
 
-        res = sched_setscheduler (0, SCHED_RR, &sched_param);
+       res = sched_setscheduler (0, SCHED_RR, &sched_param);
 
-        if (res == -1)
-                condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
+       if (res == -1)
+               condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
        return;
 }
 
@@ -1565,7 +1248,7 @@ set_oom_adj (int val)
        fprintf(fp, "%i", val);
        fclose(fp);
 }
-       
+
 static int
 child (void * param)
 {
@@ -1584,14 +1267,38 @@ child (void * param)
        if (load_config(DEFAULT_CONFIGFILE))
                exit(1);
 
+       if (init_checkers()) {
+               condlog(0, "failed to initialize checkers");
+               exit(1);
+       }
+       if (init_prio()) {
+               condlog(0, "failed to initialize prioritizers");
+               exit(1);
+       }
+
        setlogmask(LOG_UPTO(conf->verbosity + 3));
 
        /*
         * fill the voids left in the config file
         */
        if (!conf->checkint) {
-               conf->checkint = CHECKINT;
-               conf->max_checkint = MAX_CHECKINT;
+               conf->checkint = DEFAULT_CHECKINT;
+               conf->max_checkint = MAX_CHECKINT(conf->checkint);
+       }
+
+       if (conf->max_fds) {
+               struct rlimit fd_limit;
+               if (conf->max_fds > 0) {
+                       fd_limit.rlim_cur = conf->max_fds;
+                       fd_limit.rlim_max = conf->max_fds;
+               }
+               else {
+                       fd_limit.rlim_cur = RLIM_INFINITY;
+                       fd_limit.rlim_max = RLIM_INFINITY;
+               }
+               if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
+                       condlog(0, "can't set open fds limit to %d : %s\n",
+                               conf->max_fds, strerror(errno));
        }
 
        if (pidfile_create(DEFAULT_PIDFILE, getpid())) {
@@ -1602,13 +1309,13 @@ child (void * param)
        }
        signal_init();
        setscheduler();
-       set_oom_adj(-17);
+       set_oom_adj(-16);
        vecs = gvecs = init_vecs();
 
        if (!vecs)
                exit(1);
 
-       if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+       if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
                condlog(0, "can not find sysfs mount point");
                exit(1);
        }
@@ -1627,7 +1334,7 @@ child (void * param)
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, 64 * 1024);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-       
+
        pthread_create(&check_thr, &attr, checkerloop, vecs);
        pthread_create(&uevent_thr, &attr, ueventloop, vecs);
        pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, vecs);
@@ -1638,13 +1345,15 @@ child (void * param)
         * exit path
         */
        lock(vecs->lock);
-       remove_maps(vecs, stop_waiter_thread);
+       remove_maps_and_stop_waiters(vecs);
        free_pathvec(vecs->pathvec, FREE_PATHS);
 
        pthread_cancel(check_thr);
        pthread_cancel(uevent_thr);
        pthread_cancel(uxlsnr_thr);
 
+       sysfs_cleanup();
+
        free_keys(keys);
        keys = NULL;
        free_handlers(handlers);
@@ -1657,17 +1366,23 @@ child (void * param)
        vecs->lock = NULL;
        FREE(vecs);
        vecs = NULL;
-       free_config(conf);
-       conf = NULL;
 
        condlog(2, "--------shut down-------");
-       
+
        if (logsink)
                log_thread_stop();
 
        dm_lib_release();
        dm_lib_exit();
 
+       /*
+        * Freeing config must be done after condlog() and dm_lib_exit(),
+        * because logging functions like dlog() and dm_write_log()
+        * reference the config.
+        */
+       free_config(conf);
+       conf = NULL;
+
 #ifdef _DEBUG_
        dbg_free_final(NULL);
 #endif
@@ -1729,8 +1444,9 @@ main (int argc, char *argv[])
        extern int optind;
        int arg;
        int err;
-       
+
        logsink = 1;
+       dm_init();
 
        if (getuid() != 0) {
                fprintf(stderr, "need to be root\n");
@@ -1771,7 +1487,7 @@ main (int argc, char *argv[])
                err = 0;
        else
                err = daemonize();
-       
+
        if (err < 0)
                /* error */
                exit(1);