diff options
| -rw-r--r-- | block/bio.c | 60 |
1 files changed, 44 insertions, 16 deletions
diff --git a/block/bio.c b/block/bio.c index 471d7382c7d1..54da51ed43de 100644 --- a/block/bio.c +++ b/block/bio.c | |||
| @@ -1563,9 +1563,8 @@ static void bio_copy_kern_endio(struct bio *bio, int err) | |||
| 1563 | { | 1563 | { |
| 1564 | struct bio_vec *bvec; | 1564 | struct bio_vec *bvec; |
| 1565 | const int read = bio_data_dir(bio) == READ; | 1565 | const int read = bio_data_dir(bio) == READ; |
| 1566 | struct bio_map_data *bmd = bio->bi_private; | 1566 | char *p = bio->bi_private; |
| 1567 | int i; | 1567 | int i; |
| 1568 | char *p = bmd->sgvecs[0].iov_base; | ||
| 1569 | 1568 | ||
| 1570 | bio_for_each_segment_all(bvec, bio, i) { | 1569 | bio_for_each_segment_all(bvec, bio, i) { |
| 1571 | char *addr = page_address(bvec->bv_page); | 1570 | char *addr = page_address(bvec->bv_page); |
| @@ -1577,7 +1576,6 @@ static void bio_copy_kern_endio(struct bio *bio, int err) | |||
| 1577 | p += bvec->bv_len; | 1576 | p += bvec->bv_len; |
| 1578 | } | 1577 | } |
| 1579 | 1578 | ||
| 1580 | kfree(bmd); | ||
| 1581 | bio_put(bio); | 1579 | bio_put(bio); |
| 1582 | } | 1580 | } |
| 1583 | 1581 | ||
| @@ -1595,28 +1593,58 @@ static void bio_copy_kern_endio(struct bio *bio, int err) | |||
| 1595 | struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, | 1593 | struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, |
| 1596 | gfp_t gfp_mask, int reading) | 1594 | gfp_t gfp_mask, int reading) |
| 1597 | { | 1595 | { |
| 1598 | struct bio *bio; | 1596 | unsigned long kaddr = (unsigned long)data; |
| 1597 | unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 1598 | unsigned long start = kaddr >> PAGE_SHIFT; | ||
| 1599 | struct bio_vec *bvec; | 1599 | struct bio_vec *bvec; |
| 1600 | int i; | 1600 | struct bio *bio; |
| 1601 | void *p = data; | ||
| 1602 | int nr_pages = 0, i; | ||
| 1601 | 1603 | ||
| 1602 | bio = bio_copy_user(q, NULL, (unsigned long)data, len, 1, gfp_mask); | 1604 | /* |
| 1603 | if (IS_ERR(bio)) | 1605 | * Overflow, abort |
| 1604 | return bio; | 1606 | */ |
| 1607 | if (end < start) | ||
| 1608 | return ERR_PTR(-EINVAL); | ||
| 1605 | 1609 | ||
| 1606 | if (!reading) { | 1610 | nr_pages = end - start; |
| 1607 | void *p = data; | 1611 | bio = bio_kmalloc(gfp_mask, nr_pages); |
| 1612 | if (!bio) | ||
| 1613 | return ERR_PTR(-ENOMEM); | ||
| 1608 | 1614 | ||
| 1609 | bio_for_each_segment_all(bvec, bio, i) { | 1615 | while (len) { |
| 1610 | char *addr = page_address(bvec->bv_page); | 1616 | struct page *page; |
| 1617 | unsigned int bytes = PAGE_SIZE; | ||
| 1611 | 1618 | ||
| 1612 | memcpy(addr, p, bvec->bv_len); | 1619 | if (bytes > len) |
| 1613 | p += bvec->bv_len; | 1620 | bytes = len; |
| 1614 | } | 1621 | |
| 1622 | page = alloc_page(q->bounce_gfp | gfp_mask); | ||
| 1623 | if (!page) | ||
| 1624 | goto cleanup; | ||
| 1625 | |||
| 1626 | if (!reading) | ||
| 1627 | memcpy(page_address(page), p, bytes); | ||
| 1628 | |||
| 1629 | if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) | ||
| 1630 | break; | ||
| 1631 | |||
| 1632 | len -= bytes; | ||
| 1633 | p += bytes; | ||
| 1615 | } | 1634 | } |
| 1616 | 1635 | ||
| 1617 | bio->bi_end_io = bio_copy_kern_endio; | 1636 | if (!reading) |
| 1637 | bio->bi_rw |= REQ_WRITE; | ||
| 1618 | 1638 | ||
| 1639 | bio->bi_private = data; | ||
| 1640 | bio->bi_end_io = bio_copy_kern_endio; | ||
| 1619 | return bio; | 1641 | return bio; |
| 1642 | |||
| 1643 | cleanup: | ||
| 1644 | bio_for_each_segment_all(bvec, bio, i) | ||
| 1645 | __free_page(bvec->bv_page); | ||
| 1646 | bio_put(bio); | ||
| 1647 | return ERR_PTR(-ENOMEM); | ||
| 1620 | } | 1648 | } |
| 1621 | EXPORT_SYMBOL(bio_copy_kern); | 1649 | EXPORT_SYMBOL(bio_copy_kern); |
| 1622 | 1650 | ||
