linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Improve btrfs llseek implementation
@ 2019-09-26 11:39 Nikolay Borisov
  2019-09-26 11:39 ` [PATCH 1/3] btrfs: Speed up btrfs_file_llseek Nikolay Borisov
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Nikolay Borisov @ 2019-09-26 11:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Nikolay Borisov

This series aims to speed up and refactor btrfs_file_llseek. This is realized 
thanks to the fact there is no need to hold the inode locked when modifying the 
cursors of an opened file descriptor.

Patch 1 implements that and it results in around ~85% performance improvement
on a synthethic, fseek-heavy workload. Details on why this is safe are in the
patch's changelog alongside benchmarking information. 

Patch 2 streamlines btrfs_file_llseek and makes it similar to its counterparts
in ext4/xfs. Main change is that the 'out' label in the function is removed 
and the SEEK_END/SEEK_CUR are handled in the 'default' case. 

Patch 3 refactors find_desired_extent with the main goal of returning the found
offset as a function return value rather than into a pointer parameter.

This series survived xfstest.

Nikolay Borisov (3):
  btrfs: Speed up btrfs_file_llseek
  btrfs: Simplify btrfs_file_llseek
  btrfs: Return offset from find_desired_extent

 fs/btrfs/file.c | 58 ++++++++++++++++++++++---------------------------
 1 file changed, 26 insertions(+), 32 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/3] btrfs: Speed up btrfs_file_llseek
  2019-09-26 11:39 [PATCH 0/3] Improve btrfs llseek implementation Nikolay Borisov
@ 2019-09-26 11:39 ` Nikolay Borisov
  2019-09-26 18:27   ` Josef Bacik
  2019-09-26 11:39 ` [PATCH 2/3] btrfs: Simplify btrfs_file_llseek Nikolay Borisov
  2019-09-26 11:39 ` [PATCH 3/3] btrfs: Return offset from find_desired_extent Nikolay Borisov
  2 siblings, 1 reply; 9+ messages in thread
From: Nikolay Borisov @ 2019-09-26 11:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Nikolay Borisov

Modifying the file position is done on a per-file basis. This renders
holding the inode lock useless and currently holding it makes the
performance of concurrent llseek's abysmal.

Fix this by removing calls to inode_lock. This is correct because
find_desired_extent already includes proper extent locking for the
range it's going to interrogate for finding a DATA/HOLE region. The
other two cases SEEK_END/SEEK_CUR are also safe. The latter is
synchronized by file::f_lock spinlock. SEEK_END is not synchronized
but atomic, but that's OK since there is not guarantee that SEEK_END
will always be at the end of the file in the face of tail modifications. For 
more information on locking requirements see ef3d0fd27e90 ("vfs: do (nearly)
lockless generic_file_llseek")


This change brings ~85% performance improvement when doing a lot of
parallel fseeks. The workload essentially does: 

                    for (d=0; d<num_seek_read; d++)
                      {
                        /* offset %= 16777216; */
                        fseek (f, 256 * d % 16777216, SEEK_SET);
                        fread (buffer, 64, 1, f);
                      }

Without patch:

num workprocesses = 16
num fseek/fread = 8000000
step = 256
fork 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

real	0m41.412s
user	0m28.777s
sys	2m16.510s

With patch:

num workprocesses = 16
num fseek/fread = 8000000
step = 256
fork 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

real	0m11.479s
user	0m27.629s
sys	0m18.187s

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/file.c | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 12688ae6e6f2..31111a94251a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3347,13 +3347,14 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
 	struct extent_map *em = NULL;
 	struct extent_state *cached_state = NULL;
+	loff_t i_size = i_size_read(inode);
 	u64 lockstart;
 	u64 lockend;
 	u64 start;
 	u64 len;
 	int ret = 0;
 
-	if (inode->i_size == 0)
+	if (i_size == 0)
 		return -ENXIO;
 
 	/*
@@ -3363,8 +3364,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 	start = max_t(loff_t, 0, *offset);
 
 	lockstart = round_down(start, fs_info->sectorsize);
-	lockend = round_up(i_size_read(inode),
-			   fs_info->sectorsize);
+	lockend = round_up(i_size, fs_info->sectorsize);
 	if (lockend <= lockstart)
 		lockend = lockstart + fs_info->sectorsize;
 	lockend--;
@@ -3373,7 +3373,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 	lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend,
 			 &cached_state);
 
-	while (start < inode->i_size) {
+	while (start < i_size) {
 		em = btrfs_get_extent_fiemap(BTRFS_I(inode), start, len);
 		if (IS_ERR(em)) {
 			ret = PTR_ERR(em);
@@ -3397,10 +3397,10 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 	}
 	free_extent_map(em);
 	if (!ret) {
-		if (whence == SEEK_DATA && start >= inode->i_size)
+		if (whence == SEEK_DATA && start >= i_size)
 			ret = -ENXIO;
 		else
-			*offset = min_t(loff_t, start, inode->i_size);
+			*offset = min_t(loff_t, start, i_size);
 	}
 	unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
 			     &cached_state);
@@ -3412,7 +3412,6 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 	struct inode *inode = file->f_mapping->host;
 	int ret;
 
-	inode_lock(inode);
 	switch (whence) {
 	case SEEK_END:
 	case SEEK_CUR:
@@ -3420,21 +3419,16 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 		goto out;
 	case SEEK_DATA:
 	case SEEK_HOLE:
-		if (offset >= i_size_read(inode)) {
-			inode_unlock(inode);
+		if (offset >= i_size_read(inode))
 			return -ENXIO;
-		}
 
 		ret = find_desired_extent(inode, &offset, whence);
-		if (ret) {
-			inode_unlock(inode);
+		if (ret)
 			return ret;
-		}
 	}
 
 	offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
 out:
-	inode_unlock(inode);
 	return offset;
 }
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/3] btrfs: Simplify btrfs_file_llseek
  2019-09-26 11:39 [PATCH 0/3] Improve btrfs llseek implementation Nikolay Borisov
  2019-09-26 11:39 ` [PATCH 1/3] btrfs: Speed up btrfs_file_llseek Nikolay Borisov
