drbd: report congestion if we are waiting for some userland callback
authorLars Ellenberg <lars.ellenberg@linbit.com>
Thu, 14 Jun 2012 13:14:06 +0000 (15:14 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Tue, 24 Jul 2012 12:07:18 +0000 (14:07 +0200)
If the drbd worker thread is synchronously waiting for some userland
callback, we don't want some casual pageout to block on us.
Have drbd_congested() report congestion in that case.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_int.h
drivers/block/drbd/drbd_main.c
drivers/block/drbd/drbd_nl.c

index 79c69ebb0653eee884f91216026c16b975d960ed..5136510ec8be139c9acd198d19a5e190a2ea0039 100644 (file)
@@ -850,6 +850,13 @@ enum {
        AL_SUSPENDED,           /* Activity logging is currently suspended. */
        AHEAD_TO_SYNC_SOURCE,   /* Ahead -> SyncSource queued */
        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_bitmap; /* opaque for drbd_conf */
index 5bebe8d8ace31d3c613b529e80913ad4be4f6400..41ccb580d5ac81d311d102414439a4e1b66d70af 100644 (file)
@@ -3553,6 +3553,22 @@ static int drbd_congested(void *congested_data, int bdi_bits)
                goto out;
        }
 
+       if (test_bit(CALLBACK_PENDING, &mdev->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);
index 40a1c4f07190004488a2f2b4e172a4a933f7c11d..03fc853be2b91a1e7b70d266df3d2ceda1f5771b 100644 (file)
@@ -147,6 +147,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
        char *argv[] = {usermode_helper, cmd, mb, NULL };
        int ret;
 
+       if (current == mdev->worker.task)
+               set_bit(CALLBACK_PENDING, &mdev->flags);
+
        snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
 
        if (get_net_conf(mdev)) {
@@ -189,6 +192,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
                                usermode_helper, cmd, mb,
                                (ret >> 8) & 0xff, ret);
 
+       if (current == mdev->worker.task)
+               clear_bit(CALLBACK_PENDING, &mdev->flags);
+
        if (ret < 0) /* Ignore any ERRNOs we got. */
                ret = 0;