Skip to content

Commit 5da784c

Browse files
Constantine ShulyupinMiklos Szeredi
authored andcommitted
fuse: add max_pages to init_out
Replace FUSE_MAX_PAGES_PER_REQ with the configurable parameter max_pages to improve performance. Old RFC with detailed description of the problem and many fixes by Mitsuo Hayasaka (mitsuo.hayasaka.hu@hitachi.com): - https://lkml.org/lkml/2012/7/5/136 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 tmpfs fuse_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. Signed-off-by: Constantine Shulyupin <const@MakeLinux.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 8a7aa28 commit 5da784c

File tree

5 files changed

+54
-35
lines changed

5 files changed

+54
-35
lines changed

fs/fuse/dev.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ static struct fuse_req *__fuse_request_alloc(unsigned npages, gfp_t flags)
6161
struct page **pages = NULL;
6262
struct fuse_page_desc *page_descs = NULL;
6363

64+
WARN_ON(npages > FUSE_MAX_MAX_PAGES);
6465
if (npages > FUSE_REQ_INLINE_PAGES) {
6566
pages = kzalloc(npages * (sizeof(*pages) +
6667
sizeof(*page_descs)), flags);
@@ -1674,7 +1675,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
16741675
unsigned int num;
16751676
unsigned int offset;
16761677
size_t total_len = 0;
1677-
int num_pages;
1678+
unsigned int num_pages;
16781679

16791680
offset = outarg->offset & ~PAGE_MASK;
16801681
file_size = i_size_read(inode);
@@ -1686,7 +1687,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
16861687
num = file_size - outarg->offset;
16871688

16881689
num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
1689-
num_pages = min(num_pages, FUSE_MAX_PAGES_PER_REQ);
1690+
num_pages = min(num_pages, fc->max_pages);
16901691

16911692
req = fuse_get_req(fc, num_pages);
16921693
if (IS_ERR(req))

fs/fuse/file.c

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -850,11 +850,11 @@ static int fuse_readpages_fill(void *_data, struct page *page)
850850
fuse_wait_on_page_writeback(inode, page->index);
851851

852852
if (req->num_pages &&
853-
(req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
853+
(req->num_pages == fc->max_pages ||
854854
(req->num_pages + 1) * PAGE_SIZE > fc->max_read ||
855855
req->pages[req->num_pages - 1]->index + 1 != page->index)) {
856-
int nr_alloc = min_t(unsigned, data->nr_pages,
857-
FUSE_MAX_PAGES_PER_REQ);
856+
unsigned int nr_alloc = min_t(unsigned int, data->nr_pages,
857+
fc->max_pages);
858858
fuse_send_readpages(req, data->file);
859859
if (fc->async_read)
860860
req = fuse_get_req_for_background(fc, nr_alloc);
@@ -889,7 +889,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
889889
struct fuse_conn *fc = get_fuse_conn(inode);
890890
struct fuse_fill_data data;
891891
int err;
892-
int nr_alloc = min_t(unsigned, nr_pages, FUSE_MAX_PAGES_PER_REQ);
892+
unsigned int nr_alloc = min_t(unsigned int, nr_pages, fc->max_pages);
893893

894894
err = -EIO;
895895
if (is_bad_inode(inode))
@@ -1104,12 +1104,13 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
11041104
return count > 0 ? count : err;
11051105
}
11061106

1107-
static inline unsigned fuse_wr_pages(loff_t pos, size_t len)
1107+
static inline unsigned int fuse_wr_pages(loff_t pos, size_t len,
1108+
unsigned int max_pages)
11081109
{
1109-
return min_t(unsigned,
1110+
return min_t(unsigned int,
11101111
((pos + len - 1) >> PAGE_SHIFT) -
11111112
(pos >> PAGE_SHIFT) + 1,
1112-
FUSE_MAX_PAGES_PER_REQ);
1113+
max_pages);
11131114
}
11141115

11151116
static ssize_t fuse_perform_write(struct kiocb *iocb,
@@ -1131,7 +1132,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
11311132
do {
11321133
struct fuse_req *req;
11331134
ssize_t count;
1134-
unsigned nr_pages = fuse_wr_pages(pos, iov_iter_count(ii));
1135+
unsigned int nr_pages = fuse_wr_pages(pos, iov_iter_count(ii),
1136+
fc->max_pages);
11351137

11361138
req = fuse_get_req(fc, nr_pages);
11371139
if (IS_ERR(req)) {
@@ -1321,11 +1323,6 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
13211323
return ret < 0 ? ret : 0;
13221324
}
13231325

1324-
static inline int fuse_iter_npages(const struct iov_iter *ii_p)
1325-
{
1326-
return iov_iter_npages(ii_p, FUSE_MAX_PAGES_PER_REQ);
1327-
}
1328-
13291326
ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
13301327
loff_t *ppos, int flags)
13311328
{
@@ -1345,9 +1342,10 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
13451342
int err = 0;
13461343

13471344
if (io->async)
1348-
req = fuse_get_req_for_background(fc, fuse_iter_npages(iter));
1345+
req = fuse_get_req_for_background(fc, iov_iter_npages(iter,
1346+
fc->max_pages));
13491347
else
1350-
req = fuse_get_req(fc, fuse_iter_npages(iter));
1348+
req = fuse_get_req(fc, iov_iter_npages(iter, fc->max_pages));
13511349
if (IS_ERR(req))
13521350
return PTR_ERR(req);
13531351

@@ -1392,9 +1390,10 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
13921390
fuse_put_request(fc, req);
13931391
if (io->async)
13941392
req = fuse_get_req_for_background(fc,
1395-
fuse_iter_npages(iter));
1393+
iov_iter_npages(iter, fc->max_pages));
13961394
else
1397-
req = fuse_get_req(fc, fuse_iter_npages(iter));
1395+
req = fuse_get_req(fc, iov_iter_npages(iter,
1396+
fc->max_pages));
13981397
if (IS_ERR(req))
13991398
break;
14001399
}
@@ -1823,7 +1822,7 @@ static int fuse_writepages_fill(struct page *page,
18231822
is_writeback = fuse_page_is_writeback(inode, page->index);
18241823

18251824
if (req && req->num_pages &&
1826-
(is_writeback || req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
1825+
(is_writeback || req->num_pages == fc->max_pages ||
18271826
(req->num_pages + 1) * PAGE_SIZE > fc->max_write ||
18281827
data->orig_pages[req->num_pages - 1]->index + 1 != page->index)) {
18291828
fuse_writepages_send(data);
@@ -1851,7 +1850,7 @@ static int fuse_writepages_fill(struct page *page,
18511850
struct fuse_inode *fi = get_fuse_inode(inode);
18521851

18531852
err = -ENOMEM;
1854-
req = fuse_request_alloc_nofs(FUSE_MAX_PAGES_PER_REQ);
1853+
req = fuse_request_alloc_nofs(fc->max_pages);
18551854
if (!req) {
18561855
__free_page(tmp_page);
18571856
goto out_unlock;
@@ -1908,6 +1907,7 @@ static int fuse_writepages(struct address_space *mapping,
19081907
struct writeback_control *wbc)
19091908
{
19101909
struct inode *inode = mapping->host;
1910+
struct fuse_conn *fc = get_fuse_conn(inode);
19111911
struct fuse_fill_wb_data data;
19121912
int err;
19131913

@@ -1920,7 +1920,7 @@ static int fuse_writepages(struct address_space *mapping,
19201920
data.ff = NULL;
19211921

19221922
err = -ENOMEM;
1923-
data.orig_pages = kcalloc(FUSE_MAX_PAGES_PER_REQ,
1923+
data.orig_pages = kcalloc(fc->max_pages,
19241924
sizeof(struct page *),
19251925
GFP_NOFS);
19261926
if (!data.orig_pages)
@@ -2391,10 +2391,11 @@ static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src,
23912391
}
23922392

23932393
/* Make sure iov_length() won't overflow */
2394-
static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count)
2394+
static int fuse_verify_ioctl_iov(struct fuse_conn *fc, struct iovec *iov,
2395+
size_t count)
23952396
{
23962397
size_t n;
2397-
u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT;
2398+
u32 max = fc->max_pages << PAGE_SHIFT;
23982399

23992400
for (n = 0; n < count; n++, iov++) {
24002401
if (iov->iov_len > (size_t) max)
@@ -2518,7 +2519,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
25182519
BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
25192520

25202521
err = -ENOMEM;
2521-
pages = kcalloc(FUSE_MAX_PAGES_PER_REQ, sizeof(pages[0]), GFP_KERNEL);
2522+
pages = kcalloc(fc->max_pages, sizeof(pages[0]), GFP_KERNEL);
25222523
iov_page = (struct iovec *) __get_free_page(GFP_KERNEL);
25232524
if (!pages || !iov_page)
25242525
goto out;
@@ -2557,7 +2558,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
25572558

25582559
/* make sure there are enough buffer pages and init request with them */
25592560
err = -ENOMEM;
2560-
if (max_pages > FUSE_MAX_PAGES_PER_REQ)
2561+
if (max_pages > fc->max_pages)
25612562
goto out;
25622563
while (num_pages < max_pages) {
25632564
pages[num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
@@ -2644,11 +2645,11 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
26442645
in_iov = iov_page;
26452646
out_iov = in_iov + in_iovs;
26462647

2647-
err = fuse_verify_ioctl_iov(in_iov, in_iovs);
2648+
err = fuse_verify_ioctl_iov(fc, in_iov, in_iovs);
26482649
if (err)
26492650
goto out;
26502651

2651-
err = fuse_verify_ioctl_iov(out_iov, out_iovs);
2652+
err = fuse_verify_ioctl_iov(fc, out_iov, out_iovs);
26522653
if (err)
26532654
goto out;
26542655

@@ -2839,9 +2840,9 @@ static void fuse_do_truncate(struct file *file)
28392840
fuse_do_setattr(file_dentry(file), &attr, file);
28402841
}
28412842

2842-
static inline loff_t fuse_round_up(loff_t off)
2843+
static inline loff_t fuse_round_up(struct fuse_conn *fc, loff_t off)
28432844
{
2844-
return round_up(off, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT);
2845+
return round_up(off, fc->max_pages << PAGE_SHIFT);
28452846
}
28462847

28472848
static ssize_t
@@ -2870,7 +2871,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
28702871
if (async_dio && iov_iter_rw(iter) != WRITE && offset + count > i_size) {
28712872
if (offset >= i_size)
28722873
return 0;
2873-
iov_iter_truncate(iter, fuse_round_up(i_size - offset));
2874+
iov_iter_truncate(iter, fuse_round_up(ff->fc, i_size - offset));
28742875
count = iov_iter_count(iter);
28752876
}
28762877

fs/fuse/fuse_i.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828
#include <linux/refcount.h>
2929
#include <linux/user_namespace.h>
3030

31-
/** Max number of pages that can be used in a single read request */
32-
#define FUSE_MAX_PAGES_PER_REQ 32
31+
/** Default max number of pages that can be used in a single read request */
32+
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
33+
34+
/** Maximum of max_pages received in init_out */
35+
#define FUSE_MAX_MAX_PAGES 256
3336

3437
/** Bias for fi->writectr, meaning new writepages must not be sent */
3538
#define FUSE_NOWRITE INT_MIN
@@ -525,6 +528,9 @@ struct fuse_conn {
525528
/** Maximum write size */
526529
unsigned max_write;
527530

531+
/** Maxmum number of pages that can be used in a single request */
532+
unsigned int max_pages;
533+
528534
/** Input queue */
529535
struct fuse_iqueue iq;
530536

fs/fuse/inode.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
928928
}
929929
if (arg->flags & FUSE_ABORT_ERROR)
930930
fc->abort_err = 1;
931+
if (arg->flags & FUSE_MAX_PAGES) {
932+
fc->max_pages =
933+
min_t(unsigned int, FUSE_MAX_MAX_PAGES,
934+
max_t(unsigned int, arg->max_pages, 1));
935+
}
931936
} else {
932937
ra_pages = fc->max_read / PAGE_SIZE;
933938
fc->no_lock = 1;
@@ -959,7 +964,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
959964
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
960965
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
961966
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
962-
FUSE_ABORT_ERROR;
967+
FUSE_ABORT_ERROR | FUSE_MAX_PAGES;
963968
req->in.h.opcode = FUSE_INIT;
964969
req->in.numargs = 1;
965970
req->in.args[0].size = sizeof(*arg);
@@ -1152,6 +1157,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
11521157
fc->user_id = d.user_id;
11531158
fc->group_id = d.group_id;
11541159
fc->max_read = max_t(unsigned, 4096, d.max_read);
1160+
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
11551161

11561162
/* Used by get_root_inode() */
11571163
sb->s_fs_info = fc;

include/uapi/linux/fuse.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
* 7.28
121121
* - add FUSE_COPY_FILE_RANGE
122122
* - add FOPEN_CACHE_DIR
123+
* - add FUSE_MAX_PAGES, add max_pages to init_out
123124
*/
124125

125126
#ifndef _LINUX_FUSE_H
@@ -255,6 +256,7 @@ struct fuse_file_lock {
255256
* FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc
256257
* FUSE_POSIX_ACL: filesystem supports posix acls
257258
* FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
259+
* FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
258260
*/
259261
#define FUSE_ASYNC_READ (1 << 0)
260262
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -278,6 +280,7 @@ struct fuse_file_lock {
278280
#define FUSE_HANDLE_KILLPRIV (1 << 19)
279281
#define FUSE_POSIX_ACL (1 << 20)
280282
#define FUSE_ABORT_ERROR (1 << 21)
283+
#define FUSE_MAX_PAGES (1 << 22)
281284

282285
/**
283286
* CUSE INIT request/reply flags
@@ -617,7 +620,9 @@ struct fuse_init_out {
617620
uint16_t congestion_threshold;
618621
uint32_t max_write;
619622
uint32_t time_gran;
620-
uint32_t unused[9];
623+
uint16_t max_pages;
624+
uint16_t padding;
625+
uint32_t unused[8];
621626
};
622627

623628
#define CUSE_INIT_INFO_MAX 4096

0 commit comments

Comments
 (0)