@ 2019-09-26 11:39 ` Nikolay Borisov
  2019-09-26 12:02   ` Johannes Thumshirn
  2019-09-26 18:27   ` Josef Bacik
  2019-09-26 11:39 ` [PATCH 3/3] btrfs: Return offset from find_desired_extent Nikolay Borisov
  2 siblings, 2 replies; 9+ messages in thread
From: Nikolay Borisov @ 2019-09-26 11:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Nikolay Borisov

Handle SEEK_END/SEEK_CUR in a single 'default' case by directly
returning from generic_file_llsee. This makes the 'out' label redundant.
Finally return directly the vale from vfs_setpos. No semantic changesl.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/file.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 31111a94251a..8845403287ed 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3413,10 +3413,8 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 	int ret;
 
 	switch (whence) {
-	case SEEK_END:
-	case SEEK_CUR:
-		offset = generic_file_llseek(file, offset, whence);
-		goto out;
+	default:
+		return generic_file_llseek(file, offset, whence);
 	case SEEK_DATA:
 	case SEEK_HOLE:
 		if (offset >= i_size_read(inode))
@@ -3427,9 +3425,7 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 			return ret;
 	}
 
-	offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
-out:
-	return offset;
+	return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
 }
 
 static int btrfs_file_open(struct inode *inode, struct file *filp)
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] btrfs: Return offset from find_desired_extent
  2019-09-26 11:39 [PATCH 0/3] Improve btrfs llseek implementation Nikolay Borisov
  2019-09-26 11:39 ` [PATCH 1/3] btrfs: Speed up btrfs_file_llseek Nikolay Borisov
  2019-09-26 11:39 ` [PATCH 2/3] btrfs: Simplify btrfs_file_llseek Nikolay Borisov
