From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754737AbcESMPh (ORCPT ); Thu, 19 May 2016 08:15:37 -0400 Received: from mx1.redhat.com ([209.132.183.28]:54800 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753590AbcESMPe (ORCPT ); Thu, 19 May 2016 08:15:34 -0400 Date: Thu, 19 May 2016 15:15:30 +0300 From: "Michael S. Tsirkin" To: linux-kernel@vger.kernel.org Cc: Jason Wang , Eric Dumazet , davem@davemloft.net, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Steven Rostedt , brouer@redhat.com Subject: [PATCH v2] skb_array: array based FIFO for skbs Message-ID: <1463659932-14570-1-git-send-email-mst@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Mutt-Fcc: =sent X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Thu, 19 May 2016 12:15:34 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org A simple array based FIFO of pointers. Intended for net stack so uses skbs for type safety, but we can replace with with void * if others find it useful outside of net stack. Signed-off-by: Michael S. Tsirkin --- Still untested, fixed the bug pointed out by Eric. Posting since several people expressed interest in helping test this. Note: SKB_ARRAY_MIN_SIZE is a heuristic. It can be increased to more than 2 cache lines, or even to INT_MAX to disable the heuristic completely. include/linux/skb_array.h | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 include/linux/skb_array.h diff --git a/include/linux/skb_array.h b/include/linux/skb_array.h new file mode 100644 index 0000000..fd4ff23 --- /dev/null +++ b/include/linux/skb_array.h @@ -0,0 +1,115 @@ +/* + * See Documentation/skb-array.txt for more information. + */ + +#ifndef _LINUX_SKB_ARRAY_H +#define _LINUX_SKB_ARRAY_H 1 + +#include +#include +#include +#include +#include +#include +#include + +struct sk_buff; + +struct skb_array { + int producer ____cacheline_aligned_in_smp; + spinlock_t producer_lock; + int consumer ____cacheline_aligned_in_smp; + spinlock_t consumer_lock; + /* Shared consumer/producer data */ + int size ____cacheline_aligned_in_smp; /* max entries in queue */ + struct sk_buff **queue; +}; + +#define SKB_ARRAY_MIN_SIZE (2 * (0x1 << cache_line_size()) / \ + sizeof (struct sk_buff *)) + +static inline int __skb_array_produce(struct skb_array *a, + struct sk_buff *skb) +{ + /* Try to start from beginning: good for cache utilization as we'll + * keep reusing the same cache line. + * Produce at least SKB_ARRAY_MIN_SIZE entries before trying to do this, + * to reduce bouncing cache lines between them. + */ + if (a->producer >= SKB_ARRAY_MIN_SIZE && !a->queue[0]) + a->producer = 0; + if (a->queue[a->producer]) + return -ENOSPC; + a->queue[a->producer] = skb; + if (unlikely(++a->producer > a->size)) + a->producer = 0; + return 0; +} + +static inline int skb_array_produce_bh(struct skb_array *a, + struct sk_buff *skb) +{ + int ret; + + spin_lock_bh(&a->producer_lock); + ret = __skb_array_produce(a, skb); + spin_unlock_bh(&a->producer_lock); + + return ret; +} + +static inline struct sk_buff *__skb_array_peek(struct skb_array *a) +{ + if (a->queue[a->consumer]) + return a->queue[a->consumer]; + + /* Check whether producer started at the beginning. */ + if (unlikely(a->consumer >= SKB_ARRAY_MIN_SIZE && a->queue[0])) { + a->consumer = 0; + return a->queue[0]; + } + + return NULL; +} + +static inline void __skb_array_consume(struct skb_array *a) +{ + a->queue[a->consumer++] = NULL; + if (unlikely(a->consumer > a->size)) + a->consumer = 0; +} + +static inline struct sk_buff *skb_array_consume_bh(struct skb_array *a) +{ + struct sk_buff *skb; + + spin_lock_bh(&a->producer_lock); + skb = __skb_array_peek(a); + if (skb) + __skb_array_consume(a); + spin_unlock_bh(&a->producer_lock); + + return skb; +} + +static inline int skb_array_init(struct skb_array *a, int size, gfp_t gfp) +{ + a->queue = kmalloc(ALIGN(size * sizeof *(a->queue), SMP_CACHE_BYTES), + gfp); + if (!a->queue) + return -ENOMEM; + + a->size = size; + a->producer = a->consumer = 0; + spin_lock_init(&a->producer_lock); + spin_lock_init(&a->consumer_lock); + + return 0; +} + +static inline void skb_array_cleanup(struct skb_array *a) +{ + kfree(a->queue); +} + +#endif /* _LINUX_SKB_ARRAY_H */ -- MST