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>
This commit is contained in:
parent
7f34f61490
commit
15e26f6a3c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue