Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15e26f6a authored by Lars Ellenberg's avatar Lars Ellenberg Committed by Philipp Reisner
Browse files

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: default avatarPhilipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: default avatarLars Ellenberg <lars.ellenberg@linbit.com>
parent 7f34f614
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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)
{
+4 −4
Original line number Diff line number Diff line
@@ -452,8 +452,8 @@ 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,
	drbd_queue_work_if_unqueued(
		&first_peer_device(device)->connection->sender_work,
		&device->resync_work);
}

@@ -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);
}