aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-21 19:03:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-21 19:03:40 -0400
commit548453fd107f789f5f1bc2dc13cc432ceb3b5efd (patch)
treedc5a62d49260d66b7390ef110113134e3bef9152 /fs
parent9fd91217b15751997cab35ad309b37b44eaa6774 (diff)
parentfb199746303a6bfd6121834ec9e810471185c530 (diff)
Merge branch 'for-2.6.26' of git://git.kernel.dk/linux-2.6-block
* 'for-2.6.26' of git://git.kernel.dk/linux-2.6-block: block: fix blk_register_queue() return value block: fix memory hotplug and bouncing in block layer block: replace remaining __FUNCTION__ occurrences Kconfig: clean up block/Kconfig help descriptions cciss: fix warning oops on rmmod of driver cciss: Fix race between disk-adding code and interrupt handler block: move the padding adjustment to blk_rq_map_sg block: add bio_copy_user_iov support to blk_rq_map_user_iov block: convert bio_copy_user to bio_copy_user_iov loop: manage partitions in disk image cdrom: use kmalloced buffers instead of buffers on stack cdrom: make unregister_cdrom() return void cdrom: use list_head for cdrom_device_info list cdrom: protect cdrom_device_info list by mutex cdrom: cleanup hardcoded error-code cdrom: remove ifdef CONFIG_SYSCTL
Diffstat (limited to 'fs')
-rw-r--r--fs/bio.c158
1 files changed, 117 insertions, 41 deletions
diff --git a/fs/bio.c b/fs/bio.c
index 553b5b7960ad..6e0b6f66df03 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -444,22 +444,27 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len,
444 444
445struct bio_map_data { 445struct bio_map_data {
446 struct bio_vec *iovecs; 446 struct bio_vec *iovecs;
447 void __user *userptr; 447 int nr_sgvecs;
448 struct sg_iovec *sgvecs;
448}; 449};
449 450
450static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio) 451static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio,
452 struct sg_iovec *iov, int iov_count)
451{ 453{
452 memcpy(bmd->iovecs, bio->bi_io_vec, sizeof(struct bio_vec) * bio->bi_vcnt); 454 memcpy(bmd->iovecs, bio->bi_io_vec, sizeof(struct bio_vec) * bio->bi_vcnt);
455 memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count);
456 bmd->nr_sgvecs = iov_count;
453 bio->bi_private = bmd; 457 bio->bi_private = bmd;
454} 458}
455 459
456static void bio_free_map_data(struct bio_map_data *bmd) 460static void bio_free_map_data(struct bio_map_data *bmd)
457{ 461{
458 kfree(bmd->iovecs); 462 kfree(bmd->iovecs);
463 kfree(bmd->sgvecs);
459 kfree(bmd); 464 kfree(bmd);
460} 465}
461 466
462static struct bio_map_data *bio_alloc_map_data(int nr_segs) 467static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count)
463{ 468{
464 struct bio_map_data *bmd = kmalloc(sizeof(*bmd), GFP_KERNEL); 469 struct bio_map_data *bmd = kmalloc(sizeof(*bmd), GFP_KERNEL);
465 470
@@ -467,13 +472,71 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs)
467 return NULL; 472 return NULL;
468 473
469 bmd->iovecs = kmalloc(sizeof(struct bio_vec) * nr_segs, GFP_KERNEL); 474 bmd->iovecs = kmalloc(sizeof(struct bio_vec) * nr_segs, GFP_KERNEL);
470 if (bmd->iovecs) 475 if (!bmd->iovecs) {
476 kfree(bmd);
477 return NULL;
478 }
479
480 bmd->sgvecs = kmalloc(sizeof(struct sg_iovec) * iov_count, GFP_KERNEL);
481 if (bmd->sgvecs)
471 return bmd; 482 return bmd;
472 483
484 kfree(bmd->iovecs);
473 kfree(bmd); 485 kfree(bmd);
474 return NULL; 486 return NULL;
475} 487}
476 488
489static int __bio_copy_iov(struct bio *bio, struct sg_iovec *iov, int iov_count,
490 int uncopy)
491{
492 int ret = 0, i;
493 struct bio_vec *bvec;
494 int iov_idx = 0;
495 unsigned int iov_off = 0;
496 int read = bio_data_dir(bio) == READ;
497
498 __bio_for_each_segment(bvec, bio, i, 0) {
499 char *bv_addr = page_address(bvec->bv_page);
500 unsigned int bv_len = bvec->bv_len;
501
502 while (bv_len && iov_idx < iov_count) {
503 unsigned int bytes;
504 char *iov_addr;
505
506 bytes = min_t(unsigned int,
507 iov[iov_idx].iov_len - iov_off, bv_len);
508 iov_addr = iov[iov_idx].iov_base + iov_off;
509
510 if (!ret) {
511 if (!read && !uncopy)
512 ret = copy_from_user(bv_addr, iov_addr,
513 bytes);
514 if (read && uncopy)
515 ret = copy_to_user(iov_addr, bv_addr,
516 bytes);
517
518 if (ret)
519 ret = -EFAULT;
520 }
521
522 bv_len -= bytes;
523 bv_addr += bytes;
524 iov_addr += bytes;
525 iov_off += bytes;
526
527 if (iov[iov_idx].iov_len == iov_off) {
528 iov_idx++;
529 iov_off = 0;
530 }
531 }
532
533 if (uncopy)
534 __free_page(bvec->bv_page);
535 }
536
537 return ret;
538}
539
477/** 540/**
478 * bio_uncopy_user - finish previously mapped bio 541 * bio_uncopy_user - finish previously mapped bio
479 * @bio: bio being terminated 542 * @bio: bio being terminated
@@ -484,55 +547,56 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs)
484int bio_uncopy_user(struct bio *bio) 547int bio_uncopy_user(struct bio *bio)
485{ 548{
486 struct bio_map_data *bmd = bio->bi_private; 549 struct bio_map_data *bmd = bio->bi_private;
487 const int read = bio_data_dir(bio) == READ; 550 int ret;
488 struct bio_vec *bvec;
489 int i, ret = 0;
490 551
491 __bio_for_each_segment(bvec, bio, i, 0) { 552 ret = __bio_copy_iov(bio, bmd->sgvecs, bmd->nr_sgvecs, 1);
492 char *addr = page_address(bvec->bv_page);
493 unsigned int len = bmd->iovecs[i].bv_len;
494 553
495 if (read && !ret && copy_to_user(bmd->userptr, addr, len))
496 ret = -EFAULT;
497
498 __free_page(bvec->bv_page);
499 bmd->userptr += len;
500 }
501 bio_free_map_data(bmd); 554 bio_free_map_data(bmd);
502 bio_put(bio); 555 bio_put(bio);
503 return ret; 556 return ret;
504} 557}
505 558
506/** 559/**
507 * bio_copy_user - copy user data to bio 560 * bio_copy_user_iov - copy user data to bio
508 * @q: destination block queue 561 * @q: destination block queue
509 * @uaddr: start of user address 562 * @iov: the iovec.
510 * @len: length in bytes 563 * @iov_count: number of elements in the iovec
511 * @write_to_vm: bool indicating writing to pages or not 564 * @write_to_vm: bool indicating writing to pages or not
512 * 565 *
513 * Prepares and returns a bio for indirect user io, bouncing data 566 * Prepares and returns a bio for indirect user io, bouncing data
514 * to/from kernel pages as necessary. Must be paired with 567 * to/from kernel pages as necessary. Must be paired with
515 * call bio_uncopy_user() on io completion. 568 * call bio_uncopy_user() on io completion.
516 */ 569 */
517struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, 570struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov,
518 unsigned int len, int write_to_vm) 571 int iov_count, int write_to_vm)
519{ 572{
520 unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
521 unsigned long start = uaddr >> PAGE_SHIFT;
522 struct bio_map_data *bmd; 573 struct bio_map_data *bmd;
523 struct bio_vec *bvec; 574 struct bio_vec *bvec;
524 struct page *page; 575 struct page *page;
525 struct bio *bio; 576 struct bio *bio;
526 int i, ret; 577 int i, ret;
578 int nr_pages = 0;
579 unsigned int len = 0;
527 580
528 bmd = bio_alloc_map_data(end - start); 581 for (i = 0; i < iov_count; i++) {
582 unsigned long uaddr;
583 unsigned long end;
584 unsigned long start;
585
586 uaddr = (unsigned long)iov[i].iov_base;
587 end = (uaddr + iov[i].iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
588 start = uaddr >> PAGE_SHIFT;
589
590 nr_pages += end - start;
591 len += iov[i].iov_len;
592 }
593
594 bmd = bio_alloc_map_data(nr_pages, iov_count);
529 if (!bmd) 595 if (!bmd)
530 return ERR_PTR(-ENOMEM); 596 return ERR_PTR(-ENOMEM);
531 597
532 bmd->userptr = (void __user *) uaddr;
533
534 ret = -ENOMEM; 598 ret = -ENOMEM;
535 bio = bio_alloc(GFP_KERNEL, end - start); 599 bio = bio_alloc(GFP_KERNEL, nr_pages);
536 if (!bio) 600 if (!bio)
537 goto out_bmd; 601 goto out_bmd;
538 602
@@ -564,22 +628,12 @@ struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr,
564 * success 628 * success
565 */ 629 */
566 if (!write_to_vm) { 630 if (!write_to_vm) {
567 char __user *p = (char __user *) uaddr; 631 ret = __bio_copy_iov(bio, iov, iov_count, 0);
568 632 if (ret)
569 /* 633 goto cleanup;
570 * for a write, copy in data to kernel pages
571 */
572 ret = -EFAULT;
573 bio_for_each_segment(bvec, bio, i) {
574 char *addr = page_address(bvec->bv_page);
575
576 if (copy_from_user(addr, p, bvec->bv_len))
577 goto cleanup;
578 p += bvec->bv_len;
579 }
580 } 634 }
581 635
582 bio_set_map_data(bmd, bio); 636 bio_set_map_data(bmd, bio, iov, iov_count);
583 return bio; 637 return bio;
584cleanup: 638cleanup:
585 bio_for_each_segment(bvec, bio, i) 639 bio_for_each_segment(bvec, bio, i)
@@ -591,6 +645,28 @@ out_bmd:
591 return ERR_PTR(ret); 645 return ERR_PTR(ret);
592} 646}
593 647
648/**
649 * bio_copy_user - copy user data to bio
650 * @q: destination block queue
651 * @uaddr: start of user address
652 * @len: length in bytes
653 * @write_to_vm: bool indicating writing to pages or not
654 *
655 * Prepares and returns a bio for indirect user io, bouncing data
656 * to/from kernel pages as necessary. Must be paired with
657 * call bio_uncopy_user() on io completion.
658 */
659struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr,
660 unsigned int len, int write_to_vm)
661{
662 struct sg_iovec iov;
663
664 iov.iov_base = (void __user *)uaddr;
665 iov.iov_len = len;
666
667 return bio_copy_user_iov(q, &iov, 1, write_to_vm);
668}
669
594static struct bio *__bio_map_user_iov(struct request_queue *q, 670static struct bio *__bio_map_user_iov(struct request_queue *q,
595 struct block_device *bdev, 671 struct block_device *bdev,
596 struct sg_iovec *iov, int iov_count, 672 struct sg_iovec *iov, int iov_count,