[dm-devel] [PATCH 56/57] multipathd: push down lock in checkerloop()
Benjamin Marzinski
bmarzins at redhat.com
Tue May 3 23:24:28 UTC 2016
On Tue, May 03, 2016 at 05:17:34PM -0500, Benjamin Marzinski wrote:
> On Wed, Apr 27, 2016 at 01:10:57PM +0200, Hannes Reinecke wrote:
> > Instead of grabbing the lock at the start of the checkerloop
> > and releasing it at the end we should be holding it only
> > during the time when we actually need it.
>
> I'm pretty sure that this can cause crashes if multipathd reconfigures
> when it's just started servicing a uevent. It goes like this. The
> uevq_thr thread calls uev_trigger(), which checks the running_state and
> sees that it's DAEMON_IDLE. The uxlsnr_thr thread gets a
> reconfiguration request, which eventually calls set_config_state().
> set_config_state() sees the state is DAEMON_IDLE, sets it to
> DAEMON_CONFIGURE, and returns without waiting. This wakes up the child
> thread, which runs reconfigure(). reconfigure() sets conf to NULL. Then
> the uevq_thr thread in uev_trigger() calls filter_devnode()
> dereferencing conf->blist_devnode and conf->elist_devnode. conf is NULL,
> and thus it crashes.
>
> The short version is that multipathd has been using the vecs lock to
> protect conf. With your change to uev_trigger, you access conf outside
> of the vecs lock. This isn't a problem in checkerloop(), since you can't
> reconfigure while multipathd is in the DAEMON_RUNNING state, so that is
> protecting conf. You need to do the same
> set_config_state(DAEMON_RUNNING) in uev_trigger to make this safe.
Or not. ev_add_map calls set_config_state(DAEMON_CONFIGURE), so this
would deadlock if you called set_config_state(DAEMON_RUNNING) in
uev_trigger. At any rate, either reconfigures need to be blocked while
running uev_trigger, or we need some sort of reference counting on the
config structure so that we don't free it when we do a reconfigure,
until everyone has switched to the new structure.
-Ben
>
> -Ben
>
> >
> > Signed-off-by: Hannes Reinecke <hare at suse.de>
> > ---
> > multipathd/main.c | 136 ++++++++++++++++++++++++++++++++++--------------------
> > 1 file changed, 87 insertions(+), 49 deletions(-)
> >
> > diff --git a/multipathd/main.c b/multipathd/main.c
> > index 41b5a49..132101f 100644
> > --- a/multipathd/main.c
> > +++ b/multipathd/main.c
> > @@ -416,7 +416,11 @@ uev_add_map (struct uevent * uev, struct vectors * vecs)
> > return 1;
> > }
> > }
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > rc = ev_add_map(uev->kernel, alias, vecs);
> > + lock_cleanup_pop(vecs->lock);
> > FREE(alias);
> > return rc;
> > }
> > @@ -512,6 +516,10 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs)
> > return 0;
> > }
> > minor = uevent_get_minor(uev);
> > +
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > mpp = find_mp_by_minor(vecs->mpvec, minor);
> >
> > if (!mpp) {
> > @@ -528,10 +536,12 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs)
> > orphan_paths(vecs->pathvec, mpp);
> > remove_map_and_stop_waiter(mpp, vecs, 1);
> > out:
> > + lock_cleanup_pop(vecs->lock);
> > FREE(alias);
> > return 0;
> > }
> >
> > +/* Called from CLI handler */
> > int
> > ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs)
> > {
> > @@ -567,6 +577,9 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
> > return 1;
> > }
> >
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > pp = find_path_by_dev(vecs->pathvec, uev->kernel);
> > if (pp) {
> > int r;
> > @@ -594,8 +607,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
> > ret = 1;
> > }
> > }
> > - return ret;
> > }
> > + lock_cleanup_pop(vecs->lock);
> > + if (pp)
> > + return ret;
> >
> > /*
> > * get path vital state
> > @@ -608,6 +623,9 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
> > condlog(3, "%s: failed to get path info", uev->kernel);
> > return 1;
> > }
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > ret = store_path(vecs->pathvec, pp);
> > if (!ret) {
> > pp->checkint = conf->checkint;
> > @@ -619,7 +637,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
> > free_path(pp);
> > ret = 1;
> > }
> > -
> > + lock_cleanup_pop(vecs->lock);
> > return ret;
> > }
> >
> > @@ -687,12 +705,12 @@ rescan:
> > */
> > start_waiter = 1;
> > }
> > - else
> > + if (!start_waiter)
> > goto fail; /* leave path added to pathvec */
> > }
> >
> > - /* persistent reseravtion check*/
> > - mpath_pr_event_handle(pp);
> > + /* persistent reservation check*/
> > + mpath_pr_event_handle(pp);
> >
> > /*
> > * push the map to the device-mapper
> > @@ -720,7 +738,7 @@ retry:
> > * deal with asynchronous uevents :((
> > */
> > if (mpp->action == ACT_RELOAD && retries-- > 0) {
> > - condlog(0, "%s: uev_add_path sleep", mpp->alias);
> > + condlog(0, "%s: ev_add_path sleep", mpp->alias);
> > sleep(1);
> > update_mpp_paths(mpp, vecs->pathvec);
> > goto rescan;
> > @@ -749,8 +767,7 @@ retry:
> > condlog(2, "%s [%s]: path added to devmap %s",
> > pp->dev, pp->dev_t, mpp->alias);
> > return 0;
> > - }
> > - else
> > + } else
> > goto fail;
> >
> > fail_map:
> > @@ -764,17 +781,22 @@ static int
> > uev_remove_path (struct uevent *uev, struct vectors * vecs)
> > {
> > struct path *pp;
> > + int ret;
> >
> > condlog(2, "%s: remove path (uevent)", uev->kernel);
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > pp = find_path_by_dev(vecs->pathvec, uev->kernel);
> > -
> > + if (pp)
> > + ret = ev_remove_path(pp, vecs);
> > + lock_cleanup_pop(vecs->lock);
> > if (!pp) {
> > /* Not an error; path might have been purged earlier */
> > condlog(0, "%s: path already removed", uev->kernel);
> > return 0;
> > }
> > -
> > - return ev_remove_path(pp, vecs);
> > + return ret;
> > }
> >
> > int
> > @@ -877,35 +899,50 @@ static int
> > uev_update_path (struct uevent *uev, struct vectors * vecs)
> > {
> > int ro, retval = 0;
> > - struct path * pp;
> > -
> > - pp = find_path_by_dev(vecs->pathvec, uev->kernel);
> > - if (!pp) {
> > - condlog(0, "%s: spurious uevent, path not found",
> > - uev->kernel);
> > - return 1;
> > - }
> > -
> > - if (pp->initialized == INIT_REQUESTED_UDEV)
> > - return uev_add_path(uev, vecs);
> >
> > ro = uevent_get_disk_ro(uev);
> >
> > if (ro >= 0) {
> > + struct path * pp;
> > + struct multipath *mpp = NULL;
> > +
> > condlog(2, "%s: update path write_protect to '%d' (uevent)",
> > uev->kernel, ro);
> > - if (pp->mpp) {
> > - if (pp->mpp->wait_for_udev) {
> > - pp->mpp->wait_for_udev = 2;
> > - return 0;
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > + /*
> > + * pthread_mutex_lock() and pthread_mutex_unlock()
> > + * need to be at the same indentation level, hence
> > + * this slightly convoluted codepath.
> > + */
> > + pp = find_path_by_dev(vecs->pathvec, uev->kernel);
> > + if (pp) {
> > + if (pp->initialized == INIT_REQUESTED_UDEV) {
> > + retval = 2;
> > + } else {
> > + mpp = pp->mpp;
> > + if (mpp && mpp->wait_for_udev) {
> > + mpp->wait_for_udev = 2;
> > + mpp = NULL;
> > + retval = 0;
> > + }
> > }
> > + if (mpp) {
> > + retval = reload_map(vecs, mpp, 0);
> >
> > - retval = reload_map(vecs, pp->mpp, 0);
> > -
> > - condlog(2, "%s: map %s reloaded (retval %d)",
> > - uev->kernel, pp->mpp->alias, retval);
> > + condlog(2, "%s: map %s reloaded (retval %d)",
> > + uev->kernel, mpp->alias, retval);
> > + }
> > }
> > -
> > + lock_cleanup_pop(vecs->lock);
> > + if (!pp) {
> > + condlog(0, "%s: spurious uevent, path not found",
> > + uev->kernel);
> > + return 1;
> > + }
> > + if (retval == 2)
> > + return uev_add_path(uev, vecs);
> > }
> >
> > return retval;
> > @@ -1002,10 +1039,6 @@ uev_trigger (struct uevent * uev, void * trigger_data)
> > if (running_state == DAEMON_SHUTDOWN)
> > return 0;
> >
> > - pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > - lock(vecs->lock);
> > - pthread_testcancel();
> > -
> > /*
> > * device map event
> > * Add events are ignored here as the tables
> > @@ -1044,7 +1077,6 @@ uev_trigger (struct uevent * uev, void * trigger_data)
> > }
> >
> > out:
> > - lock_cleanup_pop(vecs->lock);
> > return r;
> > }
> >
> > @@ -1627,17 +1659,6 @@ checkerloop (void *ap)
> >
> > if (gettimeofday(&start_time, NULL) != 0)
> > start_time.tv_sec = 0;
> > -
> > - rc = set_config_state(DAEMON_RUNNING);
> > - if (rc == ETIMEDOUT) {
> > - condlog(4, "timeout waiting for DAEMON_IDLE");
> > - continue;
> > - }
> > -
> > - pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > - lock(vecs->lock);
> > - pthread_testcancel();
> > - strict_timing = conf->strict_timing;
> > if (start_time.tv_sec && last_time.tv_sec) {
> > timersub(&start_time, &last_time, &diff_time);
> > condlog(4, "tick (%lu.%06lu secs)",
> > @@ -1653,28 +1674,45 @@ checkerloop (void *ap)
> > if (use_watchdog)
> > sd_notify(0, "WATCHDOG=1");
> > #endif
> > + rc = set_config_state(DAEMON_RUNNING);
> > + if (rc == ETIMEDOUT) {
> > + condlog(4, "timeout waiting for DAEMON_IDLE");
> > + continue;
> > + }
> > + strict_timing = conf->strict_timing;
> > if (vecs->pathvec) {
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > vector_foreach_slot (vecs->pathvec, pp, i) {
> > num_paths += check_path(vecs, pp, ticks);
> > }
> > + lock_cleanup_pop(vecs->lock);
> > }
> > if (vecs->mpvec) {
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > defered_failback_tick(vecs->mpvec);
> > retry_count_tick(vecs->mpvec);
> > missing_uev_wait_tick(vecs);
> > + lock_cleanup_pop(vecs->lock);
> > }
> > if (count)
> > count--;
> > else {
> > + pthread_cleanup_push(cleanup_lock, &vecs->lock);
> > + lock(vecs->lock);
> > + pthread_testcancel();
> > condlog(4, "map garbage collection");
> > mpvec_garbage_collector(vecs);
> > count = MAPGCINT;
> > + lock_cleanup_pop(vecs->lock);
> > }
> >
> > - lock_cleanup_pop(vecs->lock);
> > diff_time.tv_usec = 0;
> > if (start_time.tv_sec &&
> > - gettimeofday(&end_time, NULL)) {
> > + gettimeofday(&end_time, NULL) == 0) {
> > timersub(&end_time, &start_time, &diff_time);
> > if (num_paths) {
> > condlog(3, "checked %d path%s in %lu.%06lu secs",
> > --
> > 2.6.6
>
> --
> dm-devel mailing list
> dm-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/dm-devel
More information about the dm-devel
mailing list