xref: /netbsd-src/sys/external/bsd/drm2/include/linux/kfifo.h (revision 3d10c246f0f85b5ffc369ea7cabe3e91e347f848)
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
_init_kfifo(struct kfifo_meta * meta,size_t nbytes)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
_fini_kfifo(struct kfifo_meta * meta)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
_kfifo_alloc(struct kfifo_meta * meta,void * bufp,size_t nbytes,gfp_t gfp)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
_kfifo_free(struct kfifo_meta * meta,void * bufp)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
_kfifo_len(struct kfifo_meta * meta)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
_kfifo_out_peek(struct kfifo_meta * meta,void * buf,void * ptr,size_t size)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
_kfifo_out(struct kfifo_meta * meta,const void * buf,void * ptr,size_t size)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
_kfifo_in(struct kfifo_meta * meta,void * buf,const void * ptr,size_t size)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