diff options
| -rw-r--r-- | drivers/char/raw.c | 2 | ||||
| -rw-r--r-- | fs/block_dev.c | 62 | ||||
| -rw-r--r-- | include/linux/fs.h | 4 |
3 files changed, 65 insertions, 3 deletions
diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 54a3a6d09819..0bb207eaef2f 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c | |||
| @@ -285,7 +285,7 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, | |||
| 285 | 285 | ||
| 286 | static const struct file_operations raw_fops = { | 286 | static const struct file_operations raw_fops = { |
| 287 | .read = do_sync_read, | 287 | .read = do_sync_read, |
| 288 | .aio_read = generic_file_aio_read, | 288 | .aio_read = blkdev_aio_read, |
| 289 | .write = do_sync_write, | 289 | .write = do_sync_write, |
| 290 | .aio_write = blkdev_aio_write, | 290 | .aio_write = blkdev_aio_write, |
| 291 | .fsync = blkdev_fsync, | 291 | .fsync = blkdev_fsync, |
diff --git a/fs/block_dev.c b/fs/block_dev.c index 38e721b35d45..cdfb625824e2 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
| @@ -116,6 +116,8 @@ EXPORT_SYMBOL(invalidate_bdev); | |||
| 116 | 116 | ||
| 117 | int set_blocksize(struct block_device *bdev, int size) | 117 | int set_blocksize(struct block_device *bdev, int size) |
| 118 | { | 118 | { |
| 119 | struct address_space *mapping; | ||
| 120 | |||
| 119 | /* Size must be a power of two, and between 512 and PAGE_SIZE */ | 121 | /* Size must be a power of two, and between 512 and PAGE_SIZE */ |
| 120 | if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size)) | 122 | if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size)) |
| 121 | return -EINVAL; | 123 | return -EINVAL; |
| @@ -124,6 +126,20 @@ int set_blocksize(struct block_device *bdev, int size) | |||
| 124 | if (size < bdev_logical_block_size(bdev)) | 126 | if (size < bdev_logical_block_size(bdev)) |
| 125 | return -EINVAL; | 127 | return -EINVAL; |
| 126 | 128 | ||
| 129 | /* Prevent starting I/O or mapping the device */ | ||
| 130 | down_write(&bdev->bd_block_size_semaphore); | ||
| 131 | |||
| 132 | /* Check that the block device is not memory mapped */ | ||
| 133 | mapping = bdev->bd_inode->i_mapping; | ||
| 134 | mutex_lock(&mapping->i_mmap_mutex); | ||
| 135 | if (!prio_tree_empty(&mapping->i_mmap) || | ||
| 136 | !list_empty(&mapping->i_mmap_nonlinear)) { | ||
| 137 | mutex_unlock(&mapping->i_mmap_mutex); | ||
| 138 | up_write(&bdev->bd_block_size_semaphore); | ||
| 139 | return -EBUSY; | ||
| 140 | } | ||
| 141 | mutex_unlock(&mapping->i_mmap_mutex); | ||
| 142 | |||
| 127 | /* Don't change the size if it is same as current */ | 143 | /* Don't change the size if it is same as current */ |
| 128 | if (bdev->bd_block_size != size) { | 144 | if (bdev->bd_block_size != size) { |
| 129 | sync_blockdev(bdev); | 145 | sync_blockdev(bdev); |
| @@ -131,6 +147,9 @@ int set_blocksize(struct block_device *bdev, int size) | |||
| 131 | bdev->bd_inode->i_blkbits = blksize_bits(size); | 147 | bdev->bd_inode->i_blkbits = blksize_bits(size); |
| 132 | kill_bdev(bdev); | 148 | kill_bdev(bdev); |
| 133 | } | 149 | } |
| 150 | |||
| 151 | up_write(&bdev->bd_block_size_semaphore); | ||
| 152 | |||
| 134 | return 0; | 153 | return 0; |
| 135 | } | 154 | } |
| 136 | 155 | ||
| @@ -472,6 +491,7 @@ static void init_once(void *foo) | |||
| 472 | inode_init_once(&ei->vfs_inode); | 491 | inode_init_once(&ei->vfs_inode); |
| 473 | /* Initialize mutex for freeze. */ | 492 | /* Initialize mutex for freeze. */ |
| 474 | mutex_init(&bdev->bd_fsfreeze_mutex); | 493 | mutex_init(&bdev->bd_fsfreeze_mutex); |
| 494 | init_rwsem(&bdev->bd_block_size_semaphore); | ||
| 475 | } | 495 | } |
| 476 | 496 | ||
| 477 | static inline void __bd_forget(struct inode *inode) | 497 | static inline void __bd_forget(struct inode *inode) |
| @@ -1567,6 +1587,22 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) | |||
| 1567 | return blkdev_ioctl(bdev, mode, cmd, arg); | 1587 | return blkdev_ioctl(bdev, mode, cmd, arg); |
| 1568 | } | 1588 | } |
| 1569 | 1589 | ||
| 1590 | ssize_t blkdev_aio_read(struct kiocb *iocb, const struct iovec *iov, | ||
| 1591 | unsigned long nr_segs, loff_t pos) | ||
| 1592 | { | ||
| 1593 | ssize_t ret; | ||
| 1594 | struct block_device *bdev = I_BDEV(iocb->ki_filp->f_mapping->host); | ||
| 1595 | |||
| 1596 | down_read(&bdev->bd_block_size_semaphore); | ||
| 1597 | |||
| 1598 | ret = generic_file_aio_read(iocb, iov, nr_segs, pos); | ||
| 1599 | |||
| 1600 | up_read(&bdev->bd_block_size_semaphore); | ||
| 1601 | |||
| 1602 | return ret; | ||
| 1603 | } | ||
| 1604 | EXPORT_SYMBOL_GPL(blkdev_aio_read); | ||
| 1605 | |||
| 1570 | /* | 1606 | /* |
| 1571 | * Write data to the block device. Only intended for the block device itself | 1607 | * Write data to the block device. Only intended for the block device itself |
| 1572 | * and the raw driver which basically is a fake block device. | 1608 | * and the raw driver which basically is a fake block device. |
| @@ -1578,12 +1614,16 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, | |||
| 1578 | unsigned long nr_segs, loff_t pos) | 1614 | unsigned long nr_segs, loff_t pos) |
| 1579 | { | 1615 | { |
| 1580 | struct file *file = iocb->ki_filp; | 1616 | struct file *file = iocb->ki_filp; |
| 1617 | struct block_device *bdev = I_BDEV(file->f_mapping->host); | ||
| 1581 | struct blk_plug plug; | 1618 | struct blk_plug plug; |
| 1582 | ssize_t ret; | 1619 | ssize_t ret; |
| 1583 | 1620 | ||
| 1584 | BUG_ON(iocb->ki_pos != pos); | 1621 | BUG_ON(iocb->ki_pos != pos); |
| 1585 | 1622 | ||
| 1586 | blk_start_plug(&plug); | 1623 | blk_start_plug(&plug); |
| 1624 | |||
| 1625 | down_read(&bdev->bd_block_size_semaphore); | ||
| 1626 | |||
| 1587 | ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos); | 1627 | ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos); |
| 1588 | if (ret > 0 || ret == -EIOCBQUEUED) { | 1628 | if (ret > 0 || ret == -EIOCBQUEUED) { |
| 1589 | ssize_t err; | 1629 | ssize_t err; |
| @@ -1592,11 +1632,29 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, | |||
| 1592 | if (err < 0 && ret > 0) | 1632 | if (err < 0 && ret > 0) |
| 1593 | ret = err; | 1633 | ret = err; |
| 1594 | } | 1634 | } |
| 1635 | |||
| 1636 | up_read(&bdev->bd_block_size_semaphore); | ||
| 1637 | |||
| 1595 | blk_finish_plug(&plug); | 1638 | blk_finish_plug(&plug); |
| 1639 | |||
| 1596 | return ret; | 1640 | return ret; |
| 1597 | } | 1641 | } |
| 1598 | EXPORT_SYMBOL_GPL(blkdev_aio_write); | 1642 | EXPORT_SYMBOL_GPL(blkdev_aio_write); |
| 1599 | 1643 | ||
| 1644 | int blkdev_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 1645 | { | ||
| 1646 | int ret; | ||
| 1647 | struct block_device *bdev = I_BDEV(file->f_mapping->host); | ||
| 1648 | |||
| 1649 | down_read(&bdev->bd_block_size_semaphore); | ||
| 1650 | |||
| 1651 | ret = generic_file_mmap(file, vma); | ||
| 1652 | |||
| 1653 | up_read(&bdev->bd_block_size_semaphore); | ||
| 1654 | |||
| 1655 | return ret; | ||
| 1656 | } | ||
| 1657 | |||
| 1600 | /* | 1658 | /* |
| 1601 | * Try to release a page associated with block device when the system | 1659 | * Try to release a page associated with block device when the system |
| 1602 | * is under memory pressure. | 1660 | * is under memory pressure. |
| @@ -1627,9 +1685,9 @@ const struct file_operations def_blk_fops = { | |||
| 1627 | .llseek = block_llseek, | 1685 | .llseek = block_llseek, |
| 1628 | .read = do_sync_read, | 1686 | .read = do_sync_read, |
| 1629 | .write = do_sync_write, | 1687 | .write = do_sync_write, |
| 1630 | .aio_read = generic_file_aio_read, | 1688 | .aio_read = blkdev_aio_read, |
| 1631 | .aio_write = blkdev_aio_write, | 1689 | .aio_write = blkdev_aio_write, |
| 1632 | .mmap = generic_file_mmap, | 1690 | .mmap = blkdev_mmap, |
| 1633 | .fsync = blkdev_fsync, | 1691 | .fsync = blkdev_fsync, |
| 1634 | .unlocked_ioctl = block_ioctl, | 1692 | .unlocked_ioctl = block_ioctl, |
| 1635 | #ifdef CONFIG_COMPAT | 1693 | #ifdef CONFIG_COMPAT |
diff --git a/include/linux/fs.h b/include/linux/fs.h index bd6f6e7ca48e..e60bbd0225d5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -725,6 +725,8 @@ struct block_device { | |||
| 725 | int bd_fsfreeze_count; | 725 | int bd_fsfreeze_count; |
| 726 | /* Mutex for freeze */ | 726 | /* Mutex for freeze */ |
| 727 | struct mutex bd_fsfreeze_mutex; | 727 | struct mutex bd_fsfreeze_mutex; |
| 728 | /* A semaphore that prevents I/O while block size is being changed */ | ||
| 729 | struct rw_semaphore bd_block_size_semaphore; | ||
| 728 | }; | 730 | }; |
| 729 | 731 | ||
| 730 | /* | 732 | /* |
| @@ -2565,6 +2567,8 @@ extern int generic_segment_checks(const struct iovec *iov, | |||
| 2565 | unsigned long *nr_segs, size_t *count, int access_flags); | 2567 | unsigned long *nr_segs, size_t *count, int access_flags); |
| 2566 | 2568 | ||
| 2567 | /* fs/block_dev.c */ | 2569 | /* fs/block_dev.c */ |
| 2570 | extern ssize_t blkdev_aio_read(struct kiocb *iocb, const struct iovec *iov, | ||
| 2571 | unsigned long nr_segs, loff_t pos); | ||
| 2568 | extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, | 2572 | extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, |
| 2569 | unsigned long nr_segs, loff_t pos); | 2573 | unsigned long nr_segs, loff_t pos); |
| 2570 | extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end, | 2574 | extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end, |
