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
_kfifo_alloc(struct kfifo_meta * meta,void * bufp,size_t nbytes,gfp_t gfp)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
_kfifo_free(struct kfifo_meta * meta,void * bufp)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
_kfifo_len(struct kfifo_meta * meta)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
_kfifo_out_peek(struct kfifo_meta * meta,void * buf,void * ptr,size_t size)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
_kfifo_out(struct kfifo_meta * meta,const void * buf,void * ptr,size_t size)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
_kfifo_in(struct kfifo_meta * meta,void * buf,const void * ptr,size_t size)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 struct __kfifo {
224 unsigned int in;
225 unsigned int out;
226 unsigned int mask;
227 unsigned int esize;
228 void *data;
229 };
230
231 #define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
232 union { \
233 struct __kfifo kfifo; \
234 datatype *type; \
235 const datatype *const_type; \
236 char (*rectype)[recsize]; \
237 ptrtype *ptr; \
238 ptrtype const *ptr_const; \
239 }
240
241 #define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
242 { \
243 __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
244 type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
245 }
246
247 #define _KFIFO_TYPE(TYPE, SIZE) \
248 struct __STRUCT_KFIFO(TYPE, SIZE, 0, TYPE)
249 /**
250 * DECLARE_KFIFO - macro to declare a fifo object
251 * @fifo: name of the declared fifo
252 * @type: type of the fifo elements
253 * @size: the number of elements in the fifo, this must be a power of 2
254 */
255 #define DECLARE_KFIFO(FIFO, TYPE, SIZE) _KFIFO_TYPE(TYPE, SIZE) FIFO
256
257 #endif /* _LINUX_KFIFO_H_ */
258