@ 2019-09-26 11:39 ` Nikolay Borisov
  2019-09-26 12:04   ` Johannes Thumshirn
  2019-09-26 18:28   ` Josef Bacik
  2 siblings, 2 replies; 9+ messages in thread
From: Nikolay Borisov @ 2019-09-26 11:39 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Nikolay Borisov

Instead of using an input pointer parameter as the return value and have
an int as the return type of find_desired_extent, rework the function
to directly return the found offset. Doing that the 'ret' variable in
btrfs_llseek_file can be removed. Additional (subjective) benefit is
that btrfs' llseek function now resemebles those of the other major
filesystems.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/file.c | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 8845403287ed..94c02394c178 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3342,7 +3342,8 @@ static long btrfs_fallocate(struct file *file, int mode,
 	return ret;
 }
 
-static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
+static loff_t find_desired_extent(struct inode *inode, loff_t offset,
+				  int whence)
 {
 	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
 	struct extent_map *em = NULL;
@@ -3358,10 +3359,10 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 		return -ENXIO;
 
 	/*
-	 * *offset can be negative, in this case we start finding DATA/HOLE from
+	 * offset can be negative, in this case we start finding DATA/HOLE from
 	 * the very start of the file.
 	 */
-	start = max_t(loff_t, 0, *offset);
+	start = max_t(loff_t, 0, offset);
 
 	lockstart = round_down(start, fs_info->sectorsize);
 	lockend = round_up(i_size, fs_info->sectorsize);
@@ -3396,21 +3397,23 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence)
 		cond_resched();
 	}
 	free_extent_map(em);
-	if (!ret) {
+	unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+			     &cached_state);
+	if (ret) {
+		offset = ret;
+	} else {
 		if (whence == SEEK_DATA && start >= i_size)
-			ret = -ENXIO;
+			offset = -ENXIO;
 		else
-			*offset = min_t(loff_t, start, i_size);
+			offset = min_t(loff_t, start, i_size);
 	}
-	unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
-			     &cached_state);
-	return ret;
+
+	return offset;
 }
 
 static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 {
 	struct inode *inode = file->f_mapping->host;
-	int ret;
 
 	switch (whence) {
 	default:
@@ -3420,11 +3423,12 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence)
 		if (offset >= i_size_read(inode))
 			return -ENXIO;
 
-		ret = find_desired_extent(inode, &offset, whence);
-		if (ret)
-			return ret;
+		offset = find_desired_extent(inode, offset, whence);
 	}
 
+	if (offset < 0)
+		return offset;
+
 	return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
 }
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] btrfs: Simplify btrfs_file_llseek
  2019-09-26 11:39 ` [PATCH 2/3] btrfs: Simplify btrfs_file_llseek Nikolay Borisov
@ 2019-09-26 12:02   ` Johannes Thumshirn
  2019-09-26 18:27   ` Josef Bacik
  1 sibling, 0 replies; 9+ messages in thread
From: Johannes Thumshirn @ 2019-09-26 12:02 UTC (permalink / raw)
  To: Nikolay Borisov, linux-btrfs

On 26/09/2019 13:39, Nikolay Borisov wrote:
> Handle SEEK_END/SEEK_CUR in a single 'default' case by directly
> returning from generic_file_llsee. This makes the 'out' label redundant.
    generic_file_llseek ~^
> Finally return directly the vale from vfs_setpos. No semantic changesl.
                                                        changes. ~^

Otherwise looks good:
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>


-- 
Johannes Thumshirn                            SUSE Labs Filesystems
jthumshirn@suse.de                                +49 911 74053 689
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5
90409 Nürnberg
Germany
(HRB 247165, AG München)
Key fingerprint = EC38 9CAB C2C4 F25D 8600 D0D0 0393 969D 2D76 0850

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] btrfs: Return offset from find_desired_extent
  2019-09-26 11:39 ` [PATCH 3/3] btrfs: Return offset from find_desired_extent Nikolay Borisov
@ 2019-09-26 12:04   ` Johannes Thumshirn
  2019-09-26 18:28   ` Josef Bacik
  1 sibling, 0 replies; 9+ messages in thread
From: Johannes Thumshirn @ 2019-09-26 12:04 UTC (permalink / raw)
  To: Nikolay Borisov, linux-btrfs

Looks good,
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
-- 
Johannes Thumshirn                            SUSE Labs Filesystems
jthumshirn@suse.de                                +49 911 74053 689
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5
90409 Nürnberg
Germany
(HRB 247165, AG München)
Key fingerprint = EC38 9CAB C2C4 F25D 8600 D0D0 0393 969D 2D76 0850

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] btrfs: Speed up btrfs_file_llseek
  2019-09-26 11:39 ` [PATCH 1/3] btrfs: Speed up btrfs_file_llseek Nikolay Borisov
