1 /* $NetBSD: kfifo.h,v 1.6 2021/12/19 12:33:02 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/mutex.h> 38 39 #include <linux/gfp.h> 40 #include <linux/slab.h> 41 42 struct kfifo_meta { 43 kmutex_t 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 _KFIFO_TYPE(TAG, TYPE, N) \ 56 struct TAG { \ 57 struct kfifo_meta kf_meta; \ 58 TYPE kf_buf[N]; \ 59 } 60 61 #define DECLARE_KFIFO_PTR(FIFO, TYPE) _KFIFO_PTR_TYPE(, TYPE) FIFO 62 #define DECLARE_KFIFO(FIFO, TYPE, N) _KFIFO_TYPE(, TYPE, N) FIFO 63 64 #define INIT_KFIFO(FIFO) do \ 65 { \ 66 _init_kfifo(&(FIFO).kf_meta, sizeof((FIFO).kf_buf)); \ 67 } while (0) 68 69 #define FINI_KFIFO(FIFO) do \ 70 { \ 71 _fini_kfifo(&(FIFO).kf_meta); \ 72 } while (0) 73 74 static inline void 75 _init_kfifo(struct kfifo_meta *meta, size_t nbytes) 76 { 77 78 mutex_init(&meta->kfm_lock, MUTEX_DEFAULT, IPL_VM); 79 meta->kfm_head = 0; 80 meta->kfm_tail = 0; 81 meta->kfm_nbytes = nbytes; 82 } 83 84 static inline void 85 _fini_kfifo(struct kfifo_meta *meta) 86 { 87 88 mutex_destroy(&meta->kfm_lock); 89 } 90 91 _KFIFO_PTR_TYPE(kfifo, void); 92 93 #define kfifo_alloc(FIFO, SIZE, GFP) \ 94 _kfifo_alloc(&(FIFO)->kf_meta, &(FIFO)->kf_buf, (SIZE), (GFP)) 95 96 static inline int 97 _kfifo_alloc(struct kfifo_meta *meta, void *bufp, size_t nbytes, gfp_t gfp) 98 { 99 void *buf; 100 101 buf = kmalloc(nbytes, gfp); 102 if (buf == NULL) 103 return -ENOMEM; 104 105 /* Type pun! Hope void * == struct whatever *. */ 106 memcpy(bufp, &buf, sizeof(void *)); 107 108 _init_kfifo(meta, nbytes); 109 110 return 0; 111 } 112 113 #define kfifo_free(FIFO) \ 114 _kfifo_free(&(FIFO)->kf_meta, &(FIFO)->kf_buf) 115 116 static inline void 117 _kfifo_free(struct kfifo_meta *meta, void *bufp) 118 { 119 void *buf; 120 121 mutex_destroy(&meta->kfm_lock); 122 123 memcpy(&buf, bufp, sizeof(void *)); 124 kfree(buf); 125 126 /* Paranoia. */ 127 buf = NULL; 128 memcpy(bufp, &buf, sizeof(void *)); 129 } 130 131 #define kfifo_is_empty(FIFO) (kfifo_len(FIFO) == 0) 132 #define kfifo_len(FIFO) _kfifo_len(&(FIFO)->kf_meta) 133 134 static inline size_t 135 _kfifo_len(struct kfifo_meta *meta) 136 { 137 const size_t head = meta->kfm_head; 138 const size_t tail = meta->kfm_tail; 139 const size_t nbytes = meta->kfm_nbytes; 140 141 return (head <= tail ? tail - head : nbytes + tail - head); 142 } 143 144 #define kfifo_out_peek(FIFO, PTR, SIZE) \ 145 _kfifo_out_peek(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 146 147 static inline size_t 148 _kfifo_out_peek(struct kfifo_meta *meta, void *buf, void *ptr, size_t size) 149 { 150 const char *src = buf; 151 char *dst = ptr; 152 size_t copied = 0; 153 154 mutex_spin_enter(&meta->kfm_lock); 155 const size_t head = meta->kfm_head; 156 const size_t tail = meta->kfm_tail; 157 const size_t nbytes = meta->kfm_nbytes; 158 if (head <= tail) { 159 if (size <= tail - head) { 160 memcpy(dst, src + head, size); 161 copied = size; 162 } 163 } else { 164 if (size <= nbytes - head) { 165 memcpy(dst, src + head, size); 166 copied = size; 167 } else if (size <= nbytes + tail - head) { 168 memcpy(dst, src + head, nbytes - head); 169 memcpy(dst + nbytes - head, src, 170 size - (nbytes - head)); 171 copied = size; 172 } 173 } 174 mutex_spin_exit(&meta->kfm_lock); 175 176 return copied; 177 } 178 179 #define kfifo_out(FIFO, PTR, SIZE) \ 180 _kfifo_out(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 181 182 static inline size_t 183 _kfifo_out(struct kfifo_meta *meta, const void *buf, void *ptr, size_t size) 184 { 185 const char *src = buf; 186 char *dst = ptr; 187 size_t copied = 0; 188 189 mutex_spin_enter(&meta->kfm_lock); 190 const size_t head = meta->kfm_head; 191 const size_t tail = meta->kfm_tail; 192 const size_t nbytes = meta->kfm_nbytes; 193 if (head <= tail) { 194 if (size <= tail - head) { 195 memcpy(dst, src + head, size); 196 meta->kfm_head = head + size; 197 copied = size; 198 } 199 } else { 200 if (size <= nbytes - head) { 201 memcpy(dst, src + head, size); 202 meta->kfm_head = head + size; 203 copied = size; 204 } else if (size <= nbytes + tail - head) { 205 memcpy(dst, src + head, nbytes - head); 206 memcpy(dst + nbytes - head, src, 207 size - (nbytes - head)); 208 meta->kfm_head = size - (nbytes - head); 209 copied = size; 210 } 211 } 212 mutex_spin_exit(&meta->kfm_lock); 213 214 return copied; 215 } 216 217 #define kfifo_in(FIFO, PTR, SIZE) \ 218 _kfifo_in(&(FIFO)->kf_meta, (FIFO)->kf_buf, (PTR), (SIZE)) 219 220 static inline size_t 221 _kfifo_in(struct kfifo_meta *meta, void *buf, const void *ptr, size_t size) 222 { 223 const char *src = ptr; 224 char *dst = buf; 225 size_t copied = 0; 226 227 mutex_spin_enter(&meta->kfm_lock); 228 const size_t head = meta->kfm_head; 229 const size_t tail = meta->kfm_tail; 230 const size_t nbytes = meta->kfm_nbytes; 231 if (tail <= head) { 232 if (size <= head - tail) { 233 memcpy(dst + tail, src, size); 234 meta->kfm_tail = tail + size; 235 copied = size; 236 } 237 } else { 238 if (size <= nbytes - tail) { 239 memcpy(dst + tail, src, size); 240 meta->kfm_tail = tail + size; 241 } else if (size <= nbytes + tail - head) { 242 memcpy(dst + tail, src, nbytes - tail); 243 memcpy(dst, src + nbytes - tail, 244 size - (nbytes - tail)); 245 meta->kfm_tail = size - (nbytes - tail); 246 copied = size; 247 } 248 } 249 mutex_spin_exit(&meta->kfm_lock); 250 251 return copied; 252 } 253 254 #endif /* _LINUX_KFIFO_H_ */ 255