fuse: add max_pages option
Subsystems (3)
PATCH v4 1/1 fuse: add max_pages option
Replace FUSE_MAX_PAGES_PER_REQ with the configurable mount parameter max_pages to improve performance.
Old RFC with detailed description of the problem and many fixes by Mitsuo Hayasaka ([email protected]):
- https://lkml.org/lkml/2012/7/5/136
Implementation as a mount option selected as similar to implementation of max_read and blksize. More complex implementation can use INIT message.
Performance degradation and restoration.
Fuse introduces significant performance degradation under conditions:
- block storage is very fast.
- CPU is slow or busy.
- User space fuse adds lag on each request.
Parameter max_pages helps to restore performance in this case.
We've encountered performance degradation and fixed it on a big and complex virtual environment.
Environment to reproduce degradation and improvement:
1. Add lag to user mode FUSE
Add nanosleep(&(struct timespec){ 0, 1000 }, NULL); to xmp_write_buf in passthrough_fh.c
2. patch UM fuse with configurable max_pages parameter. The patch will be provided latter.
3. run test script and perform test on tmpfsfuse_test()
{
cd /tmp
mkdir -p fusemnt
passthrough_fh -o max_pages=$1 /tmp/fusemnt
grep fuse /proc/self/mounts
dd conv=fdatasync oflag=dsync if=/dev/zero of=fusemnt/tmp/tmp \
count=1K bs=1M 2>&1 | grep -v records
rm fusemnt/tmp/tmp
killall passthrough_fh
}
Test results:
passthrough_fh /tmp/fusemnt fuse.passthrough_fh \ rw,nosuid,nodev,relatime,user_id=0,group_id=0 0 0
1073741824 bytes (1.1 GB) copied, 1.73867 s, 618 MB/s
passthrough_fh /tmp/fusemnt fuse.passthrough_fh \ rw,nosuid,nodev,relatime,user_id=0,group_id=0,max_pages=256 0 0
1073741824 bytes (1.1 GB) copied, 1.15643 s, 928 MB/s
Obviously with bigger lag the difference between 'before' and 'after' will be more significant.
Mitsuo Hayasaka, in 2012 (https://lkml.org/lkml/2012/7/5/136),
observed improvement from 400-550 to 520-740.
---
Hi Miklos,
Above is information that you requested.
Thanks,
Costa.
Changes in v4:
- join three patches together
- add notes about mount option and performance notes
Changes in v3:
- used clamp_val
- split documentation change
- split EXPORT_SYMBOL(pipe_max_size)
Changes in v2:
- add limitation by pipe_max_size, which was requested in
https://lkml.org/lkml/2012/7/12/32
Changes in v1: https://lkml.org/lkml/2017/8/6/194
- replace FUSE_MAX_PAGES_PER_REQ with
FUSE_DEFAULT_MAX_PAGES_PER_REQ and
fc->max_pages
- add mount option max_pages
--- Documentation/filesystems/fuse.txt | 5 ++- fs/fuse/dev.c | 4 +-- fs/fuse/file.c | 54 +++++++++++++++--------------- fs/fuse/fuse_i.h | 5 ++- fs/fuse/inode.c | 14 ++++++++ fs/pipe.c | 1 + 6 files changed, 52 insertions(+), 31 deletions(-)
Documentation/filesystems/fuse.txt +4 -1
@@ -108,7 +108,10 @@ Mount options With this option the maximum size of read operations can be set. The default is infinite. Note that the size of read requests is - limited anyway to 32 pages (which is 128kbyte on i386). + limited anyway to max_pages (which by default is 32 or 128KB on x86). + +'max_pages=N' + Maximal number of pages per request. The default is 32 or 128KB on x86. 'blksize=N'
fs/fuse/dev.c +2 -2
@@ -1663,7 +1663,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,unsigned int num;unsigned int offset;size_t total_len =0 ; -int num_pages; +unsigned num_pages; offset = outarg->offset & ~PAGE_MASK ; file_size =i_size_read (inode);
@@ -1675,7 +1675,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, num = file_size - outarg->offset; num_pages = (num + offset +PAGE_SIZE -1 ) >>PAGE_SHIFT ; - num_pages =min (num_pages,FUSE_MAX_PAGES_PER_REQ ); + num_pages =min (num_pages, fc->max_pages); req =fuse_get_req (fc, num_pages);if (IS_ERR (req))
fs/fuse/file.c +27 -27
171 lines not on this page · raw message
fs/fuse/fuse_i.h +4 -1
@@ -29,7 +29,7 @@#include <linux/user_namespace.h> /** Max number of pages that can be used in a single read request */ -#define FUSE_MAX_PAGES_PER_REQ 32 +#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32/** Bias for fi->writectr, meaning new writepages must not be sent */ #define FUSE_NOWRITE INT_MIN
@@ -476,6 +476,9 @@ struct fuse_conn {/** Maximum write size */ unsigned max_write; +/** Maxmum number of pages that can be used in a single request */ +unsigned max_pages; +/** Input queue */ struct fuse_iqueue iq;
fs/fuse/inode.c +14 -0
@@ -22,6 +22,7 @@#include <linux/exportfs.h> #include <linux/posix_acl.h> #include <linux/pid_namespace.h> +#include <linux/pipe_fs_i.h> MODULE_AUTHOR ("Miklos Szeredi <[email protected]>" );MODULE_DESCRIPTION ("Filesystem in Userspace" );
@@ -71,6 +72,7 @@ struct fuse_mount_data { unsigned default_permissions:1; unsigned allow_other:1;unsigned max_read; +unsigned max_pages;unsigned blksize; };
@@ -453,6 +455,7 @@ enum {OPT_DEFAULT_PERMISSIONS ,OPT_ALLOW_OTHER ,OPT_MAX_READ , +OPT_MAX_PAGES ,OPT_BLKSIZE , OPT_ERR };
@@ -465,6 +468,7 @@ static const match_table_t tokens = { {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_ALLOW_OTHER, "allow_other"}, {OPT_MAX_READ, "max_read=%u"}, + {OPT_MAX_PAGES, "max_pages=%u"}, {OPT_BLKSIZE, "blksize=%u"}, {OPT_ERR, NULL} };
@@ -546,6 +550,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev, d->max_read = value;break ; +case OPT_MAX_PAGES : +if (match_int (&args[0 ], &value)) +return 0 ; + d->max_pages = value; +break ; +case OPT_BLKSIZE :if (!is_bdev ||match_int (&args[0 ], &value))return 0 ;
@@ -577,6 +587,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)seq_puts (m,",allow_other" );if (fc->max_read != ~0 )seq_printf (m,",max_read=%u" , fc->max_read); +if (fc->max_pages !=FUSE_DEFAULT_MAX_PAGES_PER_REQ ) +seq_printf (m,",max_pages=%u" , fc->max_pages);if (sb->s_bdev && sb->s_blocksize !=FUSE_DEFAULT_BLKSIZE )seq_printf (m,",blksize=%lu" , sb->s_blocksize);return 0 ;
@@ -1141,6 +1153,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fc->user_id = d.user_id; fc->group_id = d.group_id; fc->max_read =max_t (unsigned,4096 , d.max_read); + fc->max_pages =clamp_val (d.max_pages,FUSE_DEFAULT_MAX_PAGES_PER_REQ , + pipe_max_size >>PAGE_SHIFT );/* Used by get_root_inode() */ sb->s_fs_info = fc;
fs/pipe.c +1 -0
@@ -34,6 +34,7 @@* be set by root in /proc/sys/fs/pipe-max-size */ unsigned int pipe_max_size =1048576 ; +EXPORT_SYMBOL (pipe_max_size);/* Maximum allocatable pages per user. Hard limit is unset by default, soft * matches default values.
Signature · 1 line
2.17.1
Hi Miklos,
Passed 20 days since the previous messages, so would you like to review the patch?
I've added reproduction and measurement details up your request. Also this patch can help resolve BDI_CAP_STRICTLIMIT performance issue reported recently by [email protected].
Thanks
Quoted 3 lines · Constantine Shulyupin
Replace
FUSE_MAX_PAGES_PER_REQwith the configurable mount parametermax_pagesto improve performance.
Link to message: https://marc.info/?l=linux-fsdevel&m=153623932516084&w=2
Miklos Szeredi
maintainerQuoted 4 lines · Constantine Shulyupin
Hi Miklos,
Passed 20 days since the previous messages, so would you like to review the patch?
Yes.
Quoted 3 lines
I've added reproduction and measurement details up your request. Also this patch can help resolve
BDI_CAP_STRICTLIMITperformance issue reported recently by [email protected].
Do you have feedback from <[email protected]> that this patch helps?
Thanks,
Miklos
Hi Constantine,
I haven't tested the patch yet. But after reviewing the patch, I don't see anything related to BDI_CAP_STRICTLIMIT. So would you please explain a little bit more? Thanks.
PS: I did try increasing FUSE_MAX_PAGES_PER_REQ, but it seemed not helping in my scenario(writeback cache enabled, 4K writes, total write size is not very large).
Shuoran Liu
-----邮件原件-----
发件人: Miklos Szeredi [mailto:[email protected]]
发送时间: 2018年9月26日 17:20
收件人: Constantine Shulyupin <[email protected]>
抄送: open list:FUSE: FILESYSTEM IN USERSPACE <[email protected]>; Jonathan Corbet <[email protected]>; 刘硕然 <[email protected]>; [email protected]; Amir Goldstein <[email protected]> 主题: Re: [PATCH v4] fuse: add max_pages option
Quoted 4 lines · Constantine Shulyupin
Hi Miklos,
Passed 20 days since the previous messages, so would you like to review the patch?
Yes.
Quoted 3 lines
I've added reproduction and measurement details up your request. Also this patch can help resolve
BDI_CAP_STRICTLIMITperformance issue reported recently by [email protected].
Do you have feedback from <[email protected]> that this patch helps?
Thanks,
Miklos
Hi Shuoran,
Quoted 1 line · 刘硕然
I haven't tested the patch yet. But after reviewing the patch, I don't see anything related to
BDI_CAP_STRICTLIMIT. So would you please explain a little bit more? Thanks.
Bigger size of request reduces total number of requests and reduces overhead of per request operations.
Quoted 1 line
PS: I did try increasing
FUSE_MAX_PAGES_PER_REQ, but it seemed not helping in myscenario(writeback cache enabled, 4K writes, total write size is not very large).
To utilize FUSE_MAX_PAGES_PER_REQ in kernel it is must to increase KERNEL_BUF_PAGES it libfuse too. I have libfuse patch with configurable max_pages.
But 4K writes will not benefit from increase of request size, which now is 128K and proposed size is 1M.
Thanks
刘硕然
商城技术架构部 智能存储部
------------------------------------------------------------------------------------------------
手机/+86 18201588100
邮箱/[email protected]
地址/北京市经济技术开发区科创十一街18号院京东大厦B座16层
------------------------------------------------------------------------------------------------
Quoted 24 lines
-----邮件原件-----
发件人: Constantine Shulyupin [mailto:[email protected]] 发送时间: 2018年9月26日 18:07
收件人: 刘硕然 <[email protected]>
抄送: Miklos Szeredi <[email protected]>; open list:FUSE: FILESYSTEM IN USERSPACE <[email protected]>; Jonathan Corbet <[email protected]>; [email protected]; Amir Goldstein <[email protected]>
主题: Re: [PATCH v4] fuse: addmax_pagesoptionHi Shuoran,
Quoted 1 line · 刘硕然
I haven't tested the patch yet. But after reviewing the patch, I don't see
anything related to
BDI_CAP_STRICTLIMIT. So would you please explain a little bit more? Thanks.Bigger size of request reduces total number of requests and reduces overhead of per request operations.
Quoted 1 line
PS: I did try increasing
FUSE_MAX_PAGES_PER_REQ, but it seemed nothelping in my
scenario(writeback cache enabled, 4K writes, total write size is not very large).
Quoted 7 lines
To utilize
FUSE_MAX_PAGES_PER_REQin kernel it is must to increaseKERNEL_BUF_PAGESit libfuse too. I have libfuse patch with configurablemax_pages.But 4K writes will not benefit from increase of request size, which now is 128K and proposed size is 1M.
This is true. So my original purpose of using writeback cache was trying to improve the performance of small writes. Because for big writes, fuse kernel would send requests to libfuse anyway, with or without writeback cache.
The BDI_CAP_STRICTLIMIT issue happens when writeback cache is enabled, since balance_dirty_pages() is triggered in every small writes even if no request is sent to libfuse, which slows things down.
So IMHO, increasing FUSE_MAX_PAGES_PER_REQ would certainly help for big writes, but might not be a solution to BDI_CAP_STRICTLIMIT issue. Please feel free to correct me if I misunderstand anything. Thanks in advance.
Quoted 1 line
Thanks
Miklos Szeredi
maintainerQuoted 44 lines · 刘硕然
刘硕然
商城技术架构部 智能存储部
------------------------------------------------------------------------------------------------
手机/+86 18201588100
邮箱/[email protected]
地址/北京市经济技术开发区科创十一街18号院京东大厦B座16层
------------------------------------------------------------------------------------------------Quoted 24 lines
-----邮件原件-----
发件人: Constantine Shulyupin [mailto:[email protected]] 发送时间: 2018年9月26日 18:07
收件人: 刘硕然 <[email protected]>
抄送: Miklos Szeredi <[email protected]>; open list:FUSE: FILESYSTEM IN USERSPACE <[email protected]>; Jonathan Corbet <[email protected]>; [email protected]; Amir Goldstein <[email protected]>
主题: Re: [PATCH v4] fuse: addmax_pagesoptionHi Shuoran,
Quoted 1 line · 刘硕然
I haven't tested the patch yet. But after reviewing the patch, I don't see
anything related to
BDI_CAP_STRICTLIMIT. So would you please explain a little bit more? Thanks.Bigger size of request reduces total number of requests and reduces overhead of per request operations.
Quoted 1 line
PS: I did try increasing
FUSE_MAX_PAGES_PER_REQ, but it seemed nothelping in my
scenario(writeback cache enabled, 4K writes, total write size is not very large).Quoted 7 lines
To utilize
FUSE_MAX_PAGES_PER_REQin kernel it is must to increaseKERNEL_BUF_PAGESit libfuse too. I have libfuse patch with configurablemax_pages.But 4K writes will not benefit from increase of request size, which now is 128K and proposed size is 1M.
Quoted 5 lines
This is true. So my original purpose of using writeback cache was trying to improve the performance of small writes. Because for big writes, fuse kernel would send requests to libfuse anyway, with or without writeback cache.
The
BDI_CAP_STRICTLIMITissue happens when writeback cache is enabled, sincebalance_dirty_pages()is triggered in every small writes even if no request is sent to libfuse, which slows things down.So IMHO, increasing
FUSE_MAX_PAGES_PER_REQwould certainly help for big writes, but might not be a solution toBDI_CAP_STRICTLIMITissue. Please feel free to correct me if I misunderstand anything. Thanks in advance.
Makes sense. Can you do a kernel profile to see where most of the time is actually spent in your workload?
Thanks,
Miklos
Miklos Szeredi
maintainerQuoted 4 lines · Constantine Shulyupin
Hi Miklos,
Passed 20 days since the previous messages, so would you like to review the patch?
Updated patch pushed to
git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git#for-next
The mount option was converted to negotiation via INIT request.
Please test.
Thanks,
Miklos