diff options
Diffstat (limited to 'kernel/kfifo.c')
-rw-r--r-- | kernel/kfifo.c | 76 |
1 files changed, 49 insertions, 27 deletions
diff --git a/kernel/kfifo.c b/kernel/kfifo.c index ab615e695052..b50bb622e8b0 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c | |||
@@ -159,8 +159,9 @@ static inline void __kfifo_out_data(struct kfifo *fifo, | |||
159 | memcpy(to + l, fifo->buffer, len - l); | 159 | memcpy(to + l, fifo->buffer, len - l); |
160 | } | 160 | } |
161 | 161 | ||
162 | static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo, | 162 | static inline int __kfifo_from_user_data(struct kfifo *fifo, |
163 | const void __user *from, unsigned int len, unsigned int off) | 163 | const void __user *from, unsigned int len, unsigned int off, |
164 | unsigned *lenout) | ||
164 | { | 165 | { |
165 | unsigned int l; | 166 | unsigned int l; |
166 | int ret; | 167 | int ret; |
@@ -177,16 +178,20 @@ static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo, | |||
177 | /* first put the data starting from fifo->in to buffer end */ | 178 | /* first put the data starting from fifo->in to buffer end */ |
178 | l = min(len, fifo->size - off); | 179 | l = min(len, fifo->size - off); |
179 | ret = copy_from_user(fifo->buffer + off, from, l); | 180 | ret = copy_from_user(fifo->buffer + off, from, l); |
180 | 181 | if (unlikely(ret)) { | |
181 | if (unlikely(ret)) | 182 | *lenout = ret; |
182 | return ret + len - l; | 183 | return -EFAULT; |
184 | } | ||
185 | *lenout = l; | ||
183 | 186 | ||
184 | /* then put the rest (if any) at the beginning of the buffer */ | 187 | /* then put the rest (if any) at the beginning of the buffer */ |
185 | return copy_from_user(fifo->buffer, from + l, len - l); | 188 | ret = copy_from_user(fifo->buffer, from + l, len - l); |
189 | *lenout += ret ? ret : len - l; | ||
190 | return ret ? -EFAULT : 0; | ||
186 | } | 191 | } |
187 | 192 | ||
188 | static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo, | 193 | static inline int __kfifo_to_user_data(struct kfifo *fifo, |
189 | void __user *to, unsigned int len, unsigned int off) | 194 | void __user *to, unsigned int len, unsigned int off, unsigned *lenout) |
190 | { | 195 | { |
191 | unsigned int l; | 196 | unsigned int l; |
192 | int ret; | 197 | int ret; |
@@ -203,12 +208,21 @@ static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo, | |||
203 | /* first get the data from fifo->out until the end of the buffer */ | 208 | /* first get the data from fifo->out until the end of the buffer */ |
204 | l = min(len, fifo->size - off); | 209 | l = min(len, fifo->size - off); |
205 | ret = copy_to_user(to, fifo->buffer + off, l); | 210 | ret = copy_to_user(to, fifo->buffer + off, l); |
206 | 211 | *lenout = l; | |
207 | if (unlikely(ret)) | 212 | if (unlikely(ret)) { |
208 | return ret + len - l; | 213 | *lenout -= ret; |
214 | return -EFAULT; | ||
215 | } | ||
209 | 216 | ||
210 | /* then get the rest (if any) from the beginning of the buffer */ | 217 | /* then get the rest (if any) from the beginning of the buffer */ |
211 | return copy_to_user(to + l, fifo->buffer, len - l); | 218 | len -= l; |
219 | ret = copy_to_user(to + l, fifo->buffer, len); | ||
220 | if (unlikely(ret)) { | ||
221 | *lenout += len - ret; | ||
222 | return -EFAULT; | ||
223 | } | ||
224 | *lenout += len; | ||
225 | return 0; | ||
212 | } | 226 | } |
213 | 227 | ||
214 | unsigned int __kfifo_in_n(struct kfifo *fifo, | 228 | unsigned int __kfifo_in_n(struct kfifo *fifo, |
@@ -299,10 +313,13 @@ EXPORT_SYMBOL(__kfifo_out_generic); | |||
299 | unsigned int __kfifo_from_user_n(struct kfifo *fifo, | 313 | unsigned int __kfifo_from_user_n(struct kfifo *fifo, |
300 | const void __user *from, unsigned int len, unsigned int recsize) | 314 | const void __user *from, unsigned int len, unsigned int recsize) |
301 | { | 315 | { |
316 | unsigned total; | ||
317 | |||
302 | if (kfifo_avail(fifo) < len + recsize) | 318 | if (kfifo_avail(fifo) < len + recsize) |
303 | return len + 1; | 319 | return len + 1; |
304 | 320 | ||
305 | return __kfifo_from_user_data(fifo, from, len, recsize); | 321 | __kfifo_from_user_data(fifo, from, len, recsize, &total); |
322 | return total; | ||
306 | } | 323 | } |
307 | EXPORT_SYMBOL(__kfifo_from_user_n); | 324 | EXPORT_SYMBOL(__kfifo_from_user_n); |
308 | 325 | ||
@@ -313,18 +330,21 @@ EXPORT_SYMBOL(__kfifo_from_user_n); | |||
313 | * @len: the length of the data to be added. | 330 | * @len: the length of the data to be added. |
314 | * | 331 | * |
315 | * This function copies at most @len bytes from the @from into the | 332 | * This function copies at most @len bytes from the @from into the |
316 | * FIFO depending and returns the number of copied bytes. | 333 | * FIFO depending and returns -EFAULT/0. |
317 | * | 334 | * |
318 | * Note that with only one concurrent reader and one concurrent | 335 | * Note that with only one concurrent reader and one concurrent |
319 | * writer, you don't need extra locking to use these functions. | 336 | * writer, you don't need extra locking to use these functions. |
320 | */ | 337 | */ |
321 | unsigned int kfifo_from_user(struct kfifo *fifo, | 338 | int kfifo_from_user(struct kfifo *fifo, |
322 | const void __user *from, unsigned int len) | 339 | const void __user *from, unsigned int len, unsigned *total) |
323 | { | 340 | { |
341 | int ret; | ||
324 | len = min(kfifo_avail(fifo), len); | 342 | len = min(kfifo_avail(fifo), len); |
325 | len -= __kfifo_from_user_data(fifo, from, len, 0); | 343 | ret = __kfifo_from_user_data(fifo, from, len, 0, total); |
344 | if (ret) | ||
345 | return ret; | ||
326 | __kfifo_add_in(fifo, len); | 346 | __kfifo_add_in(fifo, len); |
327 | return len; | 347 | return 0; |
328 | } | 348 | } |
329 | EXPORT_SYMBOL(kfifo_from_user); | 349 | EXPORT_SYMBOL(kfifo_from_user); |
330 | 350 | ||
@@ -339,17 +359,17 @@ unsigned int __kfifo_to_user_n(struct kfifo *fifo, | |||
339 | void __user *to, unsigned int len, unsigned int reclen, | 359 | void __user *to, unsigned int len, unsigned int reclen, |
340 | unsigned int recsize) | 360 | unsigned int recsize) |
341 | { | 361 | { |
342 | unsigned int ret; | 362 | unsigned int ret, total; |
343 | 363 | ||
344 | if (kfifo_len(fifo) < reclen + recsize) | 364 | if (kfifo_len(fifo) < reclen + recsize) |
345 | return len; | 365 | return len; |
346 | 366 | ||
347 | ret = __kfifo_to_user_data(fifo, to, reclen, recsize); | 367 | ret = __kfifo_to_user_data(fifo, to, reclen, recsize, &total); |
348 | 368 | ||
349 | if (likely(ret == 0)) | 369 | if (likely(ret == 0)) |
350 | __kfifo_add_out(fifo, reclen + recsize); | 370 | __kfifo_add_out(fifo, reclen + recsize); |
351 | 371 | ||
352 | return ret; | 372 | return total; |
353 | } | 373 | } |
354 | EXPORT_SYMBOL(__kfifo_to_user_n); | 374 | EXPORT_SYMBOL(__kfifo_to_user_n); |
355 | 375 | ||
@@ -358,20 +378,22 @@ EXPORT_SYMBOL(__kfifo_to_user_n); | |||
358 | * @fifo: the fifo to be used. | 378 | * @fifo: the fifo to be used. |
359 | * @to: where the data must be copied. | 379 | * @to: where the data must be copied. |
360 | * @len: the size of the destination buffer. | 380 | * @len: the size of the destination buffer. |
381 | @ @lenout: pointer to output variable with copied data | ||
361 | * | 382 | * |
362 | * This function copies at most @len bytes from the FIFO into the | 383 | * This function copies at most @len bytes from the FIFO into the |
363 | * @to buffer and returns the number of copied bytes. | 384 | * @to buffer and 0 or -EFAULT. |
364 | * | 385 | * |
365 | * Note that with only one concurrent reader and one concurrent | 386 | * Note that with only one concurrent reader and one concurrent |
366 | * writer, you don't need extra locking to use these functions. | 387 | * writer, you don't need extra locking to use these functions. |
367 | */ | 388 | */ |
368 | unsigned int kfifo_to_user(struct kfifo *fifo, | 389 | int kfifo_to_user(struct kfifo *fifo, |
369 | void __user *to, unsigned int len) | 390 | void __user *to, unsigned int len, unsigned *lenout) |
370 | { | 391 | { |
392 | int ret; | ||
371 | len = min(kfifo_len(fifo), len); | 393 | len = min(kfifo_len(fifo), len); |
372 | len -= __kfifo_to_user_data(fifo, to, len, 0); | 394 | ret = __kfifo_to_user_data(fifo, to, len, 0, lenout); |
373 | __kfifo_add_out(fifo, len); | 395 | __kfifo_add_out(fifo, *lenout); |
374 | return len; | 396 | return ret; |
375 | } | 397 | } |
376 | EXPORT_SYMBOL(kfifo_to_user); | 398 | EXPORT_SYMBOL(kfifo_to_user); |
377 | 399 | ||