]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
drbd: add drbd_queue_work_if_unqueued helper
authorLars Ellenberg <lars.ellenberg@linbit.com>
Mon, 28 Apr 2014 09:43:21 +0000 (11:43 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 10 Jul 2014 16:35:07 +0000 (18:35 +0200)
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>
drivers/block/drbd/drbd_int.h
drivers/block/drbd/drbd_worker.c

index c88d6c6be70bbfd9ee95ef0968a9ea23e1ea0d1a..a71f8bb9dc80340c0d1774d23f92bce41c0e99cf 100644 (file)
@@ -1777,6 +1777,17 @@ drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w)
        wake_up(&q->q_wait);
 }
 
+static inline void
+drbd_queue_work_if_unqueued(struct drbd_work_queue *q, struct drbd_work *w)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&q->q_lock, flags);
+       if (list_empty_careful(&w->list))
+               list_add_tail(&w->list, &q->q);
+       spin_unlock_irqrestore(&q->q_lock, flags);
+       wake_up(&q->q_wait);
+}
+
 static inline void
 drbd_device_post_work(struct drbd_device *device, int work_bit)
 {
index 9f0acb011f3088fb1a8dab5bd35fff9bb2b801b5..49b88731b349eefc10e32a9a161441d6f33af42a 100644 (file)
@@ -452,9 +452,9 @@ void resync_timer_fn(unsigned long data)
 {
        struct drbd_device *device = (struct drbd_device *) data;
 
-       if (list_empty(&device->resync_work.list))
-               drbd_queue_work(&first_peer_device(device)->connection->sender_work,
-                               &device->resync_work);
+       drbd_queue_work_if_unqueued(
+               &first_peer_device(device)->connection->sender_work,
+               &device->resync_work);
 }
 
 static void fifo_set(struct fifo_buffer *fb, int value)
@@ -1968,7 +1968,7 @@ static void do_unqueued_work(struct drbd_connection *connection)
 static bool dequeue_work_batch(struct drbd_work_queue *queue, struct list_head *work_list)
 {
        spin_lock_irq(&queue->q_lock);
-       list_splice_init(&queue->q, work_list);
+       list_splice_tail_init(&queue->q, work_list);
        spin_unlock_irq(&queue->q_lock);
        return !list_empty(work_list);
 }