1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * A generic kernel FIFO implementation
5 * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
8 #include <linux/kernel.h>
9 #include <linux/export.h>
10 #include <linux/slab.h>
11 #include <linux/err.h>
12 #include <linux/log2.h>
13 #include <linux/uaccess.h>
14 #include <linux/kfifo.h>
17 * internal helper to calculate the unused elements in a fifo
19 static inline unsigned int kfifo_unused(struct __kfifo
*fifo
)
21 return (fifo
->mask
+ 1) - (fifo
->in
- fifo
->out
);
24 int __kfifo_alloc(struct __kfifo
*fifo
, unsigned int size
,
25 size_t esize
, gfp_t gfp_mask
)
28 * round up to the next power of 2, since our 'let the indices
29 * wrap' technique works only in this case.
31 size
= roundup_pow_of_two(size
);
43 fifo
->data
= kmalloc_array(esize
, size
, gfp_mask
);
49 fifo
->mask
= size
- 1;
53 EXPORT_SYMBOL(__kfifo_alloc
);
55 void __kfifo_free(struct __kfifo
*fifo
)
64 EXPORT_SYMBOL(__kfifo_free
);
66 int __kfifo_init(struct __kfifo
*fifo
, void *buffer
,
67 unsigned int size
, size_t esize
)
71 if (!is_power_of_2(size
))
72 size
= rounddown_pow_of_two(size
);
83 fifo
->mask
= size
- 1;
87 EXPORT_SYMBOL(__kfifo_init
);
89 static void kfifo_copy_in(struct __kfifo
*fifo
, const void *src
,
90 unsigned int len
, unsigned int off
)
92 unsigned int size
= fifo
->mask
+ 1;
93 unsigned int esize
= fifo
->esize
;
102 l
= min(len
, size
- off
);
104 memcpy(fifo
->data
+ off
, src
, l
);
105 memcpy(fifo
->data
, src
+ l
, len
- l
);
107 * make sure that the data in the fifo is up to date before
108 * incrementing the fifo->in index counter
113 unsigned int __kfifo_in(struct __kfifo
*fifo
,
114 const void *buf
, unsigned int len
)
118 l
= kfifo_unused(fifo
);
122 kfifo_copy_in(fifo
, buf
, len
, fifo
->in
);
126 EXPORT_SYMBOL(__kfifo_in
);
128 static void kfifo_copy_out(struct __kfifo
*fifo
, void *dst
,
129 unsigned int len
, unsigned int off
)
131 unsigned int size
= fifo
->mask
+ 1;
132 unsigned int esize
= fifo
->esize
;
141 l
= min(len
, size
- off
);
143 memcpy(dst
, fifo
->data
+ off
, l
);
144 memcpy(dst
+ l
, fifo
->data
, len
- l
);
146 * make sure that the data is copied before
147 * incrementing the fifo->out index counter
152 unsigned int __kfifo_out_peek(struct __kfifo
*fifo
,
153 void *buf
, unsigned int len
)
157 l
= fifo
->in
- fifo
->out
;
161 kfifo_copy_out(fifo
, buf
, len
, fifo
->out
);
164 EXPORT_SYMBOL(__kfifo_out_peek
);
166 unsigned int __kfifo_out(struct __kfifo
*fifo
,
167 void *buf
, unsigned int len
)
169 len
= __kfifo_out_peek(fifo
, buf
, len
);
173 EXPORT_SYMBOL(__kfifo_out
);
175 static unsigned long kfifo_copy_from_user(struct __kfifo
*fifo
,
176 const void __user
*from
, unsigned int len
, unsigned int off
,
177 unsigned int *copied
)
179 unsigned int size
= fifo
->mask
+ 1;
180 unsigned int esize
= fifo
->esize
;
190 l
= min(len
, size
- off
);
192 ret
= copy_from_user(fifo
->data
+ off
, from
, l
);
194 ret
= DIV_ROUND_UP(ret
+ len
- l
, esize
);
196 ret
= copy_from_user(fifo
->data
, from
+ l
, len
- l
);
198 ret
= DIV_ROUND_UP(ret
, esize
);
201 * make sure that the data in the fifo is up to date before
202 * incrementing the fifo->in index counter
205 *copied
= len
- ret
* esize
;
206 /* return the number of elements which are not copied */
210 int __kfifo_from_user(struct __kfifo
*fifo
, const void __user
*from
,
211 unsigned long len
, unsigned int *copied
)
215 unsigned int esize
= fifo
->esize
;
221 l
= kfifo_unused(fifo
);
225 ret
= kfifo_copy_from_user(fifo
, from
, len
, fifo
->in
, copied
);
234 EXPORT_SYMBOL(__kfifo_from_user
);
236 static unsigned long kfifo_copy_to_user(struct __kfifo
*fifo
, void __user
*to
,
237 unsigned int len
, unsigned int off
, unsigned int *copied
)
241 unsigned int size
= fifo
->mask
+ 1;
242 unsigned int esize
= fifo
->esize
;
250 l
= min(len
, size
- off
);
252 ret
= copy_to_user(to
, fifo
->data
+ off
, l
);
254 ret
= DIV_ROUND_UP(ret
+ len
- l
, esize
);
256 ret
= copy_to_user(to
+ l
, fifo
->data
, len
- l
);
258 ret
= DIV_ROUND_UP(ret
, esize
);
261 * make sure that the data is copied before
262 * incrementing the fifo->out index counter
265 *copied
= len
- ret
* esize
;
266 /* return the number of elements which are not copied */
270 int __kfifo_to_user(struct __kfifo
*fifo
, void __user
*to
,
271 unsigned long len
, unsigned int *copied
)
275 unsigned int esize
= fifo
->esize
;
281 l
= fifo
->in
- fifo
->out
;
284 ret
= kfifo_copy_to_user(fifo
, to
, len
, fifo
->out
, copied
);
293 EXPORT_SYMBOL(__kfifo_to_user
);
295 static int setup_sgl_buf(struct scatterlist
*sgl
, void *buf
,
296 int nents
, unsigned int len
)
310 page
= virt_to_page(buf
);
311 off
= offset_in_page(buf
);
314 while (len
>= l
+ PAGE_SIZE
- off
) {
319 npage
= virt_to_page(buf
);
320 if (page_to_phys(page
) != page_to_phys(npage
) - l
) {
321 sg_set_page(sgl
, page
, l
- off
, off
);
323 if (++n
== nents
|| sgl
== NULL
)
330 sg_set_page(sgl
, page
, len
, off
);
334 static unsigned int setup_sgl(struct __kfifo
*fifo
, struct scatterlist
*sgl
,
335 int nents
, unsigned int len
, unsigned int off
)
337 unsigned int size
= fifo
->mask
+ 1;
338 unsigned int esize
= fifo
->esize
;
348 l
= min(len
, size
- off
);
350 n
= setup_sgl_buf(sgl
, fifo
->data
+ off
, nents
, l
);
351 n
+= setup_sgl_buf(sgl
+ n
, fifo
->data
, nents
- n
, len
- l
);
356 unsigned int __kfifo_dma_in_prepare(struct __kfifo
*fifo
,
357 struct scatterlist
*sgl
, int nents
, unsigned int len
)
361 l
= kfifo_unused(fifo
);
365 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->in
);
367 EXPORT_SYMBOL(__kfifo_dma_in_prepare
);
369 unsigned int __kfifo_dma_out_prepare(struct __kfifo
*fifo
,
370 struct scatterlist
*sgl
, int nents
, unsigned int len
)
374 l
= fifo
->in
- fifo
->out
;
378 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->out
);
380 EXPORT_SYMBOL(__kfifo_dma_out_prepare
);
382 unsigned int __kfifo_max_r(unsigned int len
, size_t recsize
)
384 unsigned int max
= (1 << (recsize
<< 3)) - 1;
390 EXPORT_SYMBOL(__kfifo_max_r
);
392 #define __KFIFO_PEEK(data, out, mask) \
393 ((data)[(out) & (mask)])
395 * __kfifo_peek_n internal helper function for determinate the length of
396 * the next record in the fifo
398 static unsigned int __kfifo_peek_n(struct __kfifo
*fifo
, size_t recsize
)
401 unsigned int mask
= fifo
->mask
;
402 unsigned char *data
= fifo
->data
;
404 l
= __KFIFO_PEEK(data
, fifo
->out
, mask
);
407 l
|= __KFIFO_PEEK(data
, fifo
->out
+ 1, mask
) << 8;
412 #define __KFIFO_POKE(data, in, mask, val) \
414 (data)[(in) & (mask)] = (unsigned char)(val) \
418 * __kfifo_poke_n internal helper function for storing the length of
419 * the record into the fifo
421 static void __kfifo_poke_n(struct __kfifo
*fifo
, unsigned int n
, size_t recsize
)
423 unsigned int mask
= fifo
->mask
;
424 unsigned char *data
= fifo
->data
;
426 __KFIFO_POKE(data
, fifo
->in
, mask
, n
);
429 __KFIFO_POKE(data
, fifo
->in
+ 1, mask
, n
>> 8);
432 unsigned int __kfifo_len_r(struct __kfifo
*fifo
, size_t recsize
)
434 return __kfifo_peek_n(fifo
, recsize
);
436 EXPORT_SYMBOL(__kfifo_len_r
);
438 unsigned int __kfifo_in_r(struct __kfifo
*fifo
, const void *buf
,
439 unsigned int len
, size_t recsize
)
441 if (len
+ recsize
> kfifo_unused(fifo
))
444 __kfifo_poke_n(fifo
, len
, recsize
);
446 kfifo_copy_in(fifo
, buf
, len
, fifo
->in
+ recsize
);
447 fifo
->in
+= len
+ recsize
;
450 EXPORT_SYMBOL(__kfifo_in_r
);
452 static unsigned int kfifo_out_copy_r(struct __kfifo
*fifo
,
453 void *buf
, unsigned int len
, size_t recsize
, unsigned int *n
)
455 *n
= __kfifo_peek_n(fifo
, recsize
);
460 kfifo_copy_out(fifo
, buf
, len
, fifo
->out
+ recsize
);
464 unsigned int __kfifo_out_peek_r(struct __kfifo
*fifo
, void *buf
,
465 unsigned int len
, size_t recsize
)
469 if (fifo
->in
== fifo
->out
)
472 return kfifo_out_copy_r(fifo
, buf
, len
, recsize
, &n
);
474 EXPORT_SYMBOL(__kfifo_out_peek_r
);
476 unsigned int __kfifo_out_r(struct __kfifo
*fifo
, void *buf
,
477 unsigned int len
, size_t recsize
)
481 if (fifo
->in
== fifo
->out
)
484 len
= kfifo_out_copy_r(fifo
, buf
, len
, recsize
, &n
);
485 fifo
->out
+= n
+ recsize
;
488 EXPORT_SYMBOL(__kfifo_out_r
);
490 void __kfifo_skip_r(struct __kfifo
*fifo
, size_t recsize
)
494 n
= __kfifo_peek_n(fifo
, recsize
);
495 fifo
->out
+= n
+ recsize
;
497 EXPORT_SYMBOL(__kfifo_skip_r
);
499 int __kfifo_from_user_r(struct __kfifo
*fifo
, const void __user
*from
,
500 unsigned long len
, unsigned int *copied
, size_t recsize
)
504 len
= __kfifo_max_r(len
, recsize
);
506 if (len
+ recsize
> kfifo_unused(fifo
)) {
511 __kfifo_poke_n(fifo
, len
, recsize
);
513 ret
= kfifo_copy_from_user(fifo
, from
, len
, fifo
->in
+ recsize
, copied
);
518 fifo
->in
+= len
+ recsize
;
521 EXPORT_SYMBOL(__kfifo_from_user_r
);
523 int __kfifo_to_user_r(struct __kfifo
*fifo
, void __user
*to
,
524 unsigned long len
, unsigned int *copied
, size_t recsize
)
529 if (fifo
->in
== fifo
->out
) {
534 n
= __kfifo_peek_n(fifo
, recsize
);
538 ret
= kfifo_copy_to_user(fifo
, to
, len
, fifo
->out
+ recsize
, copied
);
543 fifo
->out
+= n
+ recsize
;
546 EXPORT_SYMBOL(__kfifo_to_user_r
);
548 unsigned int __kfifo_dma_in_prepare_r(struct __kfifo
*fifo
,
549 struct scatterlist
*sgl
, int nents
, unsigned int len
, size_t recsize
)
553 len
= __kfifo_max_r(len
, recsize
);
555 if (len
+ recsize
> kfifo_unused(fifo
))
558 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->in
+ recsize
);
560 EXPORT_SYMBOL(__kfifo_dma_in_prepare_r
);
562 void __kfifo_dma_in_finish_r(struct __kfifo
*fifo
,
563 unsigned int len
, size_t recsize
)
565 len
= __kfifo_max_r(len
, recsize
);
566 __kfifo_poke_n(fifo
, len
, recsize
);
567 fifo
->in
+= len
+ recsize
;
569 EXPORT_SYMBOL(__kfifo_dma_in_finish_r
);
571 unsigned int __kfifo_dma_out_prepare_r(struct __kfifo
*fifo
,
572 struct scatterlist
*sgl
, int nents
, unsigned int len
, size_t recsize
)
576 len
= __kfifo_max_r(len
, recsize
);
578 if (len
+ recsize
> fifo
->in
- fifo
->out
)
581 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->out
+ recsize
);
583 EXPORT_SYMBOL(__kfifo_dma_out_prepare_r
);
585 void __kfifo_dma_out_finish_r(struct __kfifo
*fifo
, size_t recsize
)
589 len
= __kfifo_peek_n(fifo
, recsize
);
590 fifo
->out
+= len
+ recsize
;
592 EXPORT_SYMBOL(__kfifo_dma_out_finish_r
);