1 /* $NetBSD: kfifo.h,v 1.3 2018/08/27 14:41:53 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef _LINUX_KFIFO_H_ 33 #define _LINUX_KFIFO_H_ 34 35 #include <sys/types.h> 36 #include <sys/errno.h> 37 #include <sys/lock.h> 38 39 #include <linux/gfp.h> 40 #include <linux/slab.h> 41 42 struct kfifo_meta { 43 struct lock kfm_lock; 44 size_t kfm_head; 45 size_t kfm_tail; 46 size_t kfm_nbytes; 47 }; 48 49 #define _KFIFO_PTR_TYPE(TAG, TYPE) \ 50 struct TAG { \ 51 struct kfifo_meta kf_meta; \ 52 TYPE *kf_buf; \ 53 } 54 55 #define DECLARE_KFIFO_PTR(FIFO, TYPE) _KFIFO_PTR_TYPE(, TYPE) FIFO 56 57 _KFIFO_PTR_TYPE(kfifo, void); 58 59 #define kfifo_alloc(FIFO, SIZE, GFP) \ 60 _kfifo_alloc(&(FIFO)->kf_meta, &(FIFO)->kf_buf, (SIZE), (GFP)) 61 62 static inline int 63 _kfifo_alloc(struct kfifo_meta *meta, void *bufp, size_t nbytes, gfp_t gfp) 64 { 65 void *buf; 66 67 buf = kmalloc(nbytes, M_DRM, gfp); 68 if (buf == NULL) 69 return -ENOMEM; 70 71 /* Type pun! Hope void * == struct whatever *. */ 72 memcpy(bufp, &buf, sizeof(void *)); 73 74 lockinit(&meta->kfm_lock, "lkfl", 0, LK_CANRECURSE); 75 meta->kfm_head = 0; 76 meta->kfm_tail = 0; 77 meta->kfm_nbytes = nbytes; 78 79 return 0; 80 } 81 82 #define kfifo_free(FIFO) \ 83 _kfifo_free(&(FIFO)->kf_meta, &(FIFO)->kf_buf) 84 85 static inline void 86 _kfifo_free(struct kfifo_meta *meta, void *bufp) 87 { 88 void *buf; 89 90 mutex_destroy(&meta->kfm_lock); 91 92 memcpy(&buf, bufp, sizeof(void *)); 93 kfree(buf); 94 95 /* Paranoia. */ 96 buf = NULL; 97 memcpy(bufp, &buf, sizeof(void *)); 98 } 99 100 #define kfifo_is_empty(FIFO) (kfifo_len(FIFO) == 0) 101 #define kfifo_len(FIFO) _kfifo_len(&(FIFO)->kf_meta) 102 103 static inline size_t 104 _kfifo_len(struct kfifo_meta *meta) 105 { 106 const size_t head = meta->kfm_head; 107 const size_t tail = meta->kfm_tail; 108 const size_t nbytes = meta->kfm_nbytes; 109 110 return (head <= tail ? tail - head : nbytes + tail - head); 111 } 112 113 #define kfifo_out_peek(FIFO, PTR, SIZE) \ 114 _kfifo_out_peek(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 115 116 static inline size_t 117 _kfifo_out_peek(struct kfifo_meta *meta, void *buf, void *ptr, size_t size) 118 { 119 const char *src = buf; 120 char *dst = ptr; 121 size_t copied = 0; 122 123 lockmgr(&meta->kfm_lock, LK_EXCLUSIVE); 124 const size_t head = meta->kfm_head; 125 const size_t tail = meta->kfm_tail; 126 const size_t nbytes = meta->kfm_nbytes; 127 if (head <= tail) { 128 if (size <= tail - head) { 129 memcpy(dst, src + head, size); 130 copied = size; 131 } 132 } else { 133 if (size <= nbytes - head) { 134 memcpy(dst, src + head, size); 135 copied = size; 136 } else if (size <= nbytes + tail - head) { 137 memcpy(dst, src + head, nbytes - head); 138 memcpy(dst + nbytes - head, src, 139 size - (nbytes - head)); 140 copied = size; 141 } 142 } 143 lockmgr(&meta->kfm_lock, LK_RELEASE); 144 145 return copied; 146 } 147 148 #define kfifo_out(FIFO, PTR, SIZE) \ 149 _kfifo_out(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 150 151 static inline size_t 152 _kfifo_out(struct kfifo_meta *meta, const void *buf, void *ptr, size_t size) 153 { 154 const char *src = buf; 155 char *dst = ptr; 156 size_t copied = 0; 157 158 lockmgr(&meta->kfm_lock, LK_EXCLUSIVE); 159 const size_t head = meta->kfm_head; 160 const size_t tail = meta->kfm_tail; 161 const size_t nbytes = meta->kfm_nbytes; 162 if (head <= tail) { 163 if (size <= tail - head) { 164 memcpy(dst, src + head, size); 165 meta->kfm_head = head + size; 166 copied = size; 167 } 168 } else { 169 if (size <= nbytes - head) { 170 memcpy(dst, src + head, size); 171 meta->kfm_head = head + size; 172 copied = size; 173 } else if (size <= nbytes + tail - head) { 174 memcpy(dst, src + head, nbytes - head); 175 memcpy(dst + nbytes - head, src, 176 size - (nbytes - head)); 177 meta->kfm_head = size - (nbytes - head); 178 copied = size; 179 } 180 } 181 lockmgr(&meta->kfm_lock, LK_RELEASE); 182 183 return copied; 184 } 185 186 #define kfifo_in(FIFO, PTR, SIZE) \ 187 _kfifo_in(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 188 189 static inline size_t 190 _kfifo_in(struct kfifo_meta *meta, void *buf, const void *ptr, size_t size) 191 { 192 const char *src = ptr; 193 char *dst = buf; 194 size_t copied = 0; 195 196 lockmgr(&meta->kfm_lock, LK_EXCLUSIVE); 197 const size_t head = meta->kfm_head; 198 const size_t tail = meta->kfm_tail; 199 const size_t nbytes = meta->kfm_nbytes; 200 if (tail <= head) { 201 if (size <= head - tail) { 202 memcpy(dst + tail, src, size); 203 meta->kfm_tail = tail + size; 204 copied = size; 205 } 206 } else { 207 if (size <= nbytes - tail) { 208 memcpy(dst + tail, src, size); 209 meta->kfm_tail = tail + size; 210 } else if (size <= nbytes + tail - head) { 211 memcpy(dst + tail, src, nbytes - tail); 212 memcpy(dst, src + nbytes - tail, 213 size - (nbytes - tail)); 214 meta->kfm_tail = size - (nbytes - tail); 215 copied = size; 216 } 217 } 218 lockmgr(&meta->kfm_lock, LK_RELEASE); 219 220 return copied; 221 } 222 223 #endif /* _LINUX_KFIFO_H_ */ 224