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 size
= roundup_pow_of_two(size
);
82 fifo
->mask
= size
- 1;
86 EXPORT_SYMBOL(__kfifo_init
);
88 static void kfifo_copy_in(struct __kfifo
*fifo
, const void *src
,
89 unsigned int len
, unsigned int off
)
91 unsigned int size
= fifo
->mask
+ 1;
92 unsigned int esize
= fifo
->esize
;
101 l
= min(len
, size
- off
);
103 memcpy(fifo
->data
+ off
, src
, l
);
104 memcpy(fifo
->data
, src
+ l
, len
- l
);
106 * make sure that the data in the fifo is up to date before
107 * incrementing the fifo->in index counter
112 unsigned int __kfifo_in(struct __kfifo
*fifo
,
113 const void *buf
, unsigned int len
)
117 l
= kfifo_unused(fifo
);
121 kfifo_copy_in(fifo
, buf
, len
, fifo
->in
);
125 EXPORT_SYMBOL(__kfifo_in
);
127 static void kfifo_copy_out(struct __kfifo
*fifo
, void *dst
,
128 unsigned int len
, unsigned int off
)
130 unsigned int size
= fifo
->mask
+ 1;
131 unsigned int esize
= fifo
->esize
;
140 l
= min(len
, size
- off
);
142 memcpy(dst
, fifo
->data
+ off
, l
);
143 memcpy(dst
+ l
, fifo
->data
, len
- l
);
145 * make sure that the data is copied before
146 * incrementing the fifo->out index counter
151 unsigned int __kfifo_out_peek(struct __kfifo
*fifo
,
152 void *buf
, unsigned int len
)
156 l
= fifo
->in
- fifo
->out
;
160 kfifo_copy_out(fifo
, buf
, len
, fifo
->out
);
163 EXPORT_SYMBOL(__kfifo_out_peek
);
165 unsigned int __kfifo_out(struct __kfifo
*fifo
,
166 void *buf
, unsigned int len
)
168 len
= __kfifo_out_peek(fifo
, buf
, len
);
172 EXPORT_SYMBOL(__kfifo_out
);
174 static unsigned long kfifo_copy_from_user(struct __kfifo
*fifo
,
175 const void __user
*from
, unsigned int len
, unsigned int off
,
176 unsigned int *copied
)
178 unsigned int size
= fifo
->mask
+ 1;
179 unsigned int esize
= fifo
->esize
;
189 l
= min(len
, size
- off
);
191 ret
= copy_from_user(fifo
->data
+ off
, from
, l
);
193 ret
= DIV_ROUND_UP(ret
+ len
- l
, esize
);
195 ret
= copy_from_user(fifo
->data
, from
+ l
, len
- l
);
197 ret
= DIV_ROUND_UP(ret
, esize
);
200 * make sure that the data in the fifo is up to date before
201 * incrementing the fifo->in index counter
204 *copied
= len
- ret
* esize
;
205 /* return the number of elements which are not copied */
209 int __kfifo_from_user(struct __kfifo
*fifo
, const void __user
*from
,
210 unsigned long len
, unsigned int *copied
)
214 unsigned int esize
= fifo
->esize
;
220 l
= kfifo_unused(fifo
);
224 ret
= kfifo_copy_from_user(fifo
, from
, len
, fifo
->in
, copied
);
233 EXPORT_SYMBOL(__kfifo_from_user
);
235 static unsigned long kfifo_copy_to_user(struct __kfifo
*fifo
, void __user
*to
,
236 unsigned int len
, unsigned int off
, unsigned int *copied
)
240 unsigned int size
= fifo
->mask
+ 1;
241 unsigned int esize
= fifo
->esize
;
249 l
= min(len
, size
- off
);
251 ret
= copy_to_user(to
, fifo
->data
+ off
, l
);
253 ret
= DIV_ROUND_UP(ret
+ len
- l
, esize
);
255 ret
= copy_to_user(to
+ l
, fifo
->data
, len
- l
);
257 ret
= DIV_ROUND_UP(ret
, esize
);
260 * make sure that the data is copied before
261 * incrementing the fifo->out index counter
264 *copied
= len
- ret
* esize
;
265 /* return the number of elements which are not copied */
269 int __kfifo_to_user(struct __kfifo
*fifo
, void __user
*to
,
270 unsigned long len
, unsigned int *copied
)
274 unsigned int esize
= fifo
->esize
;
280 l
= fifo
->in
- fifo
->out
;
283 ret
= kfifo_copy_to_user(fifo
, to
, len
, fifo
->out
, copied
);
292 EXPORT_SYMBOL(__kfifo_to_user
);
294 static int setup_sgl_buf(struct scatterlist
*sgl
, void *buf
,
295 int nents
, unsigned int len
)
309 page
= virt_to_page(buf
);
310 off
= offset_in_page(buf
);
313 while (len
>= l
+ PAGE_SIZE
- off
) {
318 npage
= virt_to_page(buf
);
319 if (page_to_phys(page
) != page_to_phys(npage
) - l
) {
320 sg_set_page(sgl
, page
, l
- off
, off
);
322 if (++n
== nents
|| sgl
== NULL
)
329 sg_set_page(sgl
, page
, len
, off
);
333 static unsigned int setup_sgl(struct __kfifo
*fifo
, struct scatterlist
*sgl
,
334 int nents
, unsigned int len
, unsigned int off
)
336 unsigned int size
= fifo
->mask
+ 1;
337 unsigned int esize
= fifo
->esize
;
347 l
= min(len
, size
- off
);
349 n
= setup_sgl_buf(sgl
, fifo
->data
+ off
, nents
, l
);
350 n
+= setup_sgl_buf(sgl
+ n
, fifo
->data
, nents
- n
, len
- l
);
355 unsigned int __kfifo_dma_in_prepare(struct __kfifo
*fifo
,
356 struct scatterlist
*sgl
, int nents
, unsigned int len
)
360 l
= kfifo_unused(fifo
);
364 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->in
);
366 EXPORT_SYMBOL(__kfifo_dma_in_prepare
);
368 unsigned int __kfifo_dma_out_prepare(struct __kfifo
*fifo
,
369 struct scatterlist
*sgl
, int nents
, unsigned int len
)
373 l
= fifo
->in
- fifo
->out
;
377 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->out
);
379 EXPORT_SYMBOL(__kfifo_dma_out_prepare
);
381 unsigned int __kfifo_max_r(unsigned int len
, size_t recsize
)
383 unsigned int max
= (1 << (recsize
<< 3)) - 1;
389 EXPORT_SYMBOL(__kfifo_max_r
);
391 #define __KFIFO_PEEK(data, out, mask) \
392 ((data)[(out) & (mask)])
394 * __kfifo_peek_n internal helper function for determinate the length of
395 * the next record in the fifo
397 static unsigned int __kfifo_peek_n(struct __kfifo
*fifo
, size_t recsize
)
400 unsigned int mask
= fifo
->mask
;
401 unsigned char *data
= fifo
->data
;
403 l
= __KFIFO_PEEK(data
, fifo
->out
, mask
);
406 l
|= __KFIFO_PEEK(data
, fifo
->out
+ 1, mask
) << 8;
411 #define __KFIFO_POKE(data, in, mask, val) \
413 (data)[(in) & (mask)] = (unsigned char)(val) \
417 * __kfifo_poke_n internal helper function for storeing the length of
418 * the record into the fifo
420 static void __kfifo_poke_n(struct __kfifo
*fifo
, unsigned int n
, size_t recsize
)
422 unsigned int mask
= fifo
->mask
;
423 unsigned char *data
= fifo
->data
;
425 __KFIFO_POKE(data
, fifo
->in
, mask
, n
);
428 __KFIFO_POKE(data
, fifo
->in
+ 1, mask
, n
>> 8);
431 unsigned int __kfifo_len_r(struct __kfifo
*fifo
, size_t recsize
)
433 return __kfifo_peek_n(fifo
, recsize
);
435 EXPORT_SYMBOL(__kfifo_len_r
);
437 unsigned int __kfifo_in_r(struct __kfifo
*fifo
, const void *buf
,
438 unsigned int len
, size_t recsize
)
440 if (len
+ recsize
> kfifo_unused(fifo
))
443 __kfifo_poke_n(fifo
, len
, recsize
);
445 kfifo_copy_in(fifo
, buf
, len
, fifo
->in
+ recsize
);
446 fifo
->in
+= len
+ recsize
;
449 EXPORT_SYMBOL(__kfifo_in_r
);
451 static unsigned int kfifo_out_copy_r(struct __kfifo
*fifo
,
452 void *buf
, unsigned int len
, size_t recsize
, unsigned int *n
)
454 *n
= __kfifo_peek_n(fifo
, recsize
);
459 kfifo_copy_out(fifo
, buf
, len
, fifo
->out
+ recsize
);
463 unsigned int __kfifo_out_peek_r(struct __kfifo
*fifo
, void *buf
,
464 unsigned int len
, size_t recsize
)
468 if (fifo
->in
== fifo
->out
)
471 return kfifo_out_copy_r(fifo
, buf
, len
, recsize
, &n
);
473 EXPORT_SYMBOL(__kfifo_out_peek_r
);
475 unsigned int __kfifo_out_r(struct __kfifo
*fifo
, void *buf
,
476 unsigned int len
, size_t recsize
)
480 if (fifo
->in
== fifo
->out
)
483 len
= kfifo_out_copy_r(fifo
, buf
, len
, recsize
, &n
);
484 fifo
->out
+= n
+ recsize
;
487 EXPORT_SYMBOL(__kfifo_out_r
);
489 void __kfifo_skip_r(struct __kfifo
*fifo
, size_t recsize
)
493 n
= __kfifo_peek_n(fifo
, recsize
);
494 fifo
->out
+= n
+ recsize
;
496 EXPORT_SYMBOL(__kfifo_skip_r
);
498 int __kfifo_from_user_r(struct __kfifo
*fifo
, const void __user
*from
,
499 unsigned long len
, unsigned int *copied
, size_t recsize
)
503 len
= __kfifo_max_r(len
, recsize
);
505 if (len
+ recsize
> kfifo_unused(fifo
)) {
510 __kfifo_poke_n(fifo
, len
, recsize
);
512 ret
= kfifo_copy_from_user(fifo
, from
, len
, fifo
->in
+ recsize
, copied
);
517 fifo
->in
+= len
+ recsize
;
520 EXPORT_SYMBOL(__kfifo_from_user_r
);
522 int __kfifo_to_user_r(struct __kfifo
*fifo
, void __user
*to
,
523 unsigned long len
, unsigned int *copied
, size_t recsize
)
528 if (fifo
->in
== fifo
->out
) {
533 n
= __kfifo_peek_n(fifo
, recsize
);
537 ret
= kfifo_copy_to_user(fifo
, to
, len
, fifo
->out
+ recsize
, copied
);
542 fifo
->out
+= n
+ recsize
;
545 EXPORT_SYMBOL(__kfifo_to_user_r
);
547 unsigned int __kfifo_dma_in_prepare_r(struct __kfifo
*fifo
,
548 struct scatterlist
*sgl
, int nents
, unsigned int len
, size_t recsize
)
552 len
= __kfifo_max_r(len
, recsize
);
554 if (len
+ recsize
> kfifo_unused(fifo
))
557 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->in
+ recsize
);
559 EXPORT_SYMBOL(__kfifo_dma_in_prepare_r
);
561 void __kfifo_dma_in_finish_r(struct __kfifo
*fifo
,
562 unsigned int len
, size_t recsize
)
564 len
= __kfifo_max_r(len
, recsize
);
565 __kfifo_poke_n(fifo
, len
, recsize
);
566 fifo
->in
+= len
+ recsize
;
568 EXPORT_SYMBOL(__kfifo_dma_in_finish_r
);
570 unsigned int __kfifo_dma_out_prepare_r(struct __kfifo
*fifo
,
571 struct scatterlist
*sgl
, int nents
, unsigned int len
, size_t recsize
)
575 len
= __kfifo_max_r(len
, recsize
);
577 if (len
+ recsize
> fifo
->in
- fifo
->out
)
580 return setup_sgl(fifo
, sgl
, nents
, len
, fifo
->out
+ recsize
);
582 EXPORT_SYMBOL(__kfifo_dma_out_prepare_r
);
584 void __kfifo_dma_out_finish_r(struct __kfifo
*fifo
, size_t recsize
)
588 len
= __kfifo_peek_n(fifo
, recsize
);
589 fifo
->out
+= len
+ recsize
;
591 EXPORT_SYMBOL(__kfifo_dma_out_finish_r
);