diff options
-rw-r--r-- | drivers/block/drbd/drbd_req.c | 46 |
1 files changed, 26 insertions, 20 deletions
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 1249672519ca..c76402c3f64c 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c | |||
@@ -800,21 +800,33 @@ static bool remote_due_to_read_balancing(struct drbd_conf *mdev, sector_t sector | |||
800 | * The write_requests tree contains all active write requests which we | 800 | * The write_requests tree contains all active write requests which we |
801 | * currently know about. Wait for any requests to complete which conflict with | 801 | * currently know about. Wait for any requests to complete which conflict with |
802 | * the new one. | 802 | * the new one. |
803 | * | ||
804 | * Only way out: remove the conflicting intervals from the tree. | ||
803 | */ | 805 | */ |
804 | static int complete_conflicting_writes(struct drbd_conf *mdev, | 806 | static void complete_conflicting_writes(struct drbd_request *req) |
805 | sector_t sector, int size) | ||
806 | { | 807 | { |
807 | for(;;) { | 808 | DEFINE_WAIT(wait); |
808 | struct drbd_interval *i; | 809 | struct drbd_conf *mdev = req->w.mdev; |
809 | int err; | 810 | struct drbd_interval *i; |
811 | sector_t sector = req->i.sector; | ||
812 | int size = req->i.size; | ||
810 | 813 | ||
814 | i = drbd_find_overlap(&mdev->write_requests, sector, size); | ||
815 | if (!i) | ||
816 | return; | ||
817 | |||
818 | for (;;) { | ||
819 | prepare_to_wait(&mdev->misc_wait, &wait, TASK_UNINTERRUPTIBLE); | ||
811 | i = drbd_find_overlap(&mdev->write_requests, sector, size); | 820 | i = drbd_find_overlap(&mdev->write_requests, sector, size); |
812 | if (!i) | 821 | if (!i) |
813 | return 0; | 822 | break; |
814 | err = drbd_wait_misc(mdev, i); | 823 | /* Indicate to wake up device->misc_wait on progress. */ |
815 | if (err) | 824 | i->waiting = true; |
816 | return err; | 825 | spin_unlock_irq(&mdev->tconn->req_lock); |
826 | schedule(); | ||
827 | spin_lock_irq(&mdev->tconn->req_lock); | ||
817 | } | 828 | } |
829 | finish_wait(&mdev->misc_wait, &wait); | ||
818 | } | 830 | } |
819 | 831 | ||
820 | int __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time) | 832 | int __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time) |
@@ -826,7 +838,7 @@ int __drbd_make_request(struct drbd_conf *mdev, struct bio *bio, unsigned long s | |||
826 | struct drbd_request *req; | 838 | struct drbd_request *req; |
827 | struct net_conf *nc; | 839 | struct net_conf *nc; |
828 | int local, remote, send_oos = 0; | 840 | int local, remote, send_oos = 0; |
829 | int err; | 841 | int err = 0; |
830 | int ret = 0; | 842 | int ret = 0; |
831 | union drbd_dev_state s; | 843 | union drbd_dev_state s; |
832 | 844 | ||
@@ -925,16 +937,10 @@ allocate_barrier: | |||
925 | spin_lock_irq(&mdev->tconn->req_lock); | 937 | spin_lock_irq(&mdev->tconn->req_lock); |
926 | 938 | ||
927 | if (rw == WRITE) { | 939 | if (rw == WRITE) { |
928 | err = complete_conflicting_writes(mdev, sector, size); | 940 | /* This may temporarily give up the req_lock, |
929 | if (err) { | 941 | * but will re-aquire it before it returns here. |
930 | if (err != -ERESTARTSYS) | 942 | * Needs to be before the check on drbd_suspended() */ |
931 | _conn_request_state(mdev->tconn, | 943 | complete_conflicting_writes(req); |
932 | NS(conn, C_TIMEOUT), | ||
933 | CS_HARD); | ||
934 | spin_unlock_irq(&mdev->tconn->req_lock); | ||
935 | err = -EIO; | ||
936 | goto fail_free_complete; | ||
937 | } | ||
938 | } | 944 | } |
939 | 945 | ||
940 | if (drbd_suspended(mdev)) { | 946 | if (drbd_suspended(mdev)) { |