drbd: report congestion if we are waiting for some userland callback
authorLars Ellenberg <lars.ellenberg@linbit.com>
Mon, 30 Jul 2012 07:08:25 +0000 (09:08 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 8 Nov 2012 15:58:39 +0000 (16:58 +0100)
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 963766b..e010aff 100644 (file)
@@ -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 */
index 849e5de..f2af74d 100644 (file)
@@ -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);
index d4c05e2..05ed480 100644 (file)
@@ -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;