diff options
author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2014-04-28 05:43:21 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2014-07-10 12:35:07 -0400 |
commit | 15e26f6a3c6de2c665b4a30b9a70a902111f281f (patch) | |
tree | d8050e5998e46e3f6f541b3941dd727714ae07ba /drivers/block | |
parent | 7f34f61490ee87a470cf229069d59b0987f42a59 (diff) |
drbd: add drbd_queue_work_if_unqueued helper
We sometimes do
if (list_empty(&w.list))
drbd_queue_work(&q, &w.list);
Removal (list_del_init) may happen outside all locks, after all
pending work entries have been moved to an on-stack local work list.
For not dynamically allocated, but embeded, work structs,
we must avoid to re-add until it really was removed.
Move that list_empty check inside the spin_lock(&q->q_lock)
within the helper function, and change to list_empty_careful().
This may have been the reason for a list_add corruption
inside drbd_queue_work().
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/drbd/drbd_int.h | 11 | ||||
-rw-r--r-- | drivers/block/drbd/drbd_worker.c | 8 |
2 files changed, 15 insertions, 4 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index c88d6c6be70b..a71f8bb9dc80 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
@@ -1778,6 +1778,17 @@ drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w) | |||
1778 | } | 1778 | } |
1779 | 1779 | ||
1780 | static inline void | 1780 | static inline void |
1781 | drbd_queue_work_if_unqueued(struct drbd_work_queue *q, struct drbd_work *w) | ||
1782 | { | ||
1783 | unsigned long flags; | ||
1784 | spin_lock_irqsave(&q->q_lock, flags); | ||
1785 | if (list_empty_careful(&w->list)) | ||
1786 | list_add_tail(&w->list, &q->q); | ||
1787 | spin_unlock_irqrestore(&q->q_lock, flags); | ||
1788 | wake_up(&q->q_wait); | ||
1789 | } | ||
1790 | |||
1791 | static inline void | ||
1781 | drbd_device_post_work(struct drbd_device *device, int work_bit) | 1792 | drbd_device_post_work(struct drbd_device *device, int work_bit) |
1782 | { | 1793 | { |
1783 | if (!test_and_set_bit(work_bit, &device->flags)) { | 1794 | if (!test_and_set_bit(work_bit, &device->flags)) { |
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 9f0acb011f30..49b88731b349 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c | |||
@@ -452,9 +452,9 @@ void resync_timer_fn(unsigned long data) | |||
452 | { | 452 | { |
453 | struct drbd_device *device = (struct drbd_device *) data; | 453 | struct drbd_device *device = (struct drbd_device *) data; |
454 | 454 | ||
455 | if (list_empty(&device->resync_work.list)) | 455 | drbd_queue_work_if_unqueued( |
456 | drbd_queue_work(&first_peer_device(device)->connection->sender_work, | 456 | &first_peer_device(device)->connection->sender_work, |
457 | &device->resync_work); | 457 | &device->resync_work); |
458 | } | 458 | } |
459 | 459 | ||
460 | static void fifo_set(struct fifo_buffer *fb, int value) | 460 | static void fifo_set(struct fifo_buffer *fb, int value) |
@@ -1968,7 +1968,7 @@ static void do_unqueued_work(struct drbd_connection *connection) | |||
1968 | static bool dequeue_work_batch(struct drbd_work_queue *queue, struct list_head *work_list) | 1968 | static bool dequeue_work_batch(struct drbd_work_queue *queue, struct list_head *work_list) |
1969 | { | 1969 | { |
1970 | spin_lock_irq(&queue->q_lock); | 1970 | spin_lock_irq(&queue->q_lock); |
1971 | list_splice_init(&queue->q, work_list); | 1971 | list_splice_tail_init(&queue->q, work_list); |
1972 | spin_unlock_irq(&queue->q_lock); | 1972 | spin_unlock_irq(&queue->q_lock); |
1973 | return !list_empty(work_list); | 1973 | return !list_empty(work_list); |
1974 | } | 1974 | } |