@ 2019-09-26 18:27   ` Josef Bacik
  0 siblings, 0 replies; 9+ messages in thread
From: Josef Bacik @ 2019-09-26 18:27 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: linux-btrfs

On Thu, Sep 26, 2019 at 02:39:51PM +0300, Nikolay Borisov wrote:
> Modifying the file position is done on a per-file basis. This renders
> holding the inode lock useless and currently holding it makes the
> performance of concurrent llseek's abysmal.
> 
> Fix this by removing calls to inode_lock. This is correct because
> find_desired_extent already includes proper extent locking for the
> range it's going to interrogate for finding a DATA/HOLE region. The
> other two cases SEEK_END/SEEK_CUR are also safe. The latter is
> synchronized by file::f_lock spinlock. SEEK_END is not synchronized
> but atomic, but that's OK since there is not guarantee that SEEK_END
> will always be at the end of the file in the face of tail modifications. For 
> more information on locking requirements see ef3d0fd27e90 ("vfs: do (nearly)
> lockless generic_file_llseek")
> 
> 
> This change brings ~85% performance improvement when doing a lot of
> parallel fseeks. The workload essentially does: 
> 
>                     for (d=0; d<num_seek_read; d++)
>                       {
>                         /* offset %= 16777216; */
>                         fseek (f, 256 * d % 16777216, SEEK_SET);
>                         fread (buffer, 64, 1, f);
>                       }
> 
> Without patch:
> 
> num workprocesses = 16
> num fseek/fread = 8000000
> step = 256
> fork 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> 
> real	0m41.412s
> user	0m28.777s
> sys	2m16.510s
> 
> With patch:
> 
> num workprocesses = 16
> num fseek/fread = 8000000
> step = 256
> fork 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> 
> real	0m11.479s
> user	0m27.629s
> sys	0m18.187s
> 
> Signed-off-by: Nikolay Borisov <nborisov@suse.com>

Reviewed-by: Josef Bacik <josef@toxicpanda.com>

Thanks,

Josef

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] btrfs: Simplify btrfs_file_llseek
  2019-09-26 11:39 ` [PATCH 2/3] btrfs: Simplify btrfs_file_llseek Nikolay Borisov
  2019-09-26 12:02   ` Johannes Thumshirn
@ 2019-09-26 18:27   ` Josef Bacik
  1 sibling, 0 replies; 9+ messages in thread
From: Josef Bacik @ 2019-09-26 18:27 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: linux-btrfs

On Thu, Sep 26, 2019 at 02:39:52PM +0300, Nikolay Borisov wrote:
> Handle SEEK_END/SEEK_CUR in a single 'default' case by directly
> returning from generic_file_llsee. This makes the 'out' label redundant.
> Finally return directly the vale from vfs_setpos. No semantic changesl.
> 
> Signed-off-by: Nikolay Borisov <nborisov@suse.com>

Reviewed-by: Josef Bacik <josef@toxicpanda.com>

Thanks,

Josef

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] btrfs: Return offset from find_desired_extent
  2019-09-26 11:39 ` [PATCH 3/3] btrfs: Return offset from find_desired_extent Nikolay Borisov
  2019-09-26 12:04   ` Johannes Thumshirn
@ 2019-09-26 18:28   ` Josef Bacik
  1 sibling, 0 replies; 9+ messages in thread
From: Josef Bacik @ 2019-09-26 18:28 UTC (permalink / raw)
  To: Nikolay Borisov; +Cc: linux-btrfs

On Thu, Sep 26, 2019 at 02:39:53PM +0300, Nikolay Borisov wrote:
> Instead of using an input pointer parameter as the return value and have
> an int as the return type of find_desired_extent, rework the function
> to directly return the found offset. Doing that the 'ret' variable in
> btrfs_llseek_file can be removed. Additional (subjective) benefit is
> that btrfs' llseek function now resemebles those of the other major
> filesystems.
> 
> Signed-off-by: Nikolay Borisov <nborisov@suse.com>

Reviewed-by: Josef Bacik <josef@toxicpanda.com>

Thanks,

Josef

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-09-26 18:29 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-26 11:39 [PATCH 0/3] Improve btrfs llseek implementation Nikolay Borisov
2019-09-26 11:39 ` [PATCH 1/3] btrfs: Speed up btrfs_file_llseek Nikolay Borisov
2019-09-26 18:27   ` Josef Bacik
2019-09-26 11:39 ` [PATCH 2/3] btrfs: Simplify btrfs_file_llseek Nikolay Borisov
2019-09-26 12:02   ` Johannes Thumshirn
2019-09-26 18:27   ` Josef Bacik
2019-09-26 11:39 ` [PATCH 3/3] btrfs: Return offset from find_desired_extent Nikolay Borisov
2019-09-26 12:04   ` Johannes Thumshirn
2019-09-26 18:28   ` Josef Bacik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).