xref: /linux/lib/kfifo.c (revision 9dbbc3b9)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c759b35eSStefani Seibold /*
3c759b35eSStefani Seibold  * A generic kernel FIFO implementation
4c759b35eSStefani Seibold  *
5c759b35eSStefani Seibold  * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
6c759b35eSStefani Seibold  */
7c759b35eSStefani Seibold 
8c759b35eSStefani Seibold #include <linux/kernel.h>
9c759b35eSStefani Seibold #include <linux/export.h>
10c759b35eSStefani Seibold #include <linux/slab.h>
11c759b35eSStefani Seibold #include <linux/err.h>
12c759b35eSStefani Seibold #include <linux/log2.h>
13c759b35eSStefani Seibold #include <linux/uaccess.h>
14c759b35eSStefani Seibold #include <linux/kfifo.h>
15c759b35eSStefani Seibold 
16c759b35eSStefani Seibold /*
17c759b35eSStefani Seibold  * internal helper to calculate the unused elements in a fifo
18c759b35eSStefani Seibold  */
kfifo_unused(struct __kfifo * fifo)19c759b35eSStefani Seibold static inline unsigned int kfifo_unused(struct __kfifo *fifo)
20c759b35eSStefani Seibold {
21c759b35eSStefani Seibold 	return (fifo->mask + 1) - (fifo->in - fifo->out);
22c759b35eSStefani Seibold }
23c759b35eSStefani Seibold 
__kfifo_alloc(struct __kfifo * fifo,unsigned int size,size_t esize,gfp_t gfp_mask)24c759b35eSStefani Seibold int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
25c759b35eSStefani Seibold 		size_t esize, gfp_t gfp_mask)
26c759b35eSStefani Seibold {
27c759b35eSStefani Seibold 	/*
2875a24b82SMartin Kelly 	 * round up to the next power of 2, since our 'let the indices
29c759b35eSStefani Seibold 	 * wrap' technique works only in this case.
30c759b35eSStefani Seibold 	 */
31dfe2a77fSStefani Seibold 	size = roundup_pow_of_two(size);
32c759b35eSStefani Seibold 
33c759b35eSStefani Seibold 	fifo->in = 0;
34c759b35eSStefani Seibold 	fifo->out = 0;
35c759b35eSStefani Seibold 	fifo->esize = esize;
36c759b35eSStefani Seibold 
37c759b35eSStefani Seibold 	if (size < 2) {
38c759b35eSStefani Seibold 		fifo->data = NULL;
39c759b35eSStefani Seibold 		fifo->mask = 0;
40c759b35eSStefani Seibold 		return -EINVAL;
41c759b35eSStefani Seibold 	}
42c759b35eSStefani Seibold 
436da2ec56SKees Cook 	fifo->data = kmalloc_array(esize, size, gfp_mask);
44c759b35eSStefani Seibold 
45c759b35eSStefani Seibold 	if (!fifo->data) {
46c759b35eSStefani Seibold 		fifo->mask = 0;
47c759b35eSStefani Seibold 		return -ENOMEM;
48c759b35eSStefani Seibold 	}
49c759b35eSStefani Seibold 	fifo->mask = size - 1;
50c759b35eSStefani Seibold 
51c759b35eSStefani Seibold 	return 0;
52c759b35eSStefani Seibold }
53c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_alloc);
54c759b35eSStefani Seibold 
__kfifo_free(struct __kfifo * fifo)55c759b35eSStefani Seibold void __kfifo_free(struct __kfifo *fifo)
56c759b35eSStefani Seibold {
57c759b35eSStefani Seibold 	kfree(fifo->data);
58c759b35eSStefani Seibold 	fifo->in = 0;
59c759b35eSStefani Seibold 	fifo->out = 0;
60c759b35eSStefani Seibold 	fifo->esize = 0;
61c759b35eSStefani Seibold 	fifo->data = NULL;
62c759b35eSStefani Seibold 	fifo->mask = 0;
63c759b35eSStefani Seibold }
64c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_free);
65c759b35eSStefani Seibold 
__kfifo_init(struct __kfifo * fifo,void * buffer,unsigned int size,size_t esize)66c759b35eSStefani Seibold int __kfifo_init(struct __kfifo *fifo, void *buffer,
67c759b35eSStefani Seibold 		unsigned int size, size_t esize)
68c759b35eSStefani Seibold {
69c759b35eSStefani Seibold 	size /= esize;
70c759b35eSStefani Seibold 
71ab9bb631SLinus Torvalds 	if (!is_power_of_2(size))
72ab9bb631SLinus Torvalds 		size = rounddown_pow_of_two(size);
73c759b35eSStefani Seibold 
74c759b35eSStefani Seibold 	fifo->in = 0;
75c759b35eSStefani Seibold 	fifo->out = 0;
76c759b35eSStefani Seibold 	fifo->esize = esize;
77c759b35eSStefani Seibold 	fifo->data = buffer;
78c759b35eSStefani Seibold 
79c759b35eSStefani Seibold 	if (size < 2) {
80c759b35eSStefani Seibold 		fifo->mask = 0;
81c759b35eSStefani Seibold 		return -EINVAL;
82c759b35eSStefani Seibold 	}
83c759b35eSStefani Seibold 	fifo->mask = size - 1;
84c759b35eSStefani Seibold 
85c759b35eSStefani Seibold 	return 0;
86c759b35eSStefani Seibold }
87c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_init);
88c759b35eSStefani Seibold 
kfifo_copy_in(struct __kfifo * fifo,const void * src,unsigned int len,unsigned int off)89c759b35eSStefani Seibold static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
90c759b35eSStefani Seibold 		unsigned int len, unsigned int off)
91c759b35eSStefani Seibold {
92c759b35eSStefani Seibold 	unsigned int size = fifo->mask + 1;
93c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
94c759b35eSStefani Seibold 	unsigned int l;
95c759b35eSStefani Seibold 
96c759b35eSStefani Seibold 	off &= fifo->mask;
97c759b35eSStefani Seibold 	if (esize != 1) {
98c759b35eSStefani Seibold 		off *= esize;
99c759b35eSStefani Seibold 		size *= esize;
100c759b35eSStefani Seibold 		len *= esize;
101c759b35eSStefani Seibold 	}
102c759b35eSStefani Seibold 	l = min(len, size - off);
103c759b35eSStefani Seibold 
104c759b35eSStefani Seibold 	memcpy(fifo->data + off, src, l);
105c759b35eSStefani Seibold 	memcpy(fifo->data, src + l, len - l);
106c759b35eSStefani Seibold 	/*
107c759b35eSStefani Seibold 	 * make sure that the data in the fifo is up to date before
108c759b35eSStefani Seibold 	 * incrementing the fifo->in index counter
109c759b35eSStefani Seibold 	 */
110c759b35eSStefani Seibold 	smp_wmb();
111c759b35eSStefani Seibold }
112c759b35eSStefani Seibold 
__kfifo_in(struct __kfifo * fifo,const void * buf,unsigned int len)113c759b35eSStefani Seibold unsigned int __kfifo_in(struct __kfifo *fifo,
114c759b35eSStefani Seibold 		const void *buf, unsigned int len)
115c759b35eSStefani Seibold {
116c759b35eSStefani Seibold 	unsigned int l;
117c759b35eSStefani Seibold 
118c759b35eSStefani Seibold 	l = kfifo_unused(fifo);
119c759b35eSStefani Seibold 	if (len > l)
120c759b35eSStefani Seibold 		len = l;
121c759b35eSStefani Seibold 
122c759b35eSStefani Seibold 	kfifo_copy_in(fifo, buf, len, fifo->in);
123c759b35eSStefani Seibold 	fifo->in += len;
124c759b35eSStefani Seibold 	return len;
125c759b35eSStefani Seibold }
126c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_in);
127c759b35eSStefani Seibold 
kfifo_copy_out(struct __kfifo * fifo,void * dst,unsigned int len,unsigned int off)128c759b35eSStefani Seibold static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
129c759b35eSStefani Seibold 		unsigned int len, unsigned int off)
130c759b35eSStefani Seibold {
131c759b35eSStefani Seibold 	unsigned int size = fifo->mask + 1;
132c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
133c759b35eSStefani Seibold 	unsigned int l;
134c759b35eSStefani Seibold 
135c759b35eSStefani Seibold 	off &= fifo->mask;
136c759b35eSStefani Seibold 	if (esize != 1) {
137c759b35eSStefani Seibold 		off *= esize;
138c759b35eSStefani Seibold 		size *= esize;
139c759b35eSStefani Seibold 		len *= esize;
140c759b35eSStefani Seibold 	}
141c759b35eSStefani Seibold 	l = min(len, size - off);
142c759b35eSStefani Seibold 
143c759b35eSStefani Seibold 	memcpy(dst, fifo->data + off, l);
144c759b35eSStefani Seibold 	memcpy(dst + l, fifo->data, len - l);
145c759b35eSStefani Seibold 	/*
146c759b35eSStefani Seibold 	 * make sure that the data is copied before
147c759b35eSStefani Seibold 	 * incrementing the fifo->out index counter
148c759b35eSStefani Seibold 	 */
149c759b35eSStefani Seibold 	smp_wmb();
150c759b35eSStefani Seibold }
151c759b35eSStefani Seibold 
__kfifo_out_peek(struct __kfifo * fifo,void * buf,unsigned int len)152c759b35eSStefani Seibold unsigned int __kfifo_out_peek(struct __kfifo *fifo,
153c759b35eSStefani Seibold 		void *buf, unsigned int len)
154c759b35eSStefani Seibold {
155c759b35eSStefani Seibold 	unsigned int l;
156c759b35eSStefani Seibold 
157c759b35eSStefani Seibold 	l = fifo->in - fifo->out;
158c759b35eSStefani Seibold 	if (len > l)
159c759b35eSStefani Seibold 		len = l;
160c759b35eSStefani Seibold 
161c759b35eSStefani Seibold 	kfifo_copy_out(fifo, buf, len, fifo->out);
162c759b35eSStefani Seibold 	return len;
163c759b35eSStefani Seibold }
164c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_out_peek);
165c759b35eSStefani Seibold 
__kfifo_out(struct __kfifo * fifo,void * buf,unsigned int len)166c759b35eSStefani Seibold unsigned int __kfifo_out(struct __kfifo *fifo,
167c759b35eSStefani Seibold 		void *buf, unsigned int len)
168c759b35eSStefani Seibold {
169c759b35eSStefani Seibold 	len = __kfifo_out_peek(fifo, buf, len);
170c759b35eSStefani Seibold 	fifo->out += len;
171c759b35eSStefani Seibold 	return len;
172c759b35eSStefani Seibold }
173c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_out);
174c759b35eSStefani Seibold 
kfifo_copy_from_user(struct __kfifo * fifo,const void __user * from,unsigned int len,unsigned int off,unsigned int * copied)175c759b35eSStefani Seibold static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
176c759b35eSStefani Seibold 	const void __user *from, unsigned int len, unsigned int off,
177c759b35eSStefani Seibold 	unsigned int *copied)
178c759b35eSStefani Seibold {
179c759b35eSStefani Seibold 	unsigned int size = fifo->mask + 1;
180c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
181c759b35eSStefani Seibold 	unsigned int l;
182c759b35eSStefani Seibold 	unsigned long ret;
183c759b35eSStefani Seibold 
184c759b35eSStefani Seibold 	off &= fifo->mask;
185c759b35eSStefani Seibold 	if (esize != 1) {
186c759b35eSStefani Seibold 		off *= esize;
187c759b35eSStefani Seibold 		size *= esize;
188c759b35eSStefani Seibold 		len *= esize;
189c759b35eSStefani Seibold 	}
190c759b35eSStefani Seibold 	l = min(len, size - off);
191c759b35eSStefani Seibold 
192c759b35eSStefani Seibold 	ret = copy_from_user(fifo->data + off, from, l);
193c759b35eSStefani Seibold 	if (unlikely(ret))
194c759b35eSStefani Seibold 		ret = DIV_ROUND_UP(ret + len - l, esize);
195c759b35eSStefani Seibold 	else {
196c759b35eSStefani Seibold 		ret = copy_from_user(fifo->data, from + l, len - l);
197c759b35eSStefani Seibold 		if (unlikely(ret))
198c759b35eSStefani Seibold 			ret = DIV_ROUND_UP(ret, esize);
199c759b35eSStefani Seibold 	}
200c759b35eSStefani Seibold 	/*
201c759b35eSStefani Seibold 	 * make sure that the data in the fifo is up to date before
202c759b35eSStefani Seibold 	 * incrementing the fifo->in index counter
203c759b35eSStefani Seibold 	 */
204c759b35eSStefani Seibold 	smp_wmb();
205a019e48cSLars-Peter Clausen 	*copied = len - ret * esize;
206c759b35eSStefani Seibold 	/* return the number of elements which are not copied */
207c759b35eSStefani Seibold 	return ret;
208c759b35eSStefani Seibold }
209c759b35eSStefani Seibold 
__kfifo_from_user(struct __kfifo * fifo,const void __user * from,unsigned long len,unsigned int * copied)210c759b35eSStefani Seibold int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
211c759b35eSStefani Seibold 		unsigned long len, unsigned int *copied)
212c759b35eSStefani Seibold {
213c759b35eSStefani Seibold 	unsigned int l;
214c759b35eSStefani Seibold 	unsigned long ret;
215c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
216c759b35eSStefani Seibold 	int err;
217c759b35eSStefani Seibold 
218c759b35eSStefani Seibold 	if (esize != 1)
219c759b35eSStefani Seibold 		len /= esize;
220c759b35eSStefani Seibold 
221c759b35eSStefani Seibold 	l = kfifo_unused(fifo);
222c759b35eSStefani Seibold 	if (len > l)
223c759b35eSStefani Seibold 		len = l;
224c759b35eSStefani Seibold 
225c759b35eSStefani Seibold 	ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
226c759b35eSStefani Seibold 	if (unlikely(ret)) {
227c759b35eSStefani Seibold 		len -= ret;
228c759b35eSStefani Seibold 		err = -EFAULT;
229c759b35eSStefani Seibold 	} else
230c759b35eSStefani Seibold 		err = 0;
231c759b35eSStefani Seibold 	fifo->in += len;
232c759b35eSStefani Seibold 	return err;
233c759b35eSStefani Seibold }
234c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_from_user);
235c759b35eSStefani Seibold 
kfifo_copy_to_user(struct __kfifo * fifo,void __user * to,unsigned int len,unsigned int off,unsigned int * copied)236c759b35eSStefani Seibold static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
237c759b35eSStefani Seibold 		unsigned int len, unsigned int off, unsigned int *copied)
238c759b35eSStefani Seibold {
239c759b35eSStefani Seibold 	unsigned int l;
240c759b35eSStefani Seibold 	unsigned long ret;
241c759b35eSStefani Seibold 	unsigned int size = fifo->mask + 1;
242c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
243c759b35eSStefani Seibold 
244c759b35eSStefani Seibold 	off &= fifo->mask;
245c759b35eSStefani Seibold 	if (esize != 1) {
246c759b35eSStefani Seibold 		off *= esize;
247c759b35eSStefani Seibold 		size *= esize;
248c759b35eSStefani Seibold 		len *= esize;
249c759b35eSStefani Seibold 	}
250c759b35eSStefani Seibold 	l = min(len, size - off);
251c759b35eSStefani Seibold 
252c759b35eSStefani Seibold 	ret = copy_to_user(to, fifo->data + off, l);
253c759b35eSStefani Seibold 	if (unlikely(ret))
254c759b35eSStefani Seibold 		ret = DIV_ROUND_UP(ret + len - l, esize);
255c759b35eSStefani Seibold 	else {
256c759b35eSStefani Seibold 		ret = copy_to_user(to + l, fifo->data, len - l);
257c759b35eSStefani Seibold 		if (unlikely(ret))
258c759b35eSStefani Seibold 			ret = DIV_ROUND_UP(ret, esize);
259c759b35eSStefani Seibold 	}
260c759b35eSStefani Seibold 	/*
261c759b35eSStefani Seibold 	 * make sure that the data is copied before
262c759b35eSStefani Seibold 	 * incrementing the fifo->out index counter
263c759b35eSStefani Seibold 	 */
264c759b35eSStefani Seibold 	smp_wmb();
265a019e48cSLars-Peter Clausen 	*copied = len - ret * esize;
266c759b35eSStefani Seibold 	/* return the number of elements which are not copied */
267c759b35eSStefani Seibold 	return ret;
268c759b35eSStefani Seibold }
269c759b35eSStefani Seibold 
__kfifo_to_user(struct __kfifo * fifo,void __user * to,unsigned long len,unsigned int * copied)270c759b35eSStefani Seibold int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
271c759b35eSStefani Seibold 		unsigned long len, unsigned int *copied)
272c759b35eSStefani Seibold {
273c759b35eSStefani Seibold 	unsigned int l;
274c759b35eSStefani Seibold 	unsigned long ret;
275c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
276c759b35eSStefani Seibold 	int err;
277c759b35eSStefani Seibold 
278c759b35eSStefani Seibold 	if (esize != 1)
279c759b35eSStefani Seibold 		len /= esize;
280c759b35eSStefani Seibold 
281c759b35eSStefani Seibold 	l = fifo->in - fifo->out;
282c759b35eSStefani Seibold 	if (len > l)
283c759b35eSStefani Seibold 		len = l;
284c759b35eSStefani Seibold 	ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
285c759b35eSStefani Seibold 	if (unlikely(ret)) {
286c759b35eSStefani Seibold 		len -= ret;
287c759b35eSStefani Seibold 		err = -EFAULT;
288c759b35eSStefani Seibold 	} else
289c759b35eSStefani Seibold 		err = 0;
290c759b35eSStefani Seibold 	fifo->out += len;
291c759b35eSStefani Seibold 	return err;
292c759b35eSStefani Seibold }
293c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_to_user);
294c759b35eSStefani Seibold 
setup_sgl_buf(struct scatterlist * sgl,void * buf,int nents,unsigned int len)295c759b35eSStefani Seibold static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
296c759b35eSStefani Seibold 		int nents, unsigned int len)
297c759b35eSStefani Seibold {
298c759b35eSStefani Seibold 	int n;
299c759b35eSStefani Seibold 	unsigned int l;
300c759b35eSStefani Seibold 	unsigned int off;
301c759b35eSStefani Seibold 	struct page *page;
302c759b35eSStefani Seibold 
303c759b35eSStefani Seibold 	if (!nents)
304c759b35eSStefani Seibold 		return 0;
305c759b35eSStefani Seibold 
306c759b35eSStefani Seibold 	if (!len)
307c759b35eSStefani Seibold 		return 0;
308c759b35eSStefani Seibold 
309c759b35eSStefani Seibold 	n = 0;
310c759b35eSStefani Seibold 	page = virt_to_page(buf);
311c759b35eSStefani Seibold 	off = offset_in_page(buf);
312c759b35eSStefani Seibold 	l = 0;
313c759b35eSStefani Seibold 
314c759b35eSStefani Seibold 	while (len >= l + PAGE_SIZE - off) {
315c759b35eSStefani Seibold 		struct page *npage;
316c759b35eSStefani Seibold 
317c759b35eSStefani Seibold 		l += PAGE_SIZE;
318c759b35eSStefani Seibold 		buf += PAGE_SIZE;
319c759b35eSStefani Seibold 		npage = virt_to_page(buf);
320c759b35eSStefani Seibold 		if (page_to_phys(page) != page_to_phys(npage) - l) {
321c759b35eSStefani Seibold 			sg_set_page(sgl, page, l - off, off);
322c759b35eSStefani Seibold 			sgl = sg_next(sgl);
323c759b35eSStefani Seibold 			if (++n == nents || sgl == NULL)
324c759b35eSStefani Seibold 				return n;
325c759b35eSStefani Seibold 			page = npage;
326c759b35eSStefani Seibold 			len -= l - off;
327c759b35eSStefani Seibold 			l = off = 0;
328c759b35eSStefani Seibold 		}
329c759b35eSStefani Seibold 	}
330c759b35eSStefani Seibold 	sg_set_page(sgl, page, len, off);
331c759b35eSStefani Seibold 	return n + 1;
332c759b35eSStefani Seibold }
333c759b35eSStefani Seibold 
setup_sgl(struct __kfifo * fifo,struct scatterlist * sgl,int nents,unsigned int len,unsigned int off)334c759b35eSStefani Seibold static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
335c759b35eSStefani Seibold 		int nents, unsigned int len, unsigned int off)
336c759b35eSStefani Seibold {
337c759b35eSStefani Seibold 	unsigned int size = fifo->mask + 1;
338c759b35eSStefani Seibold 	unsigned int esize = fifo->esize;
339c759b35eSStefani Seibold 	unsigned int l;
340c759b35eSStefani Seibold 	unsigned int n;
341c759b35eSStefani Seibold 
342c759b35eSStefani Seibold 	off &= fifo->mask;
343c759b35eSStefani Seibold 	if (esize != 1) {
344c759b35eSStefani Seibold 		off *= esize;
345c759b35eSStefani Seibold 		size *= esize;
346c759b35eSStefani Seibold 		len *= esize;
347c759b35eSStefani Seibold 	}
348c759b35eSStefani Seibold 	l = min(len, size - off);
349c759b35eSStefani Seibold 
350c759b35eSStefani Seibold 	n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
351c759b35eSStefani Seibold 	n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
352c759b35eSStefani Seibold 
353c759b35eSStefani Seibold 	return n;
354c759b35eSStefani Seibold }
355c759b35eSStefani Seibold 
__kfifo_dma_in_prepare(struct __kfifo * fifo,struct scatterlist * sgl,int nents,unsigned int len)356c759b35eSStefani Seibold unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
357c759b35eSStefani Seibold 		struct scatterlist *sgl, int nents, unsigned int len)
358c759b35eSStefani Seibold {
359c759b35eSStefani Seibold 	unsigned int l;
360c759b35eSStefani Seibold 
361c759b35eSStefani Seibold 	l = kfifo_unused(fifo);
362c759b35eSStefani Seibold 	if (len > l)
363c759b35eSStefani Seibold 		len = l;
364c759b35eSStefani Seibold 
365c759b35eSStefani Seibold 	return setup_sgl(fifo, sgl, nents, len, fifo->in);
366c759b35eSStefani Seibold }
367c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_in_prepare);
368c759b35eSStefani Seibold 
__kfifo_dma_out_prepare(struct __kfifo * fifo,struct scatterlist * sgl,int nents,unsigned int len)369c759b35eSStefani Seibold unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
370c759b35eSStefani Seibold 		struct scatterlist *sgl, int nents, unsigned int len)
371c759b35eSStefani Seibold {
372c759b35eSStefani Seibold 	unsigned int l;
373c759b35eSStefani Seibold 
374c759b35eSStefani Seibold 	l = fifo->in - fifo->out;
375c759b35eSStefani Seibold 	if (len > l)
376c759b35eSStefani Seibold 		len = l;
377c759b35eSStefani Seibold 
378c759b35eSStefani Seibold 	return setup_sgl(fifo, sgl, nents, len, fifo->out);
379c759b35eSStefani Seibold }
380c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_out_prepare);
381c759b35eSStefani Seibold 
__kfifo_max_r(unsigned int len,size_t recsize)382c759b35eSStefani Seibold unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
383c759b35eSStefani Seibold {
384c759b35eSStefani Seibold 	unsigned int max = (1 << (recsize << 3)) - 1;
385c759b35eSStefani Seibold 
386c759b35eSStefani Seibold 	if (len > max)
387c759b35eSStefani Seibold 		return max;
388c759b35eSStefani Seibold 	return len;
389c759b35eSStefani Seibold }
390c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_max_r);
391c759b35eSStefani Seibold 
392c759b35eSStefani Seibold #define	__KFIFO_PEEK(data, out, mask) \
393c759b35eSStefani Seibold 	((data)[(out) & (mask)])
394c759b35eSStefani Seibold /*
395c759b35eSStefani Seibold  * __kfifo_peek_n internal helper function for determinate the length of
396c759b35eSStefani Seibold  * the next record in the fifo
397c759b35eSStefani Seibold  */
__kfifo_peek_n(struct __kfifo * fifo,size_t recsize)398c759b35eSStefani Seibold static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize)
399c759b35eSStefani Seibold {
400c759b35eSStefani Seibold 	unsigned int l;
401c759b35eSStefani Seibold 	unsigned int mask = fifo->mask;
402c759b35eSStefani Seibold 	unsigned char *data = fifo->data;
403c759b35eSStefani Seibold 
404c759b35eSStefani Seibold 	l = __KFIFO_PEEK(data, fifo->out, mask);
405c759b35eSStefani Seibold 
406c759b35eSStefani Seibold 	if (--recsize)
407c759b35eSStefani Seibold 		l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8;
408c759b35eSStefani Seibold 
409c759b35eSStefani Seibold 	return l;
410c759b35eSStefani Seibold }
411c759b35eSStefani Seibold 
412c759b35eSStefani Seibold #define	__KFIFO_POKE(data, in, mask, val) \
413c759b35eSStefani Seibold 	( \
414c759b35eSStefani Seibold 	(data)[(in) & (mask)] = (unsigned char)(val) \
415c759b35eSStefani Seibold 	)
416c759b35eSStefani Seibold 
417c759b35eSStefani Seibold /*
418*9dbbc3b9SZhen Lei  * __kfifo_poke_n internal helper function for storing the length of
419c759b35eSStefani Seibold  * the record into the fifo
420c759b35eSStefani Seibold  */
__kfifo_poke_n(struct __kfifo * fifo,unsigned int n,size_t recsize)421c759b35eSStefani Seibold static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize)
422c759b35eSStefani Seibold {
423c759b35eSStefani Seibold 	unsigned int mask = fifo->mask;
424c759b35eSStefani Seibold 	unsigned char *data = fifo->data;
425c759b35eSStefani Seibold 
426c759b35eSStefani Seibold 	__KFIFO_POKE(data, fifo->in, mask, n);
427c759b35eSStefani Seibold 
428c759b35eSStefani Seibold 	if (recsize > 1)
429c759b35eSStefani Seibold 		__KFIFO_POKE(data, fifo->in + 1, mask, n >> 8);
430c759b35eSStefani Seibold }
431c759b35eSStefani Seibold 
__kfifo_len_r(struct __kfifo * fifo,size_t recsize)432c759b35eSStefani Seibold unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize)
433c759b35eSStefani Seibold {
434c759b35eSStefani Seibold 	return __kfifo_peek_n(fifo, recsize);
435c759b35eSStefani Seibold }
436c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_len_r);
437c759b35eSStefani Seibold 
__kfifo_in_r(struct __kfifo * fifo,const void * buf,unsigned int len,size_t recsize)438c759b35eSStefani Seibold unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf,
439c759b35eSStefani Seibold 		unsigned int len, size_t recsize)
440c759b35eSStefani Seibold {
441c759b35eSStefani Seibold 	if (len + recsize > kfifo_unused(fifo))
442c759b35eSStefani Seibold 		return 0;
443c759b35eSStefani Seibold 
444c759b35eSStefani Seibold 	__kfifo_poke_n(fifo, len, recsize);
445c759b35eSStefani Seibold 
446c759b35eSStefani Seibold 	kfifo_copy_in(fifo, buf, len, fifo->in + recsize);
447c759b35eSStefani Seibold 	fifo->in += len + recsize;
448c759b35eSStefani Seibold 	return len;
449c759b35eSStefani Seibold }
450c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_in_r);
451c759b35eSStefani Seibold 
kfifo_out_copy_r(struct __kfifo * fifo,void * buf,unsigned int len,size_t recsize,unsigned int * n)452c759b35eSStefani Seibold static unsigned int kfifo_out_copy_r(struct __kfifo *fifo,
453c759b35eSStefani Seibold 	void *buf, unsigned int len, size_t recsize, unsigned int *n)
454c759b35eSStefani Seibold {
455c759b35eSStefani Seibold 	*n = __kfifo_peek_n(fifo, recsize);
456c759b35eSStefani Seibold 
457c759b35eSStefani Seibold 	if (len > *n)
458c759b35eSStefani Seibold 		len = *n;
459c759b35eSStefani Seibold 
460c759b35eSStefani Seibold 	kfifo_copy_out(fifo, buf, len, fifo->out + recsize);
461c759b35eSStefani Seibold 	return len;
462c759b35eSStefani Seibold }
463c759b35eSStefani Seibold 
__kfifo_out_peek_r(struct __kfifo * fifo,void * buf,unsigned int len,size_t recsize)464c759b35eSStefani Seibold unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
465c759b35eSStefani Seibold 		unsigned int len, size_t recsize)
466c759b35eSStefani Seibold {
467c759b35eSStefani Seibold 	unsigned int n;
468c759b35eSStefani Seibold 
469c759b35eSStefani Seibold 	if (fifo->in == fifo->out)
470c759b35eSStefani Seibold 		return 0;
471c759b35eSStefani Seibold 
472c759b35eSStefani Seibold 	return kfifo_out_copy_r(fifo, buf, len, recsize, &n);
473c759b35eSStefani Seibold }
474c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_out_peek_r);
475c759b35eSStefani Seibold 
__kfifo_out_r(struct __kfifo * fifo,void * buf,unsigned int len,size_t recsize)476c759b35eSStefani Seibold unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
477c759b35eSStefani Seibold 		unsigned int len, size_t recsize)
478c759b35eSStefani Seibold {
479c759b35eSStefani Seibold 	unsigned int n;
480c759b35eSStefani Seibold 
481c759b35eSStefani Seibold 	if (fifo->in == fifo->out)
482c759b35eSStefani Seibold 		return 0;
483c759b35eSStefani Seibold 
484c759b35eSStefani Seibold 	len = kfifo_out_copy_r(fifo, buf, len, recsize, &n);
485c759b35eSStefani Seibold 	fifo->out += n + recsize;
486c759b35eSStefani Seibold 	return len;
487c759b35eSStefani Seibold }
488c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_out_r);
489c759b35eSStefani Seibold 
__kfifo_skip_r(struct __kfifo * fifo,size_t recsize)490c759b35eSStefani Seibold void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize)
491c759b35eSStefani Seibold {
492c759b35eSStefani Seibold 	unsigned int n;
493c759b35eSStefani Seibold 
494c759b35eSStefani Seibold 	n = __kfifo_peek_n(fifo, recsize);
495c759b35eSStefani Seibold 	fifo->out += n + recsize;
496c759b35eSStefani Seibold }
497c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_skip_r);
498c759b35eSStefani Seibold 
__kfifo_from_user_r(struct __kfifo * fifo,const void __user * from,unsigned long len,unsigned int * copied,size_t recsize)499c759b35eSStefani Seibold int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from,
500c759b35eSStefani Seibold 	unsigned long len, unsigned int *copied, size_t recsize)
501c759b35eSStefani Seibold {
502c759b35eSStefani Seibold 	unsigned long ret;
503c759b35eSStefani Seibold 
504c759b35eSStefani Seibold 	len = __kfifo_max_r(len, recsize);
505c759b35eSStefani Seibold 
506c759b35eSStefani Seibold 	if (len + recsize > kfifo_unused(fifo)) {
507c759b35eSStefani Seibold 		*copied = 0;
508c759b35eSStefani Seibold 		return 0;
509c759b35eSStefani Seibold 	}
510c759b35eSStefani Seibold 
511c759b35eSStefani Seibold 	__kfifo_poke_n(fifo, len, recsize);
512c759b35eSStefani Seibold 
513c759b35eSStefani Seibold 	ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied);
514c759b35eSStefani Seibold 	if (unlikely(ret)) {
515c759b35eSStefani Seibold 		*copied = 0;
516c759b35eSStefani Seibold 		return -EFAULT;
517c759b35eSStefani Seibold 	}
518c759b35eSStefani Seibold 	fifo->in += len + recsize;
519c759b35eSStefani Seibold 	return 0;
520c759b35eSStefani Seibold }
521c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_from_user_r);
522c759b35eSStefani Seibold 
__kfifo_to_user_r(struct __kfifo * fifo,void __user * to,unsigned long len,unsigned int * copied,size_t recsize)523c759b35eSStefani Seibold int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
524c759b35eSStefani Seibold 	unsigned long len, unsigned int *copied, size_t recsize)
525c759b35eSStefani Seibold {
526c759b35eSStefani Seibold 	unsigned long ret;
527c759b35eSStefani Seibold 	unsigned int n;
528c759b35eSStefani Seibold 
529c759b35eSStefani Seibold 	if (fifo->in == fifo->out) {
530c759b35eSStefani Seibold 		*copied = 0;
531c759b35eSStefani Seibold 		return 0;
532c759b35eSStefani Seibold 	}
533c759b35eSStefani Seibold 
534c759b35eSStefani Seibold 	n = __kfifo_peek_n(fifo, recsize);
535c759b35eSStefani Seibold 	if (len > n)
536c759b35eSStefani Seibold 		len = n;
537c759b35eSStefani Seibold 
538c759b35eSStefani Seibold 	ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied);
539c759b35eSStefani Seibold 	if (unlikely(ret)) {
540c759b35eSStefani Seibold 		*copied = 0;
541c759b35eSStefani Seibold 		return -EFAULT;
542c759b35eSStefani Seibold 	}
543c759b35eSStefani Seibold 	fifo->out += n + recsize;
544c759b35eSStefani Seibold 	return 0;
545c759b35eSStefani Seibold }
546c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_to_user_r);
547c759b35eSStefani Seibold 
__kfifo_dma_in_prepare_r(struct __kfifo * fifo,struct scatterlist * sgl,int nents,unsigned int len,size_t recsize)548c759b35eSStefani Seibold unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
549c759b35eSStefani Seibold 	struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
550c759b35eSStefani Seibold {
55189b3ac63SHimangi Saraogi 	BUG_ON(!nents);
552c759b35eSStefani Seibold 
553c759b35eSStefani Seibold 	len = __kfifo_max_r(len, recsize);
554c759b35eSStefani Seibold 
555c759b35eSStefani Seibold 	if (len + recsize > kfifo_unused(fifo))
556c759b35eSStefani Seibold 		return 0;
557c759b35eSStefani Seibold 
558c759b35eSStefani Seibold 	return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
559c759b35eSStefani Seibold }
560c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);
561c759b35eSStefani Seibold 
__kfifo_dma_in_finish_r(struct __kfifo * fifo,unsigned int len,size_t recsize)562c759b35eSStefani Seibold void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
563c759b35eSStefani Seibold 	unsigned int len, size_t recsize)
564c759b35eSStefani Seibold {
565c759b35eSStefani Seibold 	len = __kfifo_max_r(len, recsize);
566c759b35eSStefani Seibold 	__kfifo_poke_n(fifo, len, recsize);
567c759b35eSStefani Seibold 	fifo->in += len + recsize;
568c759b35eSStefani Seibold }
569c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_in_finish_r);
570c759b35eSStefani Seibold 
__kfifo_dma_out_prepare_r(struct __kfifo * fifo,struct scatterlist * sgl,int nents,unsigned int len,size_t recsize)571c759b35eSStefani Seibold unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
572c759b35eSStefani Seibold 	struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
573c759b35eSStefani Seibold {
57489b3ac63SHimangi Saraogi 	BUG_ON(!nents);
575c759b35eSStefani Seibold 
576c759b35eSStefani Seibold 	len = __kfifo_max_r(len, recsize);
577c759b35eSStefani Seibold 
578c759b35eSStefani Seibold 	if (len + recsize > fifo->in - fifo->out)
579c759b35eSStefani Seibold 		return 0;
580c759b35eSStefani Seibold 
581c759b35eSStefani Seibold 	return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
582c759b35eSStefani Seibold }
583c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);
584c759b35eSStefani Seibold 
__kfifo_dma_out_finish_r(struct __kfifo * fifo,size_t recsize)585c759b35eSStefani Seibold void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
586c759b35eSStefani Seibold {
587c759b35eSStefani Seibold 	unsigned int len;
588c759b35eSStefani Seibold 
589c759b35eSStefani Seibold 	len = __kfifo_peek_n(fifo, recsize);
590c759b35eSStefani Seibold 	fifo->out += len + recsize;
591c759b35eSStefani Seibold }
592c759b35eSStefani Seibold EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
593