diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-21 19:03:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-21 19:03:40 -0400 |
commit | 548453fd107f789f5f1bc2dc13cc432ceb3b5efd (patch) | |
tree | dc5a62d49260d66b7390ef110113134e3bef9152 /fs | |
parent | 9fd91217b15751997cab35ad309b37b44eaa6774 (diff) | |
parent | fb199746303a6bfd6121834ec9e810471185c530 (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.c | 158 |
1 files changed, 117 insertions, 41 deletions
@@ -444,22 +444,27 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len, | |||
444 | 444 | ||
445 | struct bio_map_data { | 445 | struct 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 | ||
450 | static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio) | 451 | static 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 | ||
456 | static void bio_free_map_data(struct bio_map_data *bmd) | 460 | static 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 | ||
462 | static struct bio_map_data *bio_alloc_map_data(int nr_segs) | 467 | static 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 | ||
489 | static 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) | |||
484 | int bio_uncopy_user(struct bio *bio) | 547 | int 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 | */ |
517 | struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, | 570 | struct 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; |
584 | cleanup: | 638 | cleanup: |
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 | */ | ||
659 | struct 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 | |||
594 | static struct bio *__bio_map_user_iov(struct request_queue *q, | 670 | static 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, |