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