]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
45465487 | 2 | * A generic kernel FIFO implementation. |
1da177e4 | 3 | * |
45465487 | 4 | * Copyright (C) 2009 Stefani Seibold <stefani@seibold.net> |
1da177e4 LT |
5 | * Copyright (C) 2004 Stelian Pop <stelian@popies.net> |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | * | |
21 | */ | |
22 | ||
23 | #include <linux/kernel.h> | |
24 | #include <linux/module.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/err.h> | |
27 | #include <linux/kfifo.h> | |
f84d5a76 | 28 | #include <linux/log2.h> |
a121f24a | 29 | #include <linux/uaccess.h> |
1da177e4 | 30 | |
45465487 | 31 | static void _kfifo_init(struct kfifo *fifo, unsigned char *buffer, |
c1e13f25 | 32 | unsigned int size) |
45465487 SS |
33 | { |
34 | fifo->buffer = buffer; | |
35 | fifo->size = size; | |
45465487 SS |
36 | |
37 | kfifo_reset(fifo); | |
38 | } | |
39 | ||
1da177e4 | 40 | /** |
45465487 SS |
41 | * kfifo_init - initialize a FIFO using a preallocated buffer |
42 | * @fifo: the fifo to assign the buffer | |
1da177e4 LT |
43 | * @buffer: the preallocated buffer to be used. |
44 | * @size: the size of the internal buffer, this have to be a power of 2. | |
1da177e4 | 45 | * |
1da177e4 | 46 | */ |
c1e13f25 | 47 | void kfifo_init(struct kfifo *fifo, unsigned char *buffer, unsigned int size) |
1da177e4 | 48 | { |
1da177e4 | 49 | /* size must be a power of 2 */ |
f84d5a76 | 50 | BUG_ON(!is_power_of_2(size)); |
1da177e4 | 51 | |
c1e13f25 | 52 | _kfifo_init(fifo, buffer, size); |
1da177e4 LT |
53 | } |
54 | EXPORT_SYMBOL(kfifo_init); | |
55 | ||
56 | /** | |
45465487 SS |
57 | * kfifo_alloc - allocates a new FIFO internal buffer |
58 | * @fifo: the fifo to assign then new buffer | |
59 | * @size: the size of the buffer to be allocated, this have to be a power of 2. | |
1da177e4 | 60 | * @gfp_mask: get_free_pages mask, passed to kmalloc() |
1da177e4 | 61 | * |
45465487 SS |
62 | * This function dynamically allocates a new fifo internal buffer |
63 | * | |
1da177e4 | 64 | * The size will be rounded-up to a power of 2. |
45465487 SS |
65 | * The buffer will be release with kfifo_free(). |
66 | * Return 0 if no error, otherwise the an error code | |
1da177e4 | 67 | */ |
c1e13f25 | 68 | int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask) |
1da177e4 LT |
69 | { |
70 | unsigned char *buffer; | |
1da177e4 LT |
71 | |
72 | /* | |
73 | * round up to the next power of 2, since our 'let the indices | |
b33112d1 | 74 | * wrap' technique works only in this case. |
1da177e4 | 75 | */ |
b33112d1 | 76 | if (!is_power_of_2(size)) { |
1da177e4 LT |
77 | BUG_ON(size > 0x80000000); |
78 | size = roundup_pow_of_two(size); | |
79 | } | |
80 | ||
81 | buffer = kmalloc(size, gfp_mask); | |
45465487 | 82 | if (!buffer) { |
c1e13f25 | 83 | _kfifo_init(fifo, 0, 0); |
45465487 SS |
84 | return -ENOMEM; |
85 | } | |
1da177e4 | 86 | |
c1e13f25 | 87 | _kfifo_init(fifo, buffer, size); |
1da177e4 | 88 | |
45465487 | 89 | return 0; |
1da177e4 LT |
90 | } |
91 | EXPORT_SYMBOL(kfifo_alloc); | |
92 | ||
93 | /** | |
45465487 | 94 | * kfifo_free - frees the FIFO internal buffer |
1da177e4 LT |
95 | * @fifo: the fifo to be freed. |
96 | */ | |
97 | void kfifo_free(struct kfifo *fifo) | |
98 | { | |
99 | kfree(fifo->buffer); | |
1da177e4 LT |
100 | } |
101 | EXPORT_SYMBOL(kfifo_free); | |
102 | ||
a121f24a SS |
103 | /** |
104 | * kfifo_skip - skip output data | |
105 | * @fifo: the fifo to be used. | |
106 | * @len: number of bytes to skip | |
107 | */ | |
108 | void kfifo_skip(struct kfifo *fifo, unsigned int len) | |
109 | { | |
110 | if (len < kfifo_len(fifo)) { | |
111 | __kfifo_add_out(fifo, len); | |
112 | return; | |
113 | } | |
114 | kfifo_reset_out(fifo); | |
115 | } | |
116 | EXPORT_SYMBOL(kfifo_skip); | |
117 | ||
1da177e4 | 118 | /** |
7acd72eb | 119 | * kfifo_in - puts some data into the FIFO |
1da177e4 | 120 | * @fifo: the fifo to be used. |
7acd72eb | 121 | * @from: the data to be added. |
1da177e4 LT |
122 | * @len: the length of the data to be added. |
123 | * | |
7acd72eb | 124 | * This function copies at most @len bytes from the @from buffer into |
1da177e4 LT |
125 | * the FIFO depending on the free space, and returns the number of |
126 | * bytes copied. | |
127 | * | |
128 | * Note that with only one concurrent reader and one concurrent | |
129 | * writer, you don't need extra locking to use these functions. | |
130 | */ | |
7acd72eb SS |
131 | unsigned int kfifo_in(struct kfifo *fifo, |
132 | const unsigned char *from, unsigned int len) | |
1da177e4 | 133 | { |
a121f24a | 134 | unsigned int off; |
1da177e4 LT |
135 | unsigned int l; |
136 | ||
137 | len = min(len, fifo->size - fifo->in + fifo->out); | |
138 | ||
a45bce49 PM |
139 | /* |
140 | * Ensure that we sample the fifo->out index -before- we | |
141 | * start putting bytes into the kfifo. | |
142 | */ | |
143 | ||
144 | smp_mb(); | |
145 | ||
a121f24a SS |
146 | off = __kfifo_off(fifo, fifo->in); |
147 | ||
1da177e4 | 148 | /* first put the data starting from fifo->in to buffer end */ |
a121f24a SS |
149 | l = min(len, fifo->size - off); |
150 | memcpy(fifo->buffer + off, from, l); | |
1da177e4 LT |
151 | |
152 | /* then put the rest (if any) at the beginning of the buffer */ | |
7acd72eb | 153 | memcpy(fifo->buffer, from + l, len - l); |
1da177e4 | 154 | |
a121f24a | 155 | __kfifo_add_in(fifo, len); |
1da177e4 LT |
156 | |
157 | return len; | |
158 | } | |
7acd72eb | 159 | EXPORT_SYMBOL(kfifo_in); |
1da177e4 LT |
160 | |
161 | /** | |
7acd72eb | 162 | * kfifo_out - gets some data from the FIFO |
1da177e4 | 163 | * @fifo: the fifo to be used. |
7acd72eb | 164 | * @to: where the data must be copied. |
1da177e4 LT |
165 | * @len: the size of the destination buffer. |
166 | * | |
72fd4a35 | 167 | * This function copies at most @len bytes from the FIFO into the |
7acd72eb | 168 | * @to buffer and returns the number of copied bytes. |
1da177e4 LT |
169 | * |
170 | * Note that with only one concurrent reader and one concurrent | |
171 | * writer, you don't need extra locking to use these functions. | |
172 | */ | |
7acd72eb SS |
173 | unsigned int kfifo_out(struct kfifo *fifo, |
174 | unsigned char *to, unsigned int len) | |
1da177e4 | 175 | { |
a121f24a | 176 | unsigned int off; |
1da177e4 LT |
177 | unsigned int l; |
178 | ||
179 | len = min(len, fifo->in - fifo->out); | |
180 | ||
a45bce49 PM |
181 | /* |
182 | * Ensure that we sample the fifo->in index -before- we | |
183 | * start removing bytes from the kfifo. | |
184 | */ | |
185 | ||
186 | smp_rmb(); | |
187 | ||
a121f24a SS |
188 | off = __kfifo_off(fifo, fifo->out); |
189 | ||
1da177e4 | 190 | /* first get the data from fifo->out until the end of the buffer */ |
a121f24a SS |
191 | l = min(len, fifo->size - off); |
192 | memcpy(to, fifo->buffer + off, l); | |
1da177e4 LT |
193 | |
194 | /* then get the rest (if any) from the beginning of the buffer */ | |
7acd72eb | 195 | memcpy(to + l, fifo->buffer, len - l); |
1da177e4 | 196 | |
a121f24a SS |
197 | __kfifo_add_out(fifo, len); |
198 | ||
199 | return len; | |
200 | } | |
201 | EXPORT_SYMBOL(kfifo_out); | |
202 | ||
203 | /** | |
204 | * kfifo_from_user - puts some data from user space into the FIFO | |
205 | * @fifo: the fifo to be used. | |
206 | * @from: pointer to the data to be added. | |
207 | * @len: the length of the data to be added. | |
208 | * | |
209 | * This function copies at most @len bytes from the @from into the | |
210 | * FIFO depending and returns the number of copied bytes. | |
211 | * | |
212 | * Note that with only one concurrent reader and one concurrent | |
213 | * writer, you don't need extra locking to use these functions. | |
214 | */ | |
215 | unsigned int kfifo_from_user(struct kfifo *fifo, | |
216 | const void __user *from, unsigned int len) | |
217 | { | |
218 | unsigned int off; | |
219 | unsigned int l; | |
220 | int ret; | |
221 | ||
222 | len = min(len, fifo->size - fifo->in + fifo->out); | |
223 | ||
a45bce49 | 224 | /* |
a121f24a SS |
225 | * Ensure that we sample the fifo->out index -before- we |
226 | * start putting bytes into the kfifo. | |
a45bce49 PM |
227 | */ |
228 | ||
229 | smp_mb(); | |
230 | ||
a121f24a SS |
231 | off = __kfifo_off(fifo, fifo->in); |
232 | ||
233 | /* first put the data starting from fifo->in to buffer end */ | |
234 | l = min(len, fifo->size - off); | |
235 | ret = copy_from_user(fifo->buffer + off, from, l); | |
236 | ||
237 | if (unlikely(ret)) | |
238 | return l - ret; | |
239 | ||
240 | /* then put the rest (if any) at the beginning of the buffer */ | |
241 | ret = copy_from_user(fifo->buffer, from + l, len - l); | |
242 | ||
243 | if (unlikely(ret)) | |
244 | return len - ret; | |
245 | ||
246 | __kfifo_add_in(fifo, len); | |
1da177e4 LT |
247 | |
248 | return len; | |
249 | } | |
a121f24a SS |
250 | EXPORT_SYMBOL(kfifo_from_user); |
251 | ||
252 | /** | |
253 | * kfifo_to_user - gets data from the FIFO and write it to user space | |
254 | * @fifo: the fifo to be used. | |
255 | * @to: where the data must be copied. | |
256 | * @len: the size of the destination buffer. | |
257 | * | |
258 | * This function copies at most @len bytes from the FIFO into the | |
259 | * @to buffer and returns the number of copied bytes. | |
260 | * | |
261 | * Note that with only one concurrent reader and one concurrent | |
262 | * writer, you don't need extra locking to use these functions. | |
263 | */ | |
264 | unsigned int kfifo_to_user(struct kfifo *fifo, | |
265 | void __user *to, unsigned int len) | |
266 | { | |
267 | unsigned int off; | |
268 | unsigned int l; | |
269 | int ret; | |
270 | ||
271 | len = min(len, fifo->in - fifo->out); | |
272 | ||
273 | /* | |
274 | * Ensure that we sample the fifo->in index -before- we | |
275 | * start removing bytes from the kfifo. | |
276 | */ | |
277 | ||
278 | smp_rmb(); | |
279 | ||
280 | off = __kfifo_off(fifo, fifo->out); | |
281 | ||
282 | /* first get the data from fifo->out until the end of the buffer */ | |
283 | l = min(len, fifo->size - off); | |
284 | ret = copy_to_user(to, fifo->buffer + off, l); | |
285 | ||
286 | if (unlikely(ret)) | |
287 | return l - ret; | |
288 | ||
289 | /* then get the rest (if any) from the beginning of the buffer */ | |
290 | ret = copy_to_user(to + l, fifo->buffer, len - l); | |
291 | ||
292 | if (unlikely(ret)) | |
293 | return len - ret; | |
294 | ||
295 | __kfifo_add_out(fifo, len); | |
296 | ||
297 | return len; | |
298 | } | |
299 | EXPORT_SYMBOL(kfifo_to_user); | |
300 |