fuse: O_DIRECT support for files
Subsystems (2)
PATCH v4 1/1 fuse: O_DIRECT support for files
Implement ->direct_IO() method in aops. The ->direct_IO() method combines the existing fuse_direct_read/fuse_direct_write methods to implement O_DIRECT functionality.
Reaching ->direct_IO() in the read path via generic_file_aio_read ensures proper synchronization with page cache with its existing framework.
Reaching ->direct_IO() in the write path via fuse_file_aio_write is made to come via generic_file_direct_write() which makes it play nice with the page cache w.r.t other mmap pages etc.
On files marked 'direct_io' by the filesystem server, IO always follows the fuse_direct_read/write path. There is no effect of fcntl(O_DIRECT) and it always succeeds.
On files not marked with 'direct_io' by the filesystem server, the IO path depends on O_DIRECT flag by the application. This can be passed at the time of open() as well as via fcntl().
Note that asynchronous O_DIRECT iocb jobs are completed synchronously always (this has been the case with FUSE even before this patch)
---
Test case:
- concurrent read and write DDs with oflag=direct and iflag=direct set in a few writes and a few reads
- artificially decrease 6th parameter to generic_file_direct_write to simulate a partial write of the direct IO request and verify proper buffered I/O for remaining offset and size with printk's and verify write completion at backend
fs/fuse/dir.c | 3 - fs/fuse/file.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 20 deletions(-)
fs/fuse/dir.c +0 -3
@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,if (fc->no_create)return -ENOSYS ; -if (flags &O_DIRECT ) -return -EINVAL ; - forget =fuse_alloc_forget ();if (!forget)return -ENOMEM ;
fs/fuse/file.c +107 -17
187 lines not on this page · raw message
Signature · 1 line
1.7.4.4
A message that is not in the archive (sent off-list or not yet received): 20120217134958.GA6303-WxyJVcTm+KGVOal23zz4YIGKTjYczspe@public.gmane.org
Quoted 30 lines · Anand Avati
Implement ->direct_IO() method in aops. The ->direct_IO() method combines the existing fuse_direct_read/fuse_direct_write methods to implement
O_DIRECTfunctionality.Reaching ->direct_IO() in the read path via
generic_file_aio_readensures proper synchronization with page cache with its existing framework.Reaching ->direct_IO() in the write path via
fuse_file_aio_writeis made to come viageneric_file_direct_write()which makes it play nice with the page cache w.r.t other mmap pages etc.On files marked '
direct_io' by the filesystem server, IO always follows the fuse_direct_read/write path. There is no effect offcntl(O_DIRECT)and it always succeeds.On files not marked with '
direct_io' by the filesystem server, the IO path depends onO_DIRECTflag by the application. This can be passed at the time ofopen()as well as viafcntl().Note that asynchronous
O_DIRECTiocb jobs are completed synchronously always (this has been the case with FUSE even before this patch)Signed-off-by Anand Avati <[email protected]>---
Test case:
- concurrent read and write DDs with oflag=direct and iflag=direct set in a few writes and a few reads
Quoted 9 lines
- artificially decrease 6th parameter to
generic_file_direct_writeto simulate a partial write of the direct IO request and verify proper buffered I/O for remaining offset and size with printk's and verify write completion at backendfs/fuse/dir.c | 3 - fs/fuse/file.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 20 deletions(-)
All in all it looks good, just one little nit
Quoted 76 lines
fs/fuse/dir.c +0
-3@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,if (fc->no_create)return -ENOSYS ; -if (flags &O_DIRECT ) -return -EINVAL ; - forget =fuse_alloc_forget ();if (!forget)return -ENOMEM ;fs/fuse/file.c +21
-9@@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)struct fuse_conn *fc =get_fuse_conn (inode);int err; -/* VFS checks this, but only _after_ ->open() */ -if (file->f_flags &O_DIRECT ) -return -EINVAL ; - err =generic_file_open (inode, file);if (err)return err;@@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,struct file *file = iocb->ki_filp;struct address_space *mapping = file->f_mapping;size_t count =0 ; +size_t ocount =0 ;ssize_t written =0 ; +ssize_t written_buffered =0 ;struct inode *inode = mapping->host;ssize_t err;struct iov_iter i; +loff_t endbyte =0 ;WARN_ON (iocb->ki_pos != pos); - err =generic_segment_checks (iov, &nr_segs, &count,VERIFY_READ ); + ocount =0 ; + err =generic_segment_checks (iov, &nr_segs, &ocount,VERIFY_READ );if (err)return err; + count = ocount; +mutex_lock (&inode->i_mutex);vfs_check_frozen (inode->i_sb,SB_FREEZE_WRITE );@@ -962,11 +964,36 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,file_update_time (file); -iov_iter_init (&i, iov, nr_segs, count,0 ); - written =fuse_perform_write (file, mapping, &i, pos); -if (written >=0 ) - iocb->ki_pos = pos + written; +if (file->f_flags &O_DIRECT ) { + written =generic_file_direct_write (iocb, iov, &nr_segs, + pos, &iocb->ki_pos, + count, ocount); +if (written <0 || written == count) + goto out; + + pos += written; + count -= written; + +iov_iter_init (&i, iov, nr_segs, count, written); + written_buffered =fuse_perform_write (file, mapping, &i, pos); +if (written_buffered <0 ) { + err = written_buffered;
fs/fuse/file.c static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, · line 990
Quoted 10 lines
fs/fuse/file.c +9
-0+ goto out; + } + endbyte = pos + written_buffered -1 ; + err =filemap_write_and_wait_range (file->f_mapping, pos, + endbyte); +if (err) + goto out; + written += written_buffered; + iocb->ki_pos = pos + written_buffered;
You need to call invalidate_mapping_pages here to evict the pages from pagecache, basically copy what __generic_file_aio_write does and you are good to go. Thanks,
Josef
------------------------------------------------------------------------------
Virtualization & Cloud Management Using Capacity Planning Cloud computing makes use of virtualization - but cloud computing also focuses on allowing computing to be delivered as a service.
http://www.accelacomm.com/jaw/sfnl/114/51521223/
A message that is not in the archive (sent off-list or not yet received): 20120217160206.GA1856-bi+AKbBUZKY6gyzm1THtWbp2dZbC/[email protected]
Quoted 123 lines · Josef Bacik
Quoted 30 lines · Anand Avati
Implement ->direct_IO() method in aops. The ->direct_IO() method combines the existing fuse_direct_read/fuse_direct_write methods to implement
O_DIRECTfunctionality.Reaching ->direct_IO() in the read path via
generic_file_aio_readensures proper synchronization with page cache with its existing framework.Reaching ->direct_IO() in the write path via
fuse_file_aio_writeis made to come viageneric_file_direct_write()which makes it play nice with the page cache w.r.t other mmap pages etc.On files marked '
direct_io' by the filesystem server, IO always follows the fuse_direct_read/write path. There is no effect offcntl(O_DIRECT)and it always succeeds.On files not marked with '
direct_io' by the filesystem server, the IO path depends onO_DIRECTflag by the application. This can be passed at the time ofopen()as well as viafcntl().Note that asynchronous
O_DIRECTiocb jobs are completed synchronously always (this has been the case with FUSE even before this patch)Signed-off-by Anand Avati<[email protected]>---
Test case:
- concurrent read and write DDs with oflag=direct and iflag=direct set in a few writes and a few reads
Quoted 9 lines
- artificially decrease 6th parameter to
generic_file_direct_writeto simulate a partial write of the direct IO request and verify proper buffered I/O for remaining offset and size with printk's and verify write completion at backendfs/fuse/dir.c | 3 - fs/fuse/file.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 20 deletions(-)All in all it looks good, just one little nit
Quoted 80 lines
fs/fuse/dir.c +0
-3@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,if (fc->no_create)return -ENOSYS ; -if (flags&O_DIRECT ) -return -EINVAL ; - forget =fuse_alloc_forget ();if (!forget)return -ENOMEM ;fs/fuse/file.c +24
-9@@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)struct fuse_conn *fc =get_fuse_conn (inode);int err; -/* VFS checks this, but only _after_ ->open() */ -if (file->f_flags&O_DIRECT ) -return -EINVAL ; - err =generic_file_open (inode, file);if (err)return err;@@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,struct file *file = iocb->ki_filp;struct address_space *mapping = file->f_mapping;size_t count =0 ; +size_t ocount =0 ;ssize_t written =0 ; +ssize_t written_buffered =0 ;struct inode *inode = mapping->host;ssize_t err;struct iov_iter i; +loff_t endbyte =0 ;WARN_ON (iocb->ki_pos != pos); - err =generic_segment_checks (iov,&nr_segs,&count,VERIFY_READ ); + ocount =0 ; + err =generic_segment_checks (iov,&nr_segs,&ocount,VERIFY_READ );if (err)return err; + count = ocount; +mutex_lock (&inode->i_mutex);vfs_check_frozen (inode->i_sb,SB_FREEZE_WRITE );@@ -962,11 +964,36 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,file_update_time (file); -iov_iter_init (&i, iov, nr_segs, count,0 ); - written =fuse_perform_write (file, mapping,&i, pos); -if (written>=0 ) - iocb->ki_pos = pos + written; +if (file->f_flags&O_DIRECT ) { + written =generic_file_direct_write (iocb, iov,&nr_segs, + pos,&iocb->ki_pos, + count, ocount); +if (written<0 || written == count) + goto out; + + pos += written; + count -= written; + +iov_iter_init (&i, iov, nr_segs, count, written); + written_buffered =fuse_perform_write (file, mapping,&i, pos); +if (written_buffered<0 ) { + err = written_buffered; + goto out; + } + endbyte = pos + written_buffered -1 ;
Quoted 10 lines
fs/fuse/file.c static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, · line 990
Quoted 6 lines
fs/fuse/file.c +6
-0+ err =filemap_write_and_wait_range (file->f_mapping, pos, + endbyte); +if (err) + goto out; + written += written_buffered; + iocb->ki_pos = pos + written_buffered;You need to call
invalidate_mapping_pageshere to evict the pages from pagecache, basically copy what__generic_file_aio_writedoes and you are good to go. Thanks,
I have a more fundamental question. Does FUSE require such a "buffered fallback" path at all? Both branches (generic_file_direct_write on a fuse file and fuse_perform_write) finally end up in a fuse_send_write(). What is the reason behind expecting fuse_send_write() to fail via fuse_direct_IO() branch but succeed via fuse_perform_write()?
Avati
------------------------------------------------------------------------------
Virtualization & Cloud Management Using Capacity Planning Cloud computing makes use of virtualization - but cloud computing also focuses on allowing computing to be delivered as a service.
http://www.accelacomm.com/jaw/sfnl/114/51521223/
Quoted 135 lines · Anand Avati
Quoted 123 lines · Josef Bacik
Quoted 30 lines · Anand Avati
Implement ->direct_IO() method in aops. The ->direct_IO() method combines the existing fuse_direct_read/fuse_direct_write methods to implement
O_DIRECTfunctionality.Reaching ->direct_IO() in the read path via
generic_file_aio_readensures proper synchronization with page cache with its existing framework.Reaching ->direct_IO() in the write path via
fuse_file_aio_writeis made to come viageneric_file_direct_write()which makes it play nice with the page cache w.r.t other mmap pages etc.On files marked '
direct_io' by the filesystem server, IO always follows the fuse_direct_read/write path. There is no effect offcntl(O_DIRECT)and it always succeeds.On files not marked with '
direct_io' by the filesystem server, the IO path depends onO_DIRECTflag by the application. This can be passed at the time ofopen()as well as viafcntl().Note that asynchronous
O_DIRECTiocb jobs are completed synchronously always (this has been the case with FUSE even before this patch)Signed-off-by Anand Avati<[email protected]>---
Test case:
- concurrent read and write DDs with oflag=direct and iflag=direct set in a few writes and a few reads
Quoted 9 lines
- artificially decrease 6th parameter to
generic_file_direct_writeto simulate a partial write of the direct IO request and verify proper buffered I/O for remaining offset and size with printk's and verify write completion at backendfs/fuse/dir.c | 3 - fs/fuse/file.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 20 deletions(-)All in all it looks good, just one little nit
Quoted 80 lines
fs/fuse/dir.c +0
-3@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,if (fc->no_create)return -ENOSYS ; -if (flags&O_DIRECT ) -return -EINVAL ; - forget =fuse_alloc_forget ();if (!forget)return -ENOMEM ;fs/fuse/file.c +24
-9@@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)struct fuse_conn *fc =get_fuse_conn (inode);int err; -/* VFS checks this, but only _after_ ->open() */ -if (file->f_flags&O_DIRECT ) -return -EINVAL ; - err =generic_file_open (inode, file);if (err)return err;@@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,struct file *file = iocb->ki_filp;struct address_space *mapping = file->f_mapping;size_t count =0 ; +size_t ocount =0 ;ssize_t written =0 ; +ssize_t written_buffered =0 ;struct inode *inode = mapping->host;ssize_t err;struct iov_iter i; +loff_t endbyte =0 ;WARN_ON (iocb->ki_pos != pos); - err =generic_segment_checks (iov,&nr_segs,&count,VERIFY_READ ); + ocount =0 ; + err =generic_segment_checks (iov,&nr_segs,&ocount,VERIFY_READ );if (err)return err; + count = ocount; +mutex_lock (&inode->i_mutex);vfs_check_frozen (inode->i_sb,SB_FREEZE_WRITE );@@ -962,11 +964,36 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,file_update_time (file); -iov_iter_init (&i, iov, nr_segs, count,0 ); - written =fuse_perform_write (file, mapping,&i, pos); -if (written>=0 ) - iocb->ki_pos = pos + written; +if (file->f_flags&O_DIRECT ) { + written =generic_file_direct_write (iocb, iov,&nr_segs, + pos,&iocb->ki_pos, + count, ocount); +if (written<0 || written == count) + goto out; + + pos += written; + count -= written; + +iov_iter_init (&i, iov, nr_segs, count, written); + written_buffered =fuse_perform_write (file, mapping,&i, pos); +if (written_buffered<0 ) { + err = written_buffered; + goto out; + } + endbyte = pos + written_buffered -1 ;Quoted 10 lines
fs/fuse/file.c static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, · line 990
Quoted 6 lines
fs/fuse/file.c +6
-0+ err =filemap_write_and_wait_range (file->f_mapping, pos, + endbyte); +if (err) + goto out; + written += written_buffered; + iocb->ki_pos = pos + written_buffered;You need to call
invalidate_mapping_pageshere to evict the pages from pagecache, basically copy what__generic_file_aio_writedoes and you are good to go. Thanks,
Quoted 6 lines
I have a more fundamental question. Does FUSE require such a "buffered fallback" path at all? Both branches (
generic_file_direct_writeon a fuse file andfuse_perform_write) finally end up in afuse_send_write(). What is the reason behind expectingfuse_send_write()to fail viafuse_direct_IO()branch but succeed viafuse_perform_write()?
Ahh good point helps if I pay attention to what you are doing. You're right if it fails in the direct write I doubt trying it again with buffered writing is going to get you anywhere, I had it in my mind you were still using the generic dio infrastructure which can fail for a variety of reasons but you aren't so you could probably get away with just failing if generic_file_direct_write fails. Thanks,
Josef