Commit 08e6749e authored by Andrew Morton's avatar Andrew Morton Committed by Trond Myklebust

[PATCH] AIO support for raw/O_DIRECT

Patch from Badari Pulavarty <pbadari@us.ibm.com> and myself

This patch adds the infrastructure for performing asynchronous (AIO) blockdev
direct-IO.

- Adds generic_file_aio_write_nolock() and make other
  generic_file_*_write() to use it.

- Modify generic_file_direct_IO() and ->direct_IO() functions to take
  "kiocb *" instead of "file *".

- Renames generic_direct_IO() to blockdev_direct_IO().

- Move generic_file_direct_IO() to mm/filemap.c (it is not
  blockdev-specific, whereas the rest of fs/direct-io.c is).

- Add AIO read/write support to the raw driver.
parent a6d61b6b
...@@ -212,9 +212,20 @@ static ssize_t raw_file_write(struct file *file, const char *buf, ...@@ -212,9 +212,20 @@ static ssize_t raw_file_write(struct file *file, const char *buf,
return generic_file_write_nolock(file, &local_iov, 1, ppos); return generic_file_write_nolock(file, &local_iov, 1, ppos);
} }
static ssize_t raw_file_aio_write(struct kiocb *iocb, const char *buf,
size_t count, loff_t pos)
{
struct iovec local_iov = { .iov_base = (void *)buf, .iov_len = count };
return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
}
static struct file_operations raw_fops = { static struct file_operations raw_fops = {
.read = generic_file_read, .read = generic_file_read,
.aio_read = generic_file_aio_read,
.write = raw_file_write, .write = raw_file_write,
.aio_write = raw_file_aio_write,
.open = raw_open, .open = raw_open,
.release= raw_release, .release= raw_release,
.ioctl = raw_ioctl, .ioctl = raw_ioctl,
......
...@@ -118,12 +118,13 @@ blkdev_get_blocks(struct inode *inode, sector_t iblock, ...@@ -118,12 +118,13 @@ blkdev_get_blocks(struct inode *inode, sector_t iblock,
} }
static int static int
blkdev_direct_IO(int rw, struct file *file, const struct iovec *iov, blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs) loff_t offset, unsigned long nr_segs)
{ {
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host; struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
return generic_direct_IO(rw, inode, inode->i_bdev, iov, offset, return blockdev_direct_IO(rw, iocb, inode, inode->i_bdev, iov, offset,
nr_segs, blkdev_get_blocks); nr_segs, blkdev_get_blocks);
} }
......
...@@ -845,9 +845,9 @@ direct_io_worker(int rw, struct inode *inode, const struct iovec *iov, ...@@ -845,9 +845,9 @@ direct_io_worker(int rw, struct inode *inode, const struct iovec *iov,
* This is a library function for use by filesystem drivers. * This is a library function for use by filesystem drivers.
*/ */
int int
generic_direct_IO(int rw, struct inode *inode, struct block_device *bdev, blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
const struct iovec *iov, loff_t offset, unsigned long nr_segs, struct block_device *bdev, const struct iovec *iov, loff_t offset,
get_blocks_t get_blocks) unsigned long nr_segs, get_blocks_t get_blocks)
{ {
int seg; int seg;
size_t size; size_t size;
...@@ -886,25 +886,3 @@ generic_direct_IO(int rw, struct inode *inode, struct block_device *bdev, ...@@ -886,25 +886,3 @@ generic_direct_IO(int rw, struct inode *inode, struct block_device *bdev,
out: out:
return retval; return retval;
} }
ssize_t
generic_file_direct_IO(int rw, struct file *file, const struct iovec *iov,
loff_t offset, unsigned long nr_segs)
{
struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
ssize_t retval;
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = mapping->a_ops->direct_IO(rw, file, iov, offset, nr_segs);
if (rw == WRITE && mapping->nrpages)
invalidate_inode_pages2(mapping);
out:
return retval;
}
...@@ -652,12 +652,13 @@ ext2_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks, ...@@ -652,12 +652,13 @@ ext2_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks,
} }
static int static int
ext2_direct_IO(int rw, struct file *file, const struct iovec *iov, ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs) loff_t offset, unsigned long nr_segs)
{ {
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host; struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
return generic_direct_IO(rw, inode, inode->i_sb->s_bdev, iov, return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, ext2_get_blocks); offset, nr_segs, ext2_get_blocks);
} }
......
...@@ -1405,10 +1405,11 @@ static int ext3_releasepage(struct page *page, int wait) ...@@ -1405,10 +1405,11 @@ static int ext3_releasepage(struct page *page, int wait)
* If the O_DIRECT write is intantiating holes inside i_size and the machine * If the O_DIRECT write is intantiating holes inside i_size and the machine
* crashes then stale disk data _may_ be exposed inside the file. * crashes then stale disk data _may_ be exposed inside the file.
*/ */
static int ext3_direct_IO(int rw, struct file *file, static int ext3_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset, const struct iovec *iov, loff_t offset,
unsigned long nr_segs) unsigned long nr_segs)
{ {
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host; struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
struct ext3_inode_info *ei = EXT3_I(inode); struct ext3_inode_info *ei = EXT3_I(inode);
handle_t *handle = NULL; handle_t *handle = NULL;
...@@ -1437,8 +1438,8 @@ static int ext3_direct_IO(int rw, struct file *file, ...@@ -1437,8 +1438,8 @@ static int ext3_direct_IO(int rw, struct file *file,
} }
} }
ret = generic_direct_IO(rw, inode, inode->i_sb->s_bdev, iov, offset, ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
nr_segs, ext3_direct_io_get_blocks); offset, nr_segs, ext3_direct_io_get_blocks);
out_stop: out_stop:
if (handle) { if (handle) {
......
...@@ -310,12 +310,13 @@ static sector_t jfs_bmap(struct address_space *mapping, sector_t block) ...@@ -310,12 +310,13 @@ static sector_t jfs_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, jfs_get_block); return generic_block_bmap(mapping, block, jfs_get_block);
} }
static int jfs_direct_IO(int rw, struct file *file, const struct iovec *iov, static int jfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs) loff_t offset, unsigned long nr_segs)
{ {
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host; struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
return generic_direct_IO(rw, inode, inode->i_sb->s_bdev, iov, return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, jfs_get_blocks); offset, nr_segs, jfs_get_blocks);
} }
......
...@@ -235,7 +235,7 @@ do_nfs_direct_IO(int rw, const struct inode *inode, ...@@ -235,7 +235,7 @@ do_nfs_direct_IO(int rw, const struct inode *inode,
* this function to do a write. * this function to do a write.
*/ */
int int
nfs_direct_IO(int rw, struct file *file, const struct iovec *iov, nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs) loff_t offset, unsigned long nr_segs)
{ {
/* None of this works yet, so prevent it from compiling. */ /* None of this works yet, so prevent it from compiling. */
......
...@@ -625,14 +625,15 @@ linvfs_get_blocks_direct( ...@@ -625,14 +625,15 @@ linvfs_get_blocks_direct(
STATIC int STATIC int
linvfs_direct_IO( linvfs_direct_IO(
int rw, int rw,
struct file *file, struct kiocb *iocb,
const struct iovec *iov, const struct iovec *iov,
loff_t offset, loff_t offset,
unsigned long nr_segs) unsigned long nr_segs)
{ {
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host; struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
return generic_direct_IO(rw, inode, NULL, return blockdev_direct_IO(rw, iocb, inode, NULL,
iov, offset, nr_segs, linvfs_get_blocks_direct); iov, offset, nr_segs, linvfs_get_blocks_direct);
} }
......
...@@ -276,6 +276,7 @@ struct iattr { ...@@ -276,6 +276,7 @@ struct iattr {
struct page; struct page;
struct address_space; struct address_space;
struct writeback_control; struct writeback_control;
struct kiocb;
struct address_space_operations { struct address_space_operations {
int (*writepage)(struct page *page, struct writeback_control *wbc); int (*writepage)(struct page *page, struct writeback_control *wbc);
...@@ -301,7 +302,7 @@ struct address_space_operations { ...@@ -301,7 +302,7 @@ struct address_space_operations {
sector_t (*bmap)(struct address_space *, sector_t); sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long); int (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, int); int (*releasepage) (struct page *, int);
int (*direct_IO)(int, struct file *, const struct iovec *iov, int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
loff_t offset, unsigned long nr_segs); loff_t offset, unsigned long nr_segs);
}; };
...@@ -736,7 +737,6 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u ...@@ -736,7 +737,6 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u
* read, write, poll, fsync, readv, writev can be called * read, write, poll, fsync, readv, writev can be called
* without the big kernel lock held in all filesystems. * without the big kernel lock held in all filesystems.
*/ */
struct kiocb;
struct file_operations { struct file_operations {
struct module *owner; struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int); loff_t (*llseek) (struct file *, loff_t, int);
...@@ -1234,6 +1234,8 @@ extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); ...@@ -1234,6 +1234,8 @@ extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *); extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
extern ssize_t generic_file_aio_read(struct kiocb *, char *, size_t, loff_t); extern ssize_t generic_file_aio_read(struct kiocb *, char *, size_t, loff_t);
extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t); extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t);
extern ssize_t generic_file_aio_write_nolock(struct kiocb *, const struct iovec *,
unsigned long, loff_t *);
extern ssize_t do_sync_read(struct file *filp, char *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_read(struct file *filp, char *buf, size_t len, loff_t *ppos);
extern ssize_t do_sync_write(struct file *filp, const char *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char *buf, size_t len, loff_t *ppos);
ssize_t generic_file_write_nolock(struct file *file, const struct iovec *iov, ssize_t generic_file_write_nolock(struct file *file, const struct iovec *iov,
...@@ -1243,10 +1245,11 @@ extern void do_generic_mapping_read(struct address_space *, struct file_ra_state ...@@ -1243,10 +1245,11 @@ extern void do_generic_mapping_read(struct address_space *, struct file_ra_state
loff_t *, read_descriptor_t *, read_actor_t); loff_t *, read_descriptor_t *, read_actor_t);
extern void extern void
file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping);
extern ssize_t generic_file_direct_IO(int rw, struct file *file, extern ssize_t generic_file_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset, unsigned long nr_segs); const struct iovec *iov, loff_t offset, unsigned long nr_segs);
extern int generic_direct_IO(int rw, struct inode *inode, struct block_device *bdev, extern int blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_blocks_t *get_blocks); struct block_device *bdev, const struct iovec *iov, loff_t offset,
unsigned long nr_segs, get_blocks_t *get_blocks);
extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov, extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos); unsigned long nr_segs, loff_t *ppos);
ssize_t generic_file_writev(struct file *filp, const struct iovec *iov, ssize_t generic_file_writev(struct file *filp, const struct iovec *iov,
......
...@@ -215,7 +215,7 @@ EXPORT_SYMBOL(ll_rw_block); ...@@ -215,7 +215,7 @@ EXPORT_SYMBOL(ll_rw_block);
EXPORT_SYMBOL(submit_bh); EXPORT_SYMBOL(submit_bh);
EXPORT_SYMBOL(unlock_buffer); EXPORT_SYMBOL(unlock_buffer);
EXPORT_SYMBOL(__wait_on_buffer); EXPORT_SYMBOL(__wait_on_buffer);
EXPORT_SYMBOL(generic_direct_IO); EXPORT_SYMBOL(blockdev_direct_IO);
EXPORT_SYMBOL(block_write_full_page); EXPORT_SYMBOL(block_write_full_page);
EXPORT_SYMBOL(block_read_full_page); EXPORT_SYMBOL(block_read_full_page);
EXPORT_SYMBOL(block_prepare_write); EXPORT_SYMBOL(block_prepare_write);
......
...@@ -805,7 +805,7 @@ __generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -805,7 +805,7 @@ __generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
nr_segs = iov_shorten((struct iovec *)iov, nr_segs = iov_shorten((struct iovec *)iov,
nr_segs, count); nr_segs, count);
} }
retval = generic_file_direct_IO(READ, filp, retval = generic_file_direct_IO(READ, iocb,
iov, pos, nr_segs); iov, pos, nr_segs);
if (retval > 0) if (retval > 0)
*ppos = pos + retval; *ppos = pos + retval;
...@@ -1532,9 +1532,10 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) ...@@ -1532,9 +1532,10 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes)
* okir@monad.swb.de * okir@monad.swb.de
*/ */
ssize_t ssize_t
generic_file_write_nolock(struct file *file, const struct iovec *iov, generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos) unsigned long nr_segs, loff_t *ppos)
{ {
struct file *file = iocb->ki_filp;
struct address_space * mapping = file->f_dentry->d_inode->i_mapping; struct address_space * mapping = file->f_dentry->d_inode->i_mapping;
struct address_space_operations *a_ops = mapping->a_ops; struct address_space_operations *a_ops = mapping->a_ops;
size_t ocount; /* original count */ size_t ocount; /* original count */
...@@ -1674,7 +1675,7 @@ generic_file_write_nolock(struct file *file, const struct iovec *iov, ...@@ -1674,7 +1675,7 @@ generic_file_write_nolock(struct file *file, const struct iovec *iov,
if (count != ocount) if (count != ocount)
nr_segs = iov_shorten((struct iovec *)iov, nr_segs = iov_shorten((struct iovec *)iov,
nr_segs, count); nr_segs, count);
written = generic_file_direct_IO(WRITE, file, written = generic_file_direct_IO(WRITE, iocb,
iov, pos, nr_segs); iov, pos, nr_segs);
if (written > 0) { if (written > 0) {
loff_t end = pos + written; loff_t end = pos + written;
...@@ -1786,12 +1787,39 @@ generic_file_write_nolock(struct file *file, const struct iovec *iov, ...@@ -1786,12 +1787,39 @@ generic_file_write_nolock(struct file *file, const struct iovec *iov,
return err; return err;
} }
ssize_t
generic_file_write_nolock(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos)
{
struct kiocb kiocb;
ssize_t ret;
init_sync_kiocb(&kiocb, file);
ret = generic_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&kiocb);
return ret;
}
ssize_t generic_file_aio_write(struct kiocb *iocb, const char *buf, ssize_t generic_file_aio_write(struct kiocb *iocb, const char *buf,
size_t count, loff_t pos) size_t count, loff_t pos)
{ {
return generic_file_write(iocb->ki_filp, buf, count, &iocb->ki_pos); struct file *file = iocb->ki_filp;
struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
int err;
struct iovec local_iov = { .iov_base = (void *)buf, .iov_len = count };
BUG_ON(iocb->ki_pos != pos);
down(&inode->i_sem);
err = generic_file_aio_write_nolock(iocb, &local_iov, 1,
&iocb->ki_pos);
up(&inode->i_sem);
return err;
} }
EXPORT_SYMBOL(generic_file_aio_write); EXPORT_SYMBOL(generic_file_aio_write);
EXPORT_SYMBOL(generic_file_aio_write_nolock);
ssize_t generic_file_write(struct file *file, const char *buf, ssize_t generic_file_write(struct file *file, const char *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
...@@ -1831,3 +1859,26 @@ ssize_t generic_file_writev(struct file *file, const struct iovec *iov, ...@@ -1831,3 +1859,26 @@ ssize_t generic_file_writev(struct file *file, const struct iovec *iov,
up(&inode->i_sem); up(&inode->i_sem);
return ret; return ret;
} }
ssize_t
generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
ssize_t retval;
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = mapping->a_ops->direct_IO(rw, iocb, iov, offset, nr_segs);
if (rw == WRITE && mapping->nrpages)
invalidate_inode_pages2(mapping);
out:
return retval;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment