diff mbox series

[3/6] fuse: Allow parallel direct writes for O_DIRECT

Message ID 20230829161116.2914040-4-bschubert@ddn.com (mailing list archive)
State New, archived
Headers show
Series fuse direct write consolidation and parallel IO | expand

Commit Message

Bernd Schubert Aug. 29, 2023, 4:11 p.m. UTC
Take a shared lock in fuse_cache_write_iter. This was already
done for FOPEN_DIRECT_IO in

commit 153524053bbb ("fuse: allow non-extending parallel direct
writes on the same file")

but so far missing for plain O_DIRECT. Server side needs
to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
it supports parallel dio writes.

Cc: Hao Xu <howeyxu@tencent.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
---
 fs/fuse/file.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

Comments

Miklos Szeredi Aug. 30, 2023, 1:28 p.m. UTC | #1
On Tue, 29 Aug 2023 at 18:11, Bernd Schubert <bschubert@ddn.com> wrote:
>
> Take a shared lock in fuse_cache_write_iter. This was already
> done for FOPEN_DIRECT_IO in
>
> commit 153524053bbb ("fuse: allow non-extending parallel direct
> writes on the same file")
>
> but so far missing for plain O_DIRECT. Server side needs
> to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
> it supports parallel dio writes.

Hmm, I think file_remove_privs() needs exclusive lock (due to calling
notify_change()).   And the fallback case also.

Need to be careful with such locking changes...

Thanks,
Miklos
Bernd Schubert Aug. 30, 2023, 2:38 p.m. UTC | #2
On 8/30/23 15:28, Miklos Szeredi wrote:
> On Tue, 29 Aug 2023 at 18:11, Bernd Schubert <bschubert@ddn.com> wrote:
>>
>> Take a shared lock in fuse_cache_write_iter. This was already
>> done for FOPEN_DIRECT_IO in
>>
>> commit 153524053bbb ("fuse: allow non-extending parallel direct
>> writes on the same file")
>>
>> but so far missing for plain O_DIRECT. Server side needs
>> to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
>> it supports parallel dio writes.
> 
> Hmm, I think file_remove_privs() needs exclusive lock (due to calling
> notify_change()).   And the fallback case also.
> 
> Need to be careful with such locking changes...

Hrmm, yeah, I missed that :( Really sorry and thanks!

I guess I can fix it if by exporting dentry_needs_remove_privs. I hope 
that is acceptable. Interesting is that btrfs_direct_write seems to have 
the same issue.

btrfs_direct_write
	if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode))
		ilock_flags |= BTRFS_ILOCK_SHARED;
...
	err = btrfs_inode_lock(BTRFS_I(inode), ilock_flags);
...	
	err = btrfs_write_check(iocb, from, err);
...
			ret = file_remove_privs(file);


I think that can be fixed as well after exporting 
dentry_needs_remove_privs().


Btw, why is fuse_direct_write_iter not dropping privileges? That is 
another change I need to document...


Another issue I just see is that it needs to check file size again after 
taking the lock.


Thanks a lot for your review,
Bernd
Miklos Szeredi Aug. 30, 2023, 2:50 p.m. UTC | #3
On Wed, 30 Aug 2023 at 16:38, Bernd Schubert <aakef@fastmail.fm> wrote:
>
>
>
> On 8/30/23 15:28, Miklos Szeredi wrote:
> > On Tue, 29 Aug 2023 at 18:11, Bernd Schubert <bschubert@ddn.com> wrote:
> >>
> >> Take a shared lock in fuse_cache_write_iter. This was already
> >> done for FOPEN_DIRECT_IO in
> >>
> >> commit 153524053bbb ("fuse: allow non-extending parallel direct
> >> writes on the same file")
> >>
> >> but so far missing for plain O_DIRECT. Server side needs
> >> to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
> >> it supports parallel dio writes.
> >
> > Hmm, I think file_remove_privs() needs exclusive lock (due to calling
> > notify_change()).   And the fallback case also.
> >
> > Need to be careful with such locking changes...
>
> Hrmm, yeah, I missed that :( Really sorry and thanks!
>
> I guess I can fix it if by exporting dentry_needs_remove_privs. I hope
> that is acceptable. Interesting is that btrfs_direct_write seems to have
> the same issue.
>
> btrfs_direct_write
>         if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode))
>                 ilock_flags |= BTRFS_ILOCK_SHARED;
> ...
>         err = btrfs_inode_lock(BTRFS_I(inode), ilock_flags);
> ...
>         err = btrfs_write_check(iocb, from, err);
> ...
>                         ret = file_remove_privs(file);
>
>
> I think that can be fixed as well after exporting
> dentry_needs_remove_privs().

Interesting.  Would be worth asking the btrfs devs what they think of this.

>
>
> Btw, why is fuse_direct_write_iter not dropping privileges? That is
> another change I need to document...

Generally read and write can't fail due to lack of privileges, so that
should be okay.  But it would be more consistent to drop privs during
I/O as well.  But this is something that needs some thought as well,
because there could be non-obvious consequences.

> Another issue I just see is that it needs to check file size again after
> taking the lock.

Yes.

> Thanks a lot for your review,

Thanks for doing the actual work, that's the harder one.

Thanks,
Miklos
Hao Xu Aug. 31, 2023, 8:30 a.m. UTC | #4
On 8/30/23 00:11, Bernd Schubert wrote:
> Take a shared lock in fuse_cache_write_iter. This was already
> done for FOPEN_DIRECT_IO in
> 
> commit 153524053bbb ("fuse: allow non-extending parallel direct
> writes on the same file")
> 
> but so far missing for plain O_DIRECT. Server side needs
> to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
> it supports parallel dio writes.
> 
> Cc: Hao Xu <howeyxu@tencent.com>
> Cc: Miklos Szeredi <miklos@szeredi.hu>
> Cc: Dharmendra Singh <dsingh@ddn.com>
> Cc: linux-fsdevel@vger.kernel.org
> Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> ---
>   fs/fuse/file.c | 18 ++++++++++++++++--
>   1 file changed, 16 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 6b8b9512c336..a6b99bc80fe7 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -1314,6 +1314,10 @@ static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from
>   	struct file *file = iocb->ki_filp;
>   	struct fuse_file *ff = file->private_data;
>   
> +	/* this function is about direct IO only */
> +	if (!(iocb->ki_flags & IOCB_DIRECT))
> +		return false;

This means for buffered write in fuse_cache_write_iter(), we grab shared 
lock, looks not right.

> +
>   	/* server side has to advise that it supports parallel dio writes */
>   	if (!(ff->open_flags & FOPEN_PARALLEL_DIRECT_WRITES))
>   		return false;
> @@ -1337,6 +1341,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>   	struct inode *inode = mapping->host;
>   	ssize_t err;
>   	struct fuse_conn *fc = get_fuse_conn(inode);
> +	bool excl_lock = fuse_dio_wr_exclusive_lock(iocb, from);
>   
>   	if (fc->writeback_cache && !(iocb->ki_flags & IOCB_DIRECT)) {
>   		/* Update size (EOF optimization) and mode (SUID clearing) */
> @@ -1355,7 +1360,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>   	}
>   
>   writethrough:
> -	inode_lock(inode);
> +	if (excl_lock)
> +		inode_lock(inode);
> +	else
> +		inode_lock_shared(inode);
>   
>   	err = generic_write_checks(iocb, from);
>   	if (err <= 0)
> @@ -1370,6 +1378,9 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>   		goto out;
>   
>   	if (iocb->ki_flags & IOCB_DIRECT) {
> +		/* file extending writes will trigger i_size_write - exclusive
> +		 * lock is needed
> +		 */
>   		written = generic_file_direct_write(iocb, from);
>   		if (written < 0 || !iov_iter_count(from))
>   			goto out;
> @@ -1379,7 +1390,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>   		written = fuse_perform_write(iocb, from);
>   	}
>   out:
> -	inode_unlock(inode);
> +	if (excl_lock)
> +		inode_unlock(inode);
> +	else
> +		inode_unlock_shared(inode);
>   	if (written > 0)
>   		written = generic_write_sync(iocb, written);
>
Bernd Schubert Aug. 31, 2023, 8:33 a.m. UTC | #5
On 8/31/23 10:30, Hao Xu wrote:
> On 8/30/23 00:11, Bernd Schubert wrote:
>> Take a shared lock in fuse_cache_write_iter. This was already
>> done for FOPEN_DIRECT_IO in
>>
>> commit 153524053bbb ("fuse: allow non-extending parallel direct
>> writes on the same file")
>>
>> but so far missing for plain O_DIRECT. Server side needs
>> to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that
>> it supports parallel dio writes.
>>
>> Cc: Hao Xu <howeyxu@tencent.com>
>> Cc: Miklos Szeredi <miklos@szeredi.hu>
>> Cc: Dharmendra Singh <dsingh@ddn.com>
>> Cc: linux-fsdevel@vger.kernel.org
>> Signed-off-by: Bernd Schubert <bschubert@ddn.com>
>> ---
>>   fs/fuse/file.c | 18 ++++++++++++++++--
>>   1 file changed, 16 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
>> index 6b8b9512c336..a6b99bc80fe7 100644
>> --- a/fs/fuse/file.c
>> +++ b/fs/fuse/file.c
>> @@ -1314,6 +1314,10 @@ static bool fuse_dio_wr_exclusive_lock(struct 
>> kiocb *iocb, struct iov_iter *from
>>       struct file *file = iocb->ki_filp;
>>       struct fuse_file *ff = file->private_data;
>> +    /* this function is about direct IO only */
>> +    if (!(iocb->ki_flags & IOCB_DIRECT))
>> +        return false;
> 
> This means for buffered write in fuse_cache_write_iter(), we grab shared 
> lock, looks not right.
> 

Yeah, sorry, I made all values the other way around, consistently. 
Miklos had already noticed.


Thanks,
Bernd
diff mbox series

Patch

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 6b8b9512c336..a6b99bc80fe7 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1314,6 +1314,10 @@  static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from
 	struct file *file = iocb->ki_filp;
 	struct fuse_file *ff = file->private_data;
 
+	/* this function is about direct IO only */
+	if (!(iocb->ki_flags & IOCB_DIRECT))
+		return false;
+
 	/* server side has to advise that it supports parallel dio writes */
 	if (!(ff->open_flags & FOPEN_PARALLEL_DIRECT_WRITES))
 		return false;
@@ -1337,6 +1341,7 @@  static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	struct inode *inode = mapping->host;
 	ssize_t err;
 	struct fuse_conn *fc = get_fuse_conn(inode);
+	bool excl_lock = fuse_dio_wr_exclusive_lock(iocb, from);
 
 	if (fc->writeback_cache && !(iocb->ki_flags & IOCB_DIRECT)) {
 		/* Update size (EOF optimization) and mode (SUID clearing) */
@@ -1355,7 +1360,10 @@  static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	}
 
 writethrough:
-	inode_lock(inode);
+	if (excl_lock)
+		inode_lock(inode);
+	else
+		inode_lock_shared(inode);
 
 	err = generic_write_checks(iocb, from);
 	if (err <= 0)
@@ -1370,6 +1378,9 @@  static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
 		goto out;
 
 	if (iocb->ki_flags & IOCB_DIRECT) {
+		/* file extending writes will trigger i_size_write - exclusive
+		 * lock is needed
+		 */
 		written = generic_file_direct_write(iocb, from);
 		if (written < 0 || !iov_iter_count(from))
 			goto out;
@@ -1379,7 +1390,10 @@  static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
 		written = fuse_perform_write(iocb, from);
 	}
 out:
-	inode_unlock(inode);
+	if (excl_lock)
+		inode_unlock(inode);
+	else
+		inode_unlock_shared(inode);
 	if (written > 0)
 		written = generic_write_sync(iocb, written);