diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 963766bafab4..e010afff336a 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -815,6 +815,12 @@ enum { CONN_DRY_RUN, /* Expect disconnect after resync handshake. */ CREATE_BARRIER, /* next P_DATA is preceded by a P_BARRIER */ STATE_SENT, /* Do not change state/UUIDs while this is set */ + CALLBACK_PENDING, /* Whether we have a call_usermodehelper(, UMH_WAIT_PROC) + * pending, from drbd worker context. + * If set, bdi_write_congested() returns true, + * so shrink_page_list() would not recurse into, + * and potentially deadlock on, this drbd worker. + */ }; struct drbd_tconn { /* is a resource from the config file */ diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 849e5de9ea8f..f2af74d06860 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2338,6 +2338,22 @@ static int drbd_congested(void *congested_data, int bdi_bits) goto out; } + if (test_bit(CALLBACK_PENDING, &mdev->tconn->flags)) { + r |= (1 << BDI_async_congested); + /* Without good local data, we would need to read from remote, + * and that would need the worker thread as well, which is + * currently blocked waiting for that usermode helper to + * finish. + */ + if (!get_ldev_if_state(mdev, D_UP_TO_DATE)) + r |= (1 << BDI_sync_congested); + else + put_ldev(mdev); + r &= bdi_bits; + reason = 'c'; + goto out; + } + if (get_ldev(mdev)) { q = bdev_get_queue(mdev->ldev->backing_bdev); r = bdi_congested(&q->backing_dev_info, bdi_bits); diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index d4c05e26a13a..05ed4804c72c 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -323,11 +323,15 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd) NULL }; char mb[12]; char *argv[] = {usermode_helper, cmd, mb, NULL }; + struct drbd_tconn *tconn = mdev->tconn; struct sib_info sib; int ret; + if (current == tconn->worker.task) + set_bit(CALLBACK_PENDING, &tconn->flags); + snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev)); - setup_khelper_env(mdev->tconn, envp); + setup_khelper_env(tconn, envp); /* The helper may take some time. * write out any unsynced meta data changes now */ @@ -350,6 +354,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd) sib.helper_exit_code = ret; drbd_bcast_event(mdev, &sib); + if (current == tconn->worker.task) + clear_bit(CALLBACK_PENDING, &tconn->flags); + if (ret < 0) /* Ignore any ERRNOs we got. */ ret = 0;