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