Patch "ptr_ring: fix race conditions when resizing" has been added to the 4.9-stable tree
This is a note to let you know that I've just added the patch titled
ptr_ring: fix race conditions when resizing
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
ptr_ring-fix-race-conditions-when-resizing.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From foo@baz Thu Feb 23 21:13:05 CET 2017
From: "Michael S. Tsirkin" <[email protected]>
Date: Sun, 19 Feb 2017 07:17:17 +0200
Subject: ptr_ring: fix race conditions when resizing
From: "Michael S. Tsirkin" <[email protected]>
[ Upstream commit e71695307114335be1ed912f4a347396c2ed0e69 ]
Resizing currently drops consumer lock. This can cause entries to be reordered, which isn't good in itself. More importantly, consumer can detect a false ring empty condition and block forever.
Further, nesting of consumer within producer lock is problematic for tun, since it produces entries in a BH, which causes a lock order reversal:
CPU0 CPU1
---- ----
consume:
lock(&(&r->consumer_lock)->rlock);
resize:
local_irq_disable();
lock(&(&r->producer_lock)->rlock);
lock(&(&r->consumer_lock)->rlock); <Interrupt>
produce:
lock(&(&r->producer_lock)->rlock);
To fix, nest producer lock within consumer lock during resize, and keep consumer lock during the whole swap operation.
--- include/linux/ptr_ring.h | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-)
include/linux/ptr_ring.h +31 -5
@@ -111,6 +111,11 @@ static inline int __ptr_ring_produce(strreturn 0 ; } +/* +* Note: resize (below) nests producer lock within consumer lock, so if you +* consume in interrupt or BH context, you must disable interrupts/BH when +* calling this. +*/ static inline int ptr_ring_produce (struct ptr_ring *r,void *ptr) {int ret;
@@ -242,6 +247,11 @@ static inline void *__ptr_ring_consume(sreturn ptr; } +/* +* Note: resize (below) nests producer lock within consumer lock, so if you +* call this in interrupt or BH context, you must disable interrupts/BH when +* producing. +*/ static inline void *ptr_ring_consume (struct ptr_ring *r) {void *ptr;
@@ -357,7 +367,7 @@ static inline void **__ptr_ring_swap_quevoid **old;void *ptr; -while ((ptr =ptr_ring_consume (r))) +while ((ptr =__ptr_ring_consume (r)))if (producer < size) queue[producer++] = ptr;else if (destroy)
@@ -372,6 +382,12 @@ static inline void **__ptr_ring_swap_quereturn old; } +/* +* Note: producer lock is nested within consumer lock, so if you +* resize you must make sure all uses nest correctly. +* In particular if you consume ring in interrupt or BH context, you must +* disable interrupts/BH when doing so. +*/ static inline int ptr_ring_resize (struct ptr_ring *r,int size,gfp_t gfp,void (*destroy)(void *)) {
@@ -382,17 +398,25 @@ static inline int ptr_ring_resize(structif (!queue)return -ENOMEM ; -spin_lock_irqsave (&(r)->producer_lock, flags); +spin_lock_irqsave (&(r)->consumer_lock, flags); +spin_lock (&(r)->producer_lock); old =__ptr_ring_swap_queue (r, queue, size, gfp, destroy); -spin_unlock_irqrestore (&(r)->producer_lock, flags); +spin_unlock (&(r)->producer_lock); +spin_unlock_irqrestore (&(r)->consumer_lock, flags);kfree (old);return 0 ; } +/* +* Note: producer lock is nested within consumer lock, so if you +* resize you must make sure all uses nest correctly. +* In particular if you consume ring in interrupt or BH context, you must +* disable interrupts/BH when doing so. +*/ static inline int ptr_ring_resize_multiple (struct ptr_ring **rings,int nrings,int size,gfp_t gfp,void (*destroy)(void *))
@@ -412,10 +436,12 @@ static inline int ptr_ring_resize_multip }for (i =0 ; i < nrings; ++i) { -spin_lock_irqsave (&(rings[i])->producer_lock, flags); +spin_lock_irqsave (&(rings[i])->consumer_lock, flags); +spin_lock (&(rings[i])->producer_lock); queues[i] =__ptr_ring_swap_queue (rings[i], queues[i], size, gfp, destroy); -spin_unlock_irqrestore (&(rings[i])->producer_lock, flags); +spin_unlock (&(rings[i])->producer_lock); +spin_unlock_irqrestore (&(rings[i])->consumer_lock, flags); }for (i =0 ; i < nrings; ++i)
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/ptr_ring-fix-race-conditions-when-resizing.patch