| From foo@baz Sun Dec 31 11:13:15 CET 2017 |
| From: "Michael S. Tsirkin" <mst@redhat.com> |
| Date: Tue, 5 Dec 2017 21:29:37 +0200 |
| Subject: ptr_ring: add barriers |
| |
| From: "Michael S. Tsirkin" <mst@redhat.com> |
| |
| |
| [ Upstream commit a8ceb5dbfde1092b466936bca0ff3be127ecf38e ] |
| |
| Users of ptr_ring expect that it's safe to give the |
| data structure a pointer and have it be available |
| to consumers, but that actually requires an smb_wmb |
| or a stronger barrier. |
| |
| In absence of such barriers and on architectures that reorder writes, |
| consumer might read an un=initialized value from an skb pointer stored |
| in the skb array. This was observed causing crashes. |
| |
| To fix, add memory barriers. The barrier we use is a wmb, the |
| assumption being that producers do not need to read the value so we do |
| not need to order these reads. |
| |
| Reported-by: George Cherian <george.cherian@cavium.com> |
| Suggested-by: Jason Wang <jasowang@redhat.com> |
| Signed-off-by: Michael S. Tsirkin <mst@redhat.com> |
| Acked-by: Jason Wang <jasowang@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/ptr_ring.h | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| --- a/include/linux/ptr_ring.h |
| +++ b/include/linux/ptr_ring.h |
| @@ -99,12 +99,18 @@ static inline bool ptr_ring_full_bh(stru |
| |
| /* Note: callers invoking this in a loop must use a compiler barrier, |
| * for example cpu_relax(). Callers must hold producer_lock. |
| + * Callers are responsible for making sure pointer that is being queued |
| + * points to a valid data. |
| */ |
| static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr) |
| { |
| if (unlikely(!r->size) || r->queue[r->producer]) |
| return -ENOSPC; |
| |
| + /* Make sure the pointer we are storing points to a valid data. */ |
| + /* Pairs with smp_read_barrier_depends in __ptr_ring_consume. */ |
| + smp_wmb(); |
| + |
| r->queue[r->producer++] = ptr; |
| if (unlikely(r->producer >= r->size)) |
| r->producer = 0; |
| @@ -244,6 +250,9 @@ static inline void *__ptr_ring_consume(s |
| if (ptr) |
| __ptr_ring_discard_one(r); |
| |
| + /* Make sure anyone accessing data through the pointer is up to date. */ |
| + /* Pairs with smp_wmb in __ptr_ring_produce. */ |
| + smp_read_barrier_depends(); |
| return ptr; |
| } |
| |