This patches provides two approaches for enabling direct IO from user space:
- userspace(such as losetup) can pass 'file' which is opened/fcntl as O_DIRECT - sysfs file is provided to run dio tests easily Also __loop_update_dio() is introduced to check if direct I/O can be used on current loop setting. Signed-off-by: Ming Lei <ming....@canonical.com> --- drivers/block/loop.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/block/loop.h | 1 + 2 files changed, 77 insertions(+) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 555b895..0b1ee2b 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -421,6 +421,44 @@ struct switch_request { struct completion wait; }; +static void __loop_update_dio(struct loop_device *lo, bool dio) +{ + struct file *file = lo->lo_backing_file; + struct inode *inode = file->f_mapping->host; + bool use_dio; + + /* + * loop block's logical block size is 512, now + * we support direct I/O only if the backing + * block devices' minimize I/O size is 512. + */ + if (dio) { + if (inode->i_sb->s_bdev && + bdev_io_min(inode->i_sb->s_bdev) == 512) + use_dio = true; + else + use_dio = false; + } else { + use_dio = false; + } + + if (lo->use_dio == use_dio) + return; + + /* flush dirty pages to avoid stale data from following dio */ + if (use_dio) + vfs_fsync(file, 0); + + blk_mq_freeze_queue(lo->lo_queue); + lo->use_dio = use_dio; + blk_mq_unfreeze_queue(lo->lo_queue); +} + +static inline void loop_update_dio(struct loop_device *lo) +{ + __loop_update_dio(lo, io_is_direct(lo->lo_backing_file)); +} + /* * Do the actual switch; called from the BIO completion routine */ @@ -441,6 +479,7 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) mapping->host->i_bdev->bd_block_size : PAGE_SIZE; lo->old_gfp_mask = mapping_gfp_mask(mapping); mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); + loop_update_dio(lo); } /* @@ -627,6 +666,41 @@ static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf) return sprintf(buf, "%s\n", partscan ? "1" : "0"); } +static ssize_t loop_attr_do_show_use_dio(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + struct loop_device *lo = disk->private_data; + + return sprintf(buf, "%s\n", lo->use_dio ? "1" : "0"); +} + +ssize_t loop_attr_do_store_use_dio(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct gendisk *disk = dev_to_disk(dev); + struct loop_device *lo = disk->private_data; + int err; + unsigned long v; + + if (!lo->lo_backing_file) + return -ENODEV; + + err = kstrtoul(buf, 10, &v); + if (err < 0) + return err; + + __loop_update_dio(lo, !!v); + if (lo->use_dio != !!v) + return -EINVAL; + return count; +} + +static struct device_attribute loop_attr_use_dio = + __ATTR(use_dio, S_IRUGO | S_IWUSR, loop_attr_do_show_use_dio, + loop_attr_do_store_use_dio); + LOOP_ATTR_RO(backing_file); LOOP_ATTR_RO(offset); LOOP_ATTR_RO(sizelimit); @@ -639,6 +713,7 @@ static struct attribute *loop_attrs[] = { &loop_attr_sizelimit.attr, &loop_attr_autoclear.attr, &loop_attr_partscan.attr, + &loop_attr_use_dio.attr, NULL, }; @@ -783,6 +858,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync) blk_queue_flush(lo->lo_queue, REQ_FLUSH); + loop_update_dio(lo); set_capacity(lo->lo_disk, size); bd_set_size(bdev, size << 9); loop_sysfs_init(lo); diff --git a/drivers/block/loop.h b/drivers/block/loop.h index b6c7d21..d1de221 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -58,6 +58,7 @@ struct loop_device { struct mutex lo_ctl_mutex; struct kthread_worker worker; struct task_struct *worker_task; + bool use_dio; struct request_queue *lo_queue; struct blk_mq_tag_set tag_set; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/