* [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).