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