xref: /freebsd-src/sys/sys/buf_ring.h (revision 37bb465d27eefd2237e6e3af9592457fccf324e2)
18e0ad55aSJoel Dahl /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3c4e20cadSPedro F. Giffuni  *
48e0ad55aSJoel Dahl  * Copyright (c) 2007-2009 Kip Macy <kmacy@freebsd.org>
5db7f0b97SKip Macy  * All rights reserved.
687940d2bSAndrew Turner  * Copyright (c) 2024 Arm Ltd
7db7f0b97SKip Macy  *
8db7f0b97SKip Macy  * Redistribution and use in source and binary forms, with or without
98e0ad55aSJoel Dahl  * modification, are permitted provided that the following conditions
108e0ad55aSJoel Dahl  * are met:
118e0ad55aSJoel Dahl  * 1. Redistributions of source code must retain the above copyright
128e0ad55aSJoel Dahl  *    notice, this list of conditions and the following disclaimer.
138e0ad55aSJoel Dahl  * 2. Redistributions in binary form must reproduce the above copyright
148e0ad55aSJoel Dahl  *    notice, this list of conditions and the following disclaimer in the
158e0ad55aSJoel Dahl  *    documentation and/or other materials provided with the distribution.
16db7f0b97SKip Macy  *
178e0ad55aSJoel Dahl  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
188e0ad55aSJoel Dahl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19db7f0b97SKip Macy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208e0ad55aSJoel Dahl  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
218e0ad55aSJoel Dahl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228e0ad55aSJoel Dahl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238e0ad55aSJoel Dahl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248e0ad55aSJoel Dahl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258e0ad55aSJoel Dahl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268e0ad55aSJoel Dahl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278e0ad55aSJoel Dahl  * SUCH DAMAGE.
28db7f0b97SKip Macy  *
298e0ad55aSJoel Dahl  */
30db7f0b97SKip Macy 
31db7f0b97SKip Macy #ifndef	_SYS_BUF_RING_H_
32db7f0b97SKip Macy #define	_SYS_BUF_RING_H_
33db7f0b97SKip Macy 
34a161269bSMark Johnston #include <sys/param.h>
35a161269bSMark Johnston #include <sys/kassert.h>
36a161269bSMark Johnston #include <machine/atomic.h>
37db7f0b97SKip Macy #include <machine/cpu.h>
38db7f0b97SKip Macy 
39d3d34d56SAndrew Turner #if defined(DEBUG_BUFRING) && defined(_KERNEL)
40db7f0b97SKip Macy #include <sys/lock.h>
41db7f0b97SKip Macy #include <sys/mutex.h>
42db7f0b97SKip Macy #endif
43db7f0b97SKip Macy 
443cc60390SAndrew Turner /*
453cc60390SAndrew Turner  * We only apply the mask to the head and tail values when calculating the
463cc60390SAndrew Turner  * index into br_ring to access. This means the upper bits can be used as
473cc60390SAndrew Turner  * epoch to reduce the chance the atomic_cmpset succeedes when it should
483cc60390SAndrew Turner  * fail, e.g. when the head wraps while the CPU is in an interrupt. This
493cc60390SAndrew Turner  * is a probablistic fix as there is still a very unlikely chance the
503cc60390SAndrew Turner  * value wraps back to the expected value.
513cc60390SAndrew Turner  *
523cc60390SAndrew Turner  */
53db7f0b97SKip Macy struct buf_ring {
5490cd9c20SAndrew Turner 	uint32_t		br_prod_head;
5590cd9c20SAndrew Turner 	uint32_t		br_prod_tail;
56db7f0b97SKip Macy 	int              	br_prod_size;
57db7f0b97SKip Macy 	int              	br_prod_mask;
581635d917SKip Macy 	uint64_t		br_drops;
5990cd9c20SAndrew Turner 	uint32_t		br_cons_head __aligned(CACHE_LINE_SIZE);
6090cd9c20SAndrew Turner 	uint32_t		br_cons_tail;
61db7f0b97SKip Macy 	int		 	br_cons_size;
62db7f0b97SKip Macy 	int              	br_cons_mask;
63d3d34d56SAndrew Turner #if defined(DEBUG_BUFRING) && defined(_KERNEL)
64db7f0b97SKip Macy 	struct mtx		*br_lock;
65db7f0b97SKip Macy #endif
6685e43e96SAttilio Rao 	void			*br_ring[0] __aligned(CACHE_LINE_SIZE);
67db7f0b97SKip Macy };
68db7f0b97SKip Macy 
693982c699SKip Macy /*
703982c699SKip Macy  * multi-producer safe lock-free ring buffer enqueue
713982c699SKip Macy  *
723982c699SKip Macy  */
73db7f0b97SKip Macy static __inline int
74063efed2SGleb Smirnoff buf_ring_enqueue(struct buf_ring *br, void *buf)
75db7f0b97SKip Macy {
763cc60390SAndrew Turner 	uint32_t prod_head, prod_next, prod_idx;
773cc60390SAndrew Turner 	uint32_t cons_tail, mask;
789a6981efSAndrew Gallatin 
793cc60390SAndrew Turner 	mask = br->br_prod_mask;
803cc60390SAndrew Turner #ifdef DEBUG_BUFRING
819a6981efSAndrew Gallatin 	/*
829a6981efSAndrew Gallatin 	 * Note: It is possible to encounter an mbuf that was removed
839a6981efSAndrew Gallatin 	 * via drbr_peek(), and then re-added via drbr_putback() and
849a6981efSAndrew Gallatin 	 * trigger a spurious panic.
859a6981efSAndrew Gallatin 	 */
8690cd9c20SAndrew Turner 	for (uint32_t i = atomic_load_32(&br->br_cons_head);
8790cd9c20SAndrew Turner 	    i != atomic_load_32(&br->br_prod_head); i++)
883cc60390SAndrew Turner 		if (br->br_ring[i & mask] == buf)
89db7f0b97SKip Macy 			panic("buf=%p already enqueue at %d prod=%d cons=%d",
9090cd9c20SAndrew Turner 			    buf, i, atomic_load_32(&br->br_prod_tail),
9190cd9c20SAndrew Turner 			    atomic_load_32(&br->br_cons_tail));
92db7f0b97SKip Macy #endif
93db7f0b97SKip Macy 	critical_enter();
94db7f0b97SKip Macy 	do {
95fe2445f4SAndrew Turner 		/*
96fe2445f4SAndrew Turner 		 * br->br_prod_head needs to be read before br->br_cons_tail.
97fe2445f4SAndrew Turner 		 * If not then we could perform the dequeue and enqueue
98fe2445f4SAndrew Turner 		 * between reading br_cons_tail and reading br_prod_head. This
99fe2445f4SAndrew Turner 		 * could give us values where br_cons_head == br_prod_tail
100fe2445f4SAndrew Turner 		 * (after masking).
101fe2445f4SAndrew Turner 		 *
102fe2445f4SAndrew Turner 		 * To work around this us a load acquire. This is just to
103fe2445f4SAndrew Turner 		 * ensure ordering within this thread.
104fe2445f4SAndrew Turner 		 */
105fe2445f4SAndrew Turner 		prod_head = atomic_load_acq_32(&br->br_prod_head);
1063cc60390SAndrew Turner 		prod_next = prod_head + 1;
10744e1cfcaSAndrew Turner 		cons_tail = atomic_load_acq_32(&br->br_cons_tail);
108db7f0b97SKip Macy 
1093cc60390SAndrew Turner 		if ((int32_t)(cons_tail + br->br_prod_size - prod_next) < 1) {
11090cd9c20SAndrew Turner 			if (prod_head == atomic_load_32(&br->br_prod_head) &&
11190cd9c20SAndrew Turner 			    cons_tail == atomic_load_32(&br->br_cons_tail)) {
112063efed2SGleb Smirnoff 				br->br_drops++;
113db7f0b97SKip Macy 				critical_exit();
1142760fcd0SKip Macy 				return (ENOBUFS);
115db7f0b97SKip Macy 			}
11624684257SJosh Paetzel 			continue;
11724684257SJosh Paetzel 		}
118*65ec52e6SAndrew Turner 	} while (!atomic_cmpset_32(&br->br_prod_head, prod_head, prod_next));
1193cc60390SAndrew Turner 	prod_idx = prod_head & mask;
120db7f0b97SKip Macy #ifdef DEBUG_BUFRING
1213cc60390SAndrew Turner 	if (br->br_ring[prod_idx] != NULL)
122db7f0b97SKip Macy 		panic("dangling value in enqueue");
123db7f0b97SKip Macy #endif
1243cc60390SAndrew Turner 	br->br_ring[prod_idx] = buf;
12585e43e96SAttilio Rao 
12685e43e96SAttilio Rao 	/*
127db7f0b97SKip Macy 	 * If there are other enqueues in progress
1287f417bfaSPedro F. Giffuni 	 * that preceded us, we need to wait for them
129db7f0b97SKip Macy 	 * to complete
130db7f0b97SKip Macy 	 */
13190cd9c20SAndrew Turner 	while (atomic_load_32(&br->br_prod_tail) != prod_head)
132db7f0b97SKip Macy 		cpu_spinwait();
13317a597bcSAndrew Turner 	atomic_store_rel_32(&br->br_prod_tail, prod_next);
134db7f0b97SKip Macy 	critical_exit();
135db7f0b97SKip Macy 	return (0);
136db7f0b97SKip Macy }
137db7f0b97SKip Macy 
138db7f0b97SKip Macy /*
139db7f0b97SKip Macy  * multi-consumer safe dequeue
140db7f0b97SKip Macy  *
141db7f0b97SKip Macy  */
142db7f0b97SKip Macy static __inline void *
143db7f0b97SKip Macy buf_ring_dequeue_mc(struct buf_ring *br)
144db7f0b97SKip Macy {
1453cc60390SAndrew Turner 	uint32_t cons_head, cons_next, cons_idx;
146947754afSAndrew Turner 	uint32_t prod_tail, mask;
147db7f0b97SKip Macy 	void *buf;
148db7f0b97SKip Macy 
149db7f0b97SKip Macy 	critical_enter();
1503cc60390SAndrew Turner 	mask = br->br_cons_mask;
151db7f0b97SKip Macy 	do {
152fe2445f4SAndrew Turner 		/*
153fe2445f4SAndrew Turner 		 * As with buf_ring_enqueue ensure we read the head before
154fe2445f4SAndrew Turner 		 * the tail. If we read them in the wrong order we may
155fe2445f4SAndrew Turner 		 * think the bug_ring is full when it is empty.
156fe2445f4SAndrew Turner 		 */
157fe2445f4SAndrew Turner 		cons_head = atomic_load_acq_32(&br->br_cons_head);
1583cc60390SAndrew Turner 		cons_next = cons_head + 1;
159947754afSAndrew Turner 		prod_tail = atomic_load_acq_32(&br->br_prod_tail);
160db7f0b97SKip Macy 
161947754afSAndrew Turner 		if (cons_head == prod_tail) {
162db7f0b97SKip Macy 			critical_exit();
163db7f0b97SKip Macy 			return (NULL);
164db7f0b97SKip Macy 		}
165*65ec52e6SAndrew Turner 	} while (!atomic_cmpset_32(&br->br_cons_head, cons_head, cons_next));
1663cc60390SAndrew Turner 	cons_idx = cons_head & mask;
167db7f0b97SKip Macy 
1683cc60390SAndrew Turner 	buf = br->br_ring[cons_idx];
169db7f0b97SKip Macy #ifdef DEBUG_BUFRING
1703cc60390SAndrew Turner 	br->br_ring[cons_idx] = NULL;
171db7f0b97SKip Macy #endif
172db7f0b97SKip Macy 	/*
173db7f0b97SKip Macy 	 * If there are other dequeues in progress
1747f417bfaSPedro F. Giffuni 	 * that preceded us, we need to wait for them
175db7f0b97SKip Macy 	 * to complete
176db7f0b97SKip Macy 	 */
17790cd9c20SAndrew Turner 	while (atomic_load_32(&br->br_cons_tail) != cons_head)
178db7f0b97SKip Macy 		cpu_spinwait();
179db7f0b97SKip Macy 
18017a597bcSAndrew Turner 	atomic_store_rel_32(&br->br_cons_tail, cons_next);
181db7f0b97SKip Macy 	critical_exit();
182db7f0b97SKip Macy 
183db7f0b97SKip Macy 	return (buf);
184db7f0b97SKip Macy }
185db7f0b97SKip Macy 
186db7f0b97SKip Macy /*
1873982c699SKip Macy  * single-consumer dequeue
1883982c699SKip Macy  * use where dequeue is protected by a lock
1893982c699SKip Macy  * e.g. a network driver's tx queue lock
190db7f0b97SKip Macy  */
191db7f0b97SKip Macy static __inline void *
192db7f0b97SKip Macy buf_ring_dequeue_sc(struct buf_ring *br)
193db7f0b97SKip Macy {
1943cc60390SAndrew Turner 	uint32_t cons_head, cons_next, cons_idx;
1953cc60390SAndrew Turner 	uint32_t prod_tail, mask;
196db7f0b97SKip Macy 	void *buf;
197db7f0b97SKip Macy 
1983cc60390SAndrew Turner 	mask = br->br_cons_mask;
19990cd9c20SAndrew Turner 	cons_head = atomic_load_32(&br->br_cons_head);
200cadab293SWojciech Macek 	prod_tail = atomic_load_acq_32(&br->br_prod_tail);
201db7f0b97SKip Macy 
2023cc60390SAndrew Turner 	cons_next = cons_head + 1;
203db7f0b97SKip Macy 
204a913be09SKip Macy 	if (cons_head == prod_tail)
205db7f0b97SKip Macy 		return (NULL);
206db7f0b97SKip Macy 
2073cc60390SAndrew Turner 	cons_idx = cons_head & mask;
20890cd9c20SAndrew Turner 	atomic_store_32(&br->br_cons_head, cons_next);
2093cc60390SAndrew Turner 	buf = br->br_ring[cons_idx];
210db7f0b97SKip Macy 
211db7f0b97SKip Macy #ifdef DEBUG_BUFRING
2123cc60390SAndrew Turner 	br->br_ring[cons_idx] = NULL;
213d3d34d56SAndrew Turner #ifdef _KERNEL
214db7f0b97SKip Macy 	if (!mtx_owned(br->br_lock))
215db7f0b97SKip Macy 		panic("lock not held on single consumer dequeue");
216d3d34d56SAndrew Turner #endif
21790cd9c20SAndrew Turner 	if (atomic_load_32(&br->br_cons_tail) != cons_head)
218db7f0b97SKip Macy 		panic("inconsistent list cons_tail=%d cons_head=%d",
21990cd9c20SAndrew Turner 		    atomic_load_32(&br->br_cons_tail), cons_head);
220db7f0b97SKip Macy #endif
22144e1cfcaSAndrew Turner 	atomic_store_rel_32(&br->br_cons_tail, cons_next);
222db7f0b97SKip Macy 	return (buf);
223db7f0b97SKip Macy }
224db7f0b97SKip Macy 
2253982c699SKip Macy /*
226ded5ea6aSRandall Stewart  * single-consumer advance after a peek
227ded5ea6aSRandall Stewart  * use where it is protected by a lock
228ded5ea6aSRandall Stewart  * e.g. a network driver's tx queue lock
229ded5ea6aSRandall Stewart  */
230ded5ea6aSRandall Stewart static __inline void
231ded5ea6aSRandall Stewart buf_ring_advance_sc(struct buf_ring *br)
232ded5ea6aSRandall Stewart {
2333cc60390SAndrew Turner 	uint32_t cons_head, cons_next, prod_tail;
2343cc60390SAndrew Turner #ifdef DEBUG_BUFRING
2353cc60390SAndrew Turner 	uint32_t mask;
236ded5ea6aSRandall Stewart 
2373cc60390SAndrew Turner 	mask = br->br_cons_mask;
2383cc60390SAndrew Turner #endif
23990cd9c20SAndrew Turner 	cons_head = atomic_load_32(&br->br_cons_head);
24090cd9c20SAndrew Turner 	prod_tail = atomic_load_32(&br->br_prod_tail);
241ded5ea6aSRandall Stewart 
2423cc60390SAndrew Turner 	cons_next = cons_head + 1;
243ded5ea6aSRandall Stewart 	if (cons_head == prod_tail)
244ded5ea6aSRandall Stewart 		return;
24590cd9c20SAndrew Turner 	atomic_store_32(&br->br_cons_head, cons_next);
246ded5ea6aSRandall Stewart #ifdef DEBUG_BUFRING
2473cc60390SAndrew Turner 	br->br_ring[cons_head & mask] = NULL;
248ded5ea6aSRandall Stewart #endif
24944e1cfcaSAndrew Turner 	atomic_store_rel_32(&br->br_cons_tail, cons_next);
250ded5ea6aSRandall Stewart }
251ded5ea6aSRandall Stewart 
252ded5ea6aSRandall Stewart /*
253ded5ea6aSRandall Stewart  * Used to return a buffer (most likely already there)
2541d64db52SConrad Meyer  * to the top of the ring. The caller should *not*
255ded5ea6aSRandall Stewart  * have used any dequeue to pull it out of the ring
256ded5ea6aSRandall Stewart  * but instead should have used the peek() function.
257ded5ea6aSRandall Stewart  * This is normally used where the transmit queue
2581d64db52SConrad Meyer  * of a driver is full, and an mbuf must be returned.
259ded5ea6aSRandall Stewart  * Most likely whats in the ring-buffer is what
260ded5ea6aSRandall Stewart  * is being put back (since it was not removed), but
261ded5ea6aSRandall Stewart  * sometimes the lower transmit function may have
262ded5ea6aSRandall Stewart  * done a pullup or other function that will have
2631d64db52SConrad Meyer  * changed it. As an optimization we always put it
264ded5ea6aSRandall Stewart  * back (since jhb says the store is probably cheaper),
265ded5ea6aSRandall Stewart  * if we have to do a multi-queue version we will need
266ded5ea6aSRandall Stewart  * the compare and an atomic.
267ded5ea6aSRandall Stewart  */
268ded5ea6aSRandall Stewart static __inline void
269ded5ea6aSRandall Stewart buf_ring_putback_sc(struct buf_ring *br, void *new)
270ded5ea6aSRandall Stewart {
27190cd9c20SAndrew Turner 	uint32_t cons_idx, mask;
2723cc60390SAndrew Turner 
2733cc60390SAndrew Turner 	mask = br->br_cons_mask;
27490cd9c20SAndrew Turner 	cons_idx = atomic_load_32(&br->br_cons_head) & mask;
27590cd9c20SAndrew Turner 	KASSERT(cons_idx != (atomic_load_32(&br->br_prod_tail) & mask),
276ded5ea6aSRandall Stewart 		("Buf-Ring has none in putback")) ;
27790cd9c20SAndrew Turner 	br->br_ring[cons_idx] = new;
278ded5ea6aSRandall Stewart }
279ded5ea6aSRandall Stewart 
280ded5ea6aSRandall Stewart /*
2813982c699SKip Macy  * return a pointer to the first entry in the ring
2823982c699SKip Macy  * without modifying it, or NULL if the ring is empty
2833982c699SKip Macy  * race-prone if not protected by a lock
2843982c699SKip Macy  */
285db7f0b97SKip Macy static __inline void *
286db7f0b97SKip Macy buf_ring_peek(struct buf_ring *br)
287db7f0b97SKip Macy {
288947754afSAndrew Turner 	uint32_t cons_head, prod_tail, mask;
289db7f0b97SKip Macy 
290d3d34d56SAndrew Turner #if defined(DEBUG_BUFRING) && defined(_KERNEL)
291db7f0b97SKip Macy 	if ((br->br_lock != NULL) && !mtx_owned(br->br_lock))
292db7f0b97SKip Macy 		panic("lock not held on single consumer dequeue");
293db7f0b97SKip Macy #endif
2943cc60390SAndrew Turner 	mask = br->br_cons_mask;
295947754afSAndrew Turner 	prod_tail = atomic_load_acq_32(&br->br_prod_tail);
29690cd9c20SAndrew Turner 	cons_head = atomic_load_32(&br->br_cons_head);
297947754afSAndrew Turner 
298947754afSAndrew Turner 	if (cons_head == prod_tail)
2993b14b38bSKip Macy 		return (NULL);
3003b14b38bSKip Macy 
301947754afSAndrew Turner 	return (br->br_ring[cons_head & mask]);
302db7f0b97SKip Macy }
303db7f0b97SKip Macy 
3041321c502SSepherosa Ziehau static __inline void *
3051321c502SSepherosa Ziehau buf_ring_peek_clear_sc(struct buf_ring *br)
3061321c502SSepherosa Ziehau {
307947754afSAndrew Turner 	uint32_t cons_head, prod_tail, mask;
3081321c502SSepherosa Ziehau 	void *ret;
3091321c502SSepherosa Ziehau 
310d3d34d56SAndrew Turner #if defined(DEBUG_BUFRING) && defined(_KERNEL)
3111321c502SSepherosa Ziehau 	if (!mtx_owned(br->br_lock))
3121321c502SSepherosa Ziehau 		panic("lock not held on single consumer dequeue");
3131321c502SSepherosa Ziehau #endif
314c591d46eSWojciech Macek 
3153cc60390SAndrew Turner 	mask = br->br_cons_mask;
316947754afSAndrew Turner 	prod_tail = atomic_load_acq_32(&br->br_prod_tail);
31790cd9c20SAndrew Turner 	cons_head = atomic_load_32(&br->br_cons_head);
318947754afSAndrew Turner 
319947754afSAndrew Turner 	if (cons_head == prod_tail)
3201321c502SSepherosa Ziehau 		return (NULL);
3211321c502SSepherosa Ziehau 
322947754afSAndrew Turner 	ret = br->br_ring[cons_head & mask];
3231321c502SSepherosa Ziehau #ifdef DEBUG_BUFRING
3241321c502SSepherosa Ziehau 	/*
3251321c502SSepherosa Ziehau 	 * Single consumer, i.e. cons_head will not move while we are
3261321c502SSepherosa Ziehau 	 * running, so atomic_swap_ptr() is not necessary here.
3271321c502SSepherosa Ziehau 	 */
328947754afSAndrew Turner 	br->br_ring[cons_head & mask] = NULL;
3291321c502SSepherosa Ziehau #endif
330d3d34d56SAndrew Turner 	return (ret);
3311321c502SSepherosa Ziehau }
3321321c502SSepherosa Ziehau 
333db7f0b97SKip Macy static __inline int
334db7f0b97SKip Macy buf_ring_full(struct buf_ring *br)
335db7f0b97SKip Macy {
336db7f0b97SKip Macy 
33790cd9c20SAndrew Turner 	return (atomic_load_32(&br->br_prod_head) ==
33890cd9c20SAndrew Turner 	    atomic_load_32(&br->br_cons_tail) + br->br_cons_size - 1);
339db7f0b97SKip Macy }
340db7f0b97SKip Macy 
341db7f0b97SKip Macy static __inline int
342db7f0b97SKip Macy buf_ring_empty(struct buf_ring *br)
343db7f0b97SKip Macy {
344db7f0b97SKip Macy 
34590cd9c20SAndrew Turner 	return (atomic_load_32(&br->br_cons_head) ==
34690cd9c20SAndrew Turner 	    atomic_load_32(&br->br_prod_tail));
347db7f0b97SKip Macy }
348db7f0b97SKip Macy 
349db7f0b97SKip Macy static __inline int
350db7f0b97SKip Macy buf_ring_count(struct buf_ring *br)
351db7f0b97SKip Macy {
35290cd9c20SAndrew Turner 	uint32_t cons_tail, prod_tail;
353db7f0b97SKip Macy 
35490cd9c20SAndrew Turner 	cons_tail = atomic_load_32(&br->br_cons_tail);
35590cd9c20SAndrew Turner 	prod_tail = atomic_load_32(&br->br_prod_tail);
35690cd9c20SAndrew Turner 	return ((br->br_prod_size + prod_tail - cons_tail) & br->br_prod_mask);
357db7f0b97SKip Macy }
358db7f0b97SKip Macy 
359a161269bSMark Johnston #ifdef _KERNEL
360db7f0b97SKip Macy struct buf_ring *buf_ring_alloc(int count, struct malloc_type *type, int flags,
361db7f0b97SKip Macy     struct mtx *);
362db7f0b97SKip Macy void buf_ring_free(struct buf_ring *br, struct malloc_type *type);
363a161269bSMark Johnston #else
364db7f0b97SKip Macy 
365a161269bSMark Johnston #include <stdlib.h>
366a161269bSMark Johnston 
367a161269bSMark Johnston static inline struct buf_ring *
368a161269bSMark Johnston buf_ring_alloc(int count)
369a161269bSMark Johnston {
370a161269bSMark Johnston 	struct buf_ring *br;
371a161269bSMark Johnston 
372a161269bSMark Johnston 	KASSERT(powerof2(count), ("buf ring must be size power of 2"));
373a161269bSMark Johnston 
374a161269bSMark Johnston 	br = calloc(1, sizeof(struct buf_ring) + count * sizeof(void *));
375a161269bSMark Johnston 	if (br == NULL)
376a161269bSMark Johnston 		return (NULL);
377a161269bSMark Johnston 	br->br_prod_size = br->br_cons_size = count;
378a161269bSMark Johnston 	br->br_prod_mask = br->br_cons_mask = count - 1;
379a161269bSMark Johnston 	br->br_prod_head = br->br_cons_head = 0;
380a161269bSMark Johnston 	br->br_prod_tail = br->br_cons_tail = 0;
381a161269bSMark Johnston 	return (br);
382a161269bSMark Johnston }
383a161269bSMark Johnston 
384a161269bSMark Johnston static inline void
385a161269bSMark Johnston buf_ring_free(struct buf_ring *br)
386a161269bSMark Johnston {
387a161269bSMark Johnston 	free(br);
388a161269bSMark Johnston }
389a161269bSMark Johnston 
390a161269bSMark Johnston #endif /* !_KERNEL */
391a161269bSMark Johnston #endif /* _SYS_BUF_RING_H_ */
392