@@ -200,6 +200,12 @@ enum initialized_states {
* mapped by some multipath map because of map reload failure.
*/
INIT_REMOVED,
+ /*
+ * INIT_PARTIAL: paths added by update_pathvec_from_dm() will not
+ * be fully initialized. This will be handled when an add or
+ * change uevent is received.
+ */
+ INIT_PARTIAL,
};
enum prkey_sources {
@@ -194,6 +194,7 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
}
condlog(2, "%s: adding new path %s",
mpp->alias, pp->dev);
+ pp->initialized = INIT_PARTIAL;
store_path(pathvec, pp);
pp->tick = 1;
}
@@ -392,12 +393,12 @@ extract_hwe_from_path(struct multipath * mpp)
condlog(4, "%s: searching paths for valid hwe", mpp->alias);
/* doing this in two passes seems like paranoia to me */
vector_foreach_slot(mpp->paths, pp, i) {
- if (pp->state == PATH_UP &&
+ if (pp->state == PATH_UP && pp->initialized != INIT_PARTIAL &&
pp->initialized != INIT_REMOVED && pp->hwe)
goto done;
}
vector_foreach_slot(mpp->paths, pp, i) {
- if (pp->state != PATH_UP &&
+ if ((pp->state != PATH_UP || pp->initialized == INIT_PARTIAL) &&
pp->initialized != INIT_REMOVED && pp->hwe)
goto done;
}
@@ -493,6 +493,33 @@ cli_reset_map_stats (void *v, struct strbuf *reply, void *data)
return 0;
}
+static int
+add_partial_path(struct path *pp, struct vectors *vecs)
+{
+ char wwid[WWID_SIZE];
+ struct udev_device *udd;
+
+ udd = get_udev_device(pp->dev_t, DEV_DEVT);
+ if (!udd)
+ return 0;
+ strcpy(wwid, pp->wwid);
+ if (get_uid(pp, pp->state, udd, 0) != 0) {
+ strcpy(pp->wwid, wwid);
+ udev_device_unref(udd);
+ return 0;
+ }
+ if (strlen(wwid) && strncmp(wwid, pp->wwid, WWID_SIZE) != 0) {
+ condlog(0, "%s: path wwid changed from '%s' to '%s'. removing",
+ pp->dev, wwid, pp->wwid);
+ ev_remove_path(pp, vecs, 1);
+ udev_device_unref(udd);
+ return -1;
+ }
+ udev_device_unref(pp->udev);
+ pp->udev = udd;
+ return finish_path_init(pp, vecs);
+}
+
static int
cli_add_path (void *v, struct strbuf *reply, void *data)
{
@@ -518,8 +545,12 @@ cli_add_path (void *v, struct strbuf *reply, void *data)
if (pp && pp->initialized != INIT_REMOVED) {
condlog(2, "%s: path already in pathvec", param);
- if (pp->recheck_wwid == RECHECK_WWID_ON &&
- check_path_wwid_change(pp)) {
+ if (pp->initialized == INIT_PARTIAL) {
+ if (add_partial_path(pp, vecs) < 0)
+ return 1;
+ }
+ else if (pp->recheck_wwid == RECHECK_WWID_ON &&
+ check_path_wwid_change(pp)) {
condlog(0, "%s: wwid changed. Removing device",
pp->dev);
handle_path_wwid_change(pp, vecs);
@@ -977,12 +977,19 @@ check_path_wwid_change(struct path *pp)
return false;
}
+/*
+ * uev_add_path can call uev_update_path, and uev_update_path can call
+ * uev_add_path
+ */
+static int uev_update_path (struct uevent *uev, struct vectors * vecs);
+
static int
uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
{
struct path *pp;
int ret = 0, i;
struct config *conf;
+ bool partial_init = false;
condlog(3, "%s: add path (uevent)", uev->kernel);
if (strstr(uev->kernel, "..") != NULL) {
@@ -1001,7 +1008,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
int r;
struct multipath *prev_mpp = NULL;
- if (pp->initialized == INIT_REMOVED) {
+ if (pp->initialized == INIT_PARTIAL) {
+ partial_init = true;
+ goto out;
+ } else if (pp->initialized == INIT_REMOVED) {
condlog(3, "%s: re-adding removed path", pp->dev);
pp->initialized = INIT_NEW;
prev_mpp = pp->mpp;
@@ -1111,6 +1121,8 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
}
out:
lock_cleanup_pop(vecs->lock);
+ if (partial_init)
+ return uev_update_path(uev, vecs);
return ret;
}
@@ -1406,6 +1418,28 @@ fail:
return REMOVE_PATH_MAP_ERROR;
}
+int
+finish_path_init(struct path *pp, struct vectors * vecs)
+{
+ int r;
+ struct config *conf;
+
+ if (pp->udev && pp->uid_attribute && *pp->uid_attribute &&
+ !udev_device_get_is_initialized(pp->udev))
+ return 0;
+ conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config, conf);
+ r = pathinfo(pp, conf, DI_ALL|DI_BLACKLIST);
+ pthread_cleanup_pop(1);
+
+ if (r == PATHINFO_OK)
+ return 0;
+
+ condlog(0, "%s: error fully initializing path, removing", pp->dev);
+ ev_remove_path(pp, vecs, 1);
+ return -1;
+}
+
static int
uev_update_path (struct uevent *uev, struct vectors * vecs)
{
@@ -1444,7 +1478,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
}
/* Don't deal with other types of failed initialization
* now. check_path will handle it */
- if (!strlen(pp->wwid))
+ if (!strlen(pp->wwid) && pp->initialized != INIT_PARTIAL)
goto out;
strcpy(wwid, pp->wwid);
@@ -1452,12 +1486,20 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
if (rc != 0)
strcpy(pp->wwid, wwid);
- else if (strncmp(wwid, pp->wwid, WWID_SIZE) != 0) {
+ else if (strlen(wwid) &&
+ strncmp(wwid, pp->wwid, WWID_SIZE) != 0) {
condlog(0, "%s: path wwid changed from '%s' to '%s'",
uev->kernel, wwid, pp->wwid);
ev_remove_path(pp, vecs, 1);
needs_reinit = 1;
goto out;
+ } else if (pp->initialized == INIT_PARTIAL) {
+ udev_device_unref(pp->udev);
+ pp->udev = udev_device_ref(uev->udev);
+ if (finish_path_init(pp, vecs) < 0) {
+ retval = 1;
+ goto out;
+ }
} else {
udev_device_unref(pp->udev);
pp->udev = udev_device_ref(uev->udev);
@@ -1508,6 +1550,7 @@ out:
condlog(0, "%s: spurious uevent, path not found", uev->kernel);
}
+ /* pp->initalized must not be INIT_PARTIAL if needs_reinit is set */
if (needs_reinit)
retval = uev_add_path(uev, vecs, 1);
return retval;
@@ -2117,7 +2160,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
int marginal_pathgroups, marginal_changed = 0;
int ret;
- if (((pp->initialized == INIT_OK ||
+ if (((pp->initialized == INIT_OK || pp->initialized == INIT_PARTIAL ||
pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) ||
pp->initialized == INIT_REMOVED)
return 0;
@@ -65,4 +65,5 @@ int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
bool check_path_wwid_change(struct path *pp);
+int finish_path_init(struct path *pp, struct vectors * vecs);
#endif /* MAIN_H */