diff options
author | Stefani Seibold <stefani@seibold.net> | 2009-12-21 17:37:32 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-22 17:17:56 -0500 |
commit | 86d4880313603810901f639ccb5c88ff13d4ad3c (patch) | |
tree | c182d890b5c96a1143b70b00b93e3fc4a9263555 /kernel/kfifo.c | |
parent | a121f24accac1600bf5b6fb1e12eeabdfed7cb1a (diff) |
kfifo: add record handling functions
Add kfifo_in_rec() - puts some record data into the FIFO
Add kfifo_out_rec() - gets some record data from the FIFO
Add kfifo_from_user_rec() - puts some data from user space into the FIFO
Add kfifo_to_user_rec() - gets data from the FIFO and write it to user space
Add kfifo_peek_rec() - gets the size of the next FIFO record field
Add kfifo_skip_rec() - skip the next fifo out record
Add kfifo_avail_rec() - determinate the number of bytes available in a record FIFO
Signed-off-by: Stefani Seibold <stefani@seibold.net>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kfifo.c')
-rw-r--r-- | kernel/kfifo.c | 286 |
1 files changed, 193 insertions, 93 deletions
diff --git a/kernel/kfifo.c b/kernel/kfifo.c index 2a78425ef67f..e92d519f93b1 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c | |||
@@ -115,27 +115,11 @@ void kfifo_skip(struct kfifo *fifo, unsigned int len) | |||
115 | } | 115 | } |
116 | EXPORT_SYMBOL(kfifo_skip); | 116 | EXPORT_SYMBOL(kfifo_skip); |
117 | 117 | ||
118 | /** | 118 | static inline void __kfifo_in_data(struct kfifo *fifo, |
119 | * kfifo_in - puts some data into the FIFO | 119 | const void *from, unsigned int len, unsigned int off) |
120 | * @fifo: the fifo to be used. | ||
121 | * @from: the data to be added. | ||
122 | * @len: the length of the data to be added. | ||
123 | * | ||
124 | * This function copies at most @len bytes from the @from buffer into | ||
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 | */ | ||
131 | unsigned int kfifo_in(struct kfifo *fifo, | ||
132 | const unsigned char *from, unsigned int len) | ||
133 | { | 120 | { |
134 | unsigned int off; | ||
135 | unsigned int l; | 121 | unsigned int l; |
136 | 122 | ||
137 | len = min(len, fifo->size - fifo->in + fifo->out); | ||
138 | |||
139 | /* | 123 | /* |
140 | * Ensure that we sample the fifo->out index -before- we | 124 | * Ensure that we sample the fifo->out index -before- we |
141 | * start putting bytes into the kfifo. | 125 | * start putting bytes into the kfifo. |
@@ -143,7 +127,7 @@ unsigned int kfifo_in(struct kfifo *fifo, | |||
143 | 127 | ||
144 | smp_mb(); | 128 | smp_mb(); |
145 | 129 | ||
146 | off = __kfifo_off(fifo, fifo->in); | 130 | off = __kfifo_off(fifo, fifo->in + off); |
147 | 131 | ||
148 | /* first put the data starting from fifo->in to buffer end */ | 132 | /* first put the data starting from fifo->in to buffer end */ |
149 | l = min(len, fifo->size - off); | 133 | l = min(len, fifo->size - off); |
@@ -151,33 +135,13 @@ unsigned int kfifo_in(struct kfifo *fifo, | |||
151 | 135 | ||
152 | /* then put the rest (if any) at the beginning of the buffer */ | 136 | /* then put the rest (if any) at the beginning of the buffer */ |
153 | memcpy(fifo->buffer, from + l, len - l); | 137 | memcpy(fifo->buffer, from + l, len - l); |
154 | |||
155 | __kfifo_add_in(fifo, len); | ||
156 | |||
157 | return len; | ||
158 | } | 138 | } |
159 | EXPORT_SYMBOL(kfifo_in); | ||
160 | 139 | ||
161 | /** | 140 | static inline void __kfifo_out_data(struct kfifo *fifo, |
162 | * kfifo_out - gets some data from the FIFO | 141 | void *to, unsigned int len, unsigned int off) |
163 | * @fifo: the fifo to be used. | ||
164 | * @to: where the data must be copied. | ||
165 | * @len: the size of the destination buffer. | ||
166 | * | ||
167 | * This function copies at most @len bytes from the FIFO into the | ||
168 | * @to buffer and returns the number of copied bytes. | ||
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 | */ | ||
173 | unsigned int kfifo_out(struct kfifo *fifo, | ||
174 | unsigned char *to, unsigned int len) | ||
175 | { | 142 | { |
176 | unsigned int off; | ||
177 | unsigned int l; | 143 | unsigned int l; |
178 | 144 | ||
179 | len = min(len, fifo->in - fifo->out); | ||
180 | |||
181 | /* | 145 | /* |
182 | * Ensure that we sample the fifo->in index -before- we | 146 | * Ensure that we sample the fifo->in index -before- we |
183 | * start removing bytes from the kfifo. | 147 | * start removing bytes from the kfifo. |
@@ -185,7 +149,7 @@ unsigned int kfifo_out(struct kfifo *fifo, | |||
185 | 149 | ||
186 | smp_rmb(); | 150 | smp_rmb(); |
187 | 151 | ||
188 | off = __kfifo_off(fifo, fifo->out); | 152 | off = __kfifo_off(fifo, fifo->out + off); |
189 | 153 | ||
190 | /* first get the data from fifo->out until the end of the buffer */ | 154 | /* first get the data from fifo->out until the end of the buffer */ |
191 | l = min(len, fifo->size - off); | 155 | l = min(len, fifo->size - off); |
@@ -193,34 +157,14 @@ unsigned int kfifo_out(struct kfifo *fifo, | |||
193 | 157 | ||
194 | /* then get the rest (if any) from the beginning of the buffer */ | 158 | /* then get the rest (if any) from the beginning of the buffer */ |
195 | memcpy(to + l, fifo->buffer, len - l); | 159 | memcpy(to + l, fifo->buffer, len - l); |
196 | |||
197 | __kfifo_add_out(fifo, len); | ||
198 | |||
199 | return len; | ||
200 | } | 160 | } |
201 | EXPORT_SYMBOL(kfifo_out); | ||
202 | 161 | ||
203 | /** | 162 | static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo, |
204 | * kfifo_from_user - puts some data from user space into the FIFO | 163 | const void __user *from, unsigned int len, unsigned int off) |
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 | { | 164 | { |
218 | unsigned int off; | ||
219 | unsigned int l; | 165 | unsigned int l; |
220 | int ret; | 166 | int ret; |
221 | 167 | ||
222 | len = min(len, fifo->size - fifo->in + fifo->out); | ||
223 | |||
224 | /* | 168 | /* |
225 | * Ensure that we sample the fifo->out index -before- we | 169 | * Ensure that we sample the fifo->out index -before- we |
226 | * start putting bytes into the kfifo. | 170 | * start putting bytes into the kfifo. |
@@ -228,29 +172,101 @@ unsigned int kfifo_from_user(struct kfifo *fifo, | |||
228 | 172 | ||
229 | smp_mb(); | 173 | smp_mb(); |
230 | 174 | ||
231 | off = __kfifo_off(fifo, fifo->in); | 175 | off = __kfifo_off(fifo, fifo->in + off); |
232 | 176 | ||
233 | /* first put the data starting from fifo->in to buffer end */ | 177 | /* first put the data starting from fifo->in to buffer end */ |
234 | l = min(len, fifo->size - off); | 178 | l = min(len, fifo->size - off); |
235 | ret = copy_from_user(fifo->buffer + off, from, l); | 179 | ret = copy_from_user(fifo->buffer + off, from, l); |
236 | 180 | ||
237 | if (unlikely(ret)) | 181 | if (unlikely(ret)) |
238 | return l - ret; | 182 | return ret + len - l; |
239 | 183 | ||
240 | /* then put the rest (if any) at the beginning of the buffer */ | 184 | /* then put the rest (if any) at the beginning of the buffer */ |
241 | ret = copy_from_user(fifo->buffer, from + l, len - l); | 185 | return copy_from_user(fifo->buffer, from + l, len - l); |
186 | } | ||
187 | |||
188 | static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo, | ||
189 | void __user *to, unsigned int len, unsigned int off) | ||
190 | { | ||
191 | unsigned int l; | ||
192 | int ret; | ||
193 | |||
194 | /* | ||
195 | * Ensure that we sample the fifo->in index -before- we | ||
196 | * start removing bytes from the kfifo. | ||
197 | */ | ||
198 | |||
199 | smp_rmb(); | ||
200 | |||
201 | off = __kfifo_off(fifo, fifo->out + off); | ||
202 | |||
203 | /* first get the data from fifo->out until the end of the buffer */ | ||
204 | l = min(len, fifo->size - off); | ||
205 | ret = copy_to_user(to, fifo->buffer + off, l); | ||
242 | 206 | ||
243 | if (unlikely(ret)) | 207 | if (unlikely(ret)) |
244 | return len - ret; | 208 | return ret + len - l; |
245 | 209 | ||
246 | __kfifo_add_in(fifo, len); | 210 | /* then get the rest (if any) from the beginning of the buffer */ |
211 | return copy_to_user(to + l, fifo->buffer, len - l); | ||
212 | } | ||
247 | 213 | ||
214 | unsigned int __kfifo_in_n(struct kfifo *fifo, | ||
215 | const void *from, unsigned int len, unsigned int recsize) | ||
216 | { | ||
217 | if (kfifo_avail(fifo) < len + recsize) | ||
218 | return len + 1; | ||
219 | |||
220 | __kfifo_in_data(fifo, from, len, recsize); | ||
221 | return 0; | ||
222 | } | ||
223 | EXPORT_SYMBOL(__kfifo_in_n); | ||
224 | |||
225 | /** | ||
226 | * kfifo_in - puts some data into the FIFO | ||
227 | * @fifo: the fifo to be used. | ||
228 | * @from: the data to be added. | ||
229 | * @len: the length of the data to be added. | ||
230 | * | ||
231 | * This function copies at most @len bytes from the @from buffer into | ||
232 | * the FIFO depending on the free space, and returns the number of | ||
233 | * bytes copied. | ||
234 | * | ||
235 | * Note that with only one concurrent reader and one concurrent | ||
236 | * writer, you don't need extra locking to use these functions. | ||
237 | */ | ||
238 | unsigned int kfifo_in(struct kfifo *fifo, const unsigned char *from, | ||
239 | unsigned int len) | ||
240 | { | ||
241 | len = min(kfifo_avail(fifo), len); | ||
242 | |||
243 | __kfifo_in_data(fifo, from, len, 0); | ||
244 | __kfifo_add_in(fifo, len); | ||
248 | return len; | 245 | return len; |
249 | } | 246 | } |
250 | EXPORT_SYMBOL(kfifo_from_user); | 247 | EXPORT_SYMBOL(kfifo_in); |
248 | |||
249 | unsigned int __kfifo_in_generic(struct kfifo *fifo, | ||
250 | const void *from, unsigned int len, unsigned int recsize) | ||
251 | { | ||
252 | return __kfifo_in_rec(fifo, from, len, recsize); | ||
253 | } | ||
254 | EXPORT_SYMBOL(__kfifo_in_generic); | ||
255 | |||
256 | unsigned int __kfifo_out_n(struct kfifo *fifo, | ||
257 | void *to, unsigned int len, unsigned int recsize) | ||
258 | { | ||
259 | if (kfifo_len(fifo) < len + recsize) | ||
260 | return len; | ||
261 | |||
262 | __kfifo_out_data(fifo, to, len, recsize); | ||
263 | __kfifo_add_out(fifo, len + recsize); | ||
264 | return 0; | ||
265 | } | ||
266 | EXPORT_SYMBOL(__kfifo_out_n); | ||
251 | 267 | ||
252 | /** | 268 | /** |
253 | * kfifo_to_user - gets data from the FIFO and write it to user space | 269 | * kfifo_out - gets some data from the FIFO |
254 | * @fifo: the fifo to be used. | 270 | * @fifo: the fifo to be used. |
255 | * @to: where the data must be copied. | 271 | * @to: where the data must be copied. |
256 | * @len: the size of the destination buffer. | 272 | * @len: the size of the destination buffer. |
@@ -261,40 +277,124 @@ EXPORT_SYMBOL(kfifo_from_user); | |||
261 | * Note that with only one concurrent reader and one concurrent | 277 | * Note that with only one concurrent reader and one concurrent |
262 | * writer, you don't need extra locking to use these functions. | 278 | * writer, you don't need extra locking to use these functions. |
263 | */ | 279 | */ |
264 | unsigned int kfifo_to_user(struct kfifo *fifo, | 280 | unsigned int kfifo_out(struct kfifo *fifo, unsigned char *to, unsigned int len) |
265 | void __user *to, unsigned int len) | ||
266 | { | 281 | { |
267 | unsigned int off; | 282 | len = min(kfifo_len(fifo), len); |
268 | unsigned int l; | ||
269 | int ret; | ||
270 | 283 | ||
271 | len = min(len, fifo->in - fifo->out); | 284 | __kfifo_out_data(fifo, to, len, 0); |
285 | __kfifo_add_out(fifo, len); | ||
272 | 286 | ||
273 | /* | 287 | return len; |
274 | * Ensure that we sample the fifo->in index -before- we | 288 | } |
275 | * start removing bytes from the kfifo. | 289 | EXPORT_SYMBOL(kfifo_out); |
276 | */ | ||
277 | 290 | ||
278 | smp_rmb(); | 291 | unsigned int __kfifo_out_generic(struct kfifo *fifo, |
292 | void *to, unsigned int len, unsigned int recsize, | ||
293 | unsigned int *total) | ||
294 | { | ||
295 | return __kfifo_out_rec(fifo, to, len, recsize, total); | ||
296 | } | ||
297 | EXPORT_SYMBOL(__kfifo_out_generic); | ||
279 | 298 | ||
280 | off = __kfifo_off(fifo, fifo->out); | 299 | unsigned int __kfifo_from_user_n(struct kfifo *fifo, |
300 | const void __user *from, unsigned int len, unsigned int recsize) | ||
301 | { | ||
302 | if (kfifo_avail(fifo) < len + recsize) | ||
303 | return len + 1; | ||
281 | 304 | ||
282 | /* first get the data from fifo->out until the end of the buffer */ | 305 | return __kfifo_from_user_data(fifo, from, len, recsize); |
283 | l = min(len, fifo->size - off); | 306 | } |
284 | ret = copy_to_user(to, fifo->buffer + off, l); | 307 | EXPORT_SYMBOL(__kfifo_from_user_n); |
285 | 308 | ||
286 | if (unlikely(ret)) | 309 | /** |
287 | return l - ret; | 310 | * kfifo_from_user - puts some data from user space into the FIFO |
311 | * @fifo: the fifo to be used. | ||
312 | * @from: pointer to the data to be added. | ||
313 | * @len: the length of the data to be added. | ||
314 | * | ||
315 | * This function copies at most @len bytes from the @from into the | ||
316 | * FIFO depending and returns the number of copied bytes. | ||
317 | * | ||
318 | * Note that with only one concurrent reader and one concurrent | ||
319 | * writer, you don't need extra locking to use these functions. | ||
320 | */ | ||
321 | unsigned int kfifo_from_user(struct kfifo *fifo, | ||
322 | const void __user *from, unsigned int len) | ||
323 | { | ||
324 | len = min(kfifo_avail(fifo), len); | ||
325 | len -= __kfifo_from_user_data(fifo, from, len, 0); | ||
326 | __kfifo_add_in(fifo, len); | ||
327 | return len; | ||
328 | } | ||
329 | EXPORT_SYMBOL(kfifo_from_user); | ||
288 | 330 | ||
289 | /* then get the rest (if any) from the beginning of the buffer */ | 331 | unsigned int __kfifo_from_user_generic(struct kfifo *fifo, |
290 | ret = copy_to_user(to + l, fifo->buffer, len - l); | 332 | const void __user *from, unsigned int len, unsigned int recsize) |
333 | { | ||
334 | return __kfifo_from_user_rec(fifo, from, len, recsize); | ||
335 | } | ||
336 | EXPORT_SYMBOL(__kfifo_from_user_generic); | ||
291 | 337 | ||
292 | if (unlikely(ret)) | 338 | unsigned int __kfifo_to_user_n(struct kfifo *fifo, |
293 | return len - ret; | 339 | void __user *to, unsigned int len, unsigned int reclen, |
340 | unsigned int recsize) | ||
341 | { | ||
342 | unsigned int ret; | ||
294 | 343 | ||
295 | __kfifo_add_out(fifo, len); | 344 | if (kfifo_len(fifo) < reclen + recsize) |
345 | return len; | ||
296 | 346 | ||
347 | ret = __kfifo_to_user_data(fifo, to, reclen, recsize); | ||
348 | |||
349 | if (likely(ret == 0)) | ||
350 | __kfifo_add_out(fifo, reclen + recsize); | ||
351 | |||
352 | return ret; | ||
353 | } | ||
354 | EXPORT_SYMBOL(__kfifo_to_user_n); | ||
355 | |||
356 | /** | ||
357 | * kfifo_to_user - gets data from the FIFO and write it to user space | ||
358 | * @fifo: the fifo to be used. | ||
359 | * @to: where the data must be copied. | ||
360 | * @len: the size of the destination buffer. | ||
361 | * | ||
362 | * This function copies at most @len bytes from the FIFO into the | ||
363 | * @to buffer and returns the number of copied bytes. | ||
364 | * | ||
365 | * Note that with only one concurrent reader and one concurrent | ||
366 | * writer, you don't need extra locking to use these functions. | ||
367 | */ | ||
368 | unsigned int kfifo_to_user(struct kfifo *fifo, | ||
369 | void __user *to, unsigned int len) | ||
370 | { | ||
371 | len = min(kfifo_len(fifo), len); | ||
372 | len -= __kfifo_to_user_data(fifo, to, len, 0); | ||
373 | __kfifo_add_out(fifo, len); | ||
297 | return len; | 374 | return len; |
298 | } | 375 | } |
299 | EXPORT_SYMBOL(kfifo_to_user); | 376 | EXPORT_SYMBOL(kfifo_to_user); |
300 | 377 | ||
378 | unsigned int __kfifo_to_user_generic(struct kfifo *fifo, | ||
379 | void __user *to, unsigned int len, unsigned int recsize, | ||
380 | unsigned int *total) | ||
381 | { | ||
382 | return __kfifo_to_user_rec(fifo, to, len, recsize, total); | ||
383 | } | ||
384 | EXPORT_SYMBOL(__kfifo_to_user_generic); | ||
385 | |||
386 | unsigned int __kfifo_peek_generic(struct kfifo *fifo, unsigned int recsize) | ||
387 | { | ||
388 | if (recsize == 0) | ||
389 | return kfifo_avail(fifo); | ||
390 | |||
391 | return __kfifo_peek_n(fifo, recsize); | ||
392 | } | ||
393 | EXPORT_SYMBOL(__kfifo_peek_generic); | ||
394 | |||
395 | void __kfifo_skip_generic(struct kfifo *fifo, unsigned int recsize) | ||
396 | { | ||
397 | __kfifo_skip_rec(fifo, recsize); | ||
398 | } | ||
399 | EXPORT_SYMBOL(__kfifo_skip_generic); | ||
400 | |||