Skip to content

Commit 6b2fb79

Browse files
Maxim PatlasovMiklos Szeredi
authored andcommitted
fuse: optimize writepages search
Re-work fi->writepages, replacing list with rb-tree. This improves performance because kernel fuse iterates through fi->writepages for each writeback page and typical number of entries is about 800 (for 100MB of fuse writeback). Before patch: 10240+0 records in 10240+0 records out 10737418240 bytes (11 GB) copied, 41.3473 s, 260 MB/s 2 1 0 57445400 40416 6323676 0 0 33 374743 8633 19210 1 8 88 3 0 29.86% [kernel] [k] _raw_spin_lock 26.62% [fuse] [k] fuse_page_is_writeback After patch: 10240+0 records in 10240+0 records out 10737418240 bytes (11 GB) copied, 21.4954 s, 500 MB/s 2 9 0 53676040 31744 10265984 0 0 64 854790 10956 48387 1 6 88 6 0 23.55% [kernel] [k] copy_user_enhanced_fast_string 9.87% [kernel] [k] __memcpy 3.10% [kernel] [k] _raw_spin_lock Signed-off-by: Maxim Patlasov <[email protected]> Signed-off-by: Vasily Averin <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 5ddd9ce commit 6b2fb79

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

fs/fuse/file.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
357357

358358
struct fuse_writepage_args {
359359
struct fuse_io_args ia;
360-
struct list_head writepages_entry;
360+
struct rb_node writepages_entry;
361361
struct list_head queue_entry;
362362
struct fuse_writepage_args *next;
363363
struct inode *inode;
@@ -366,17 +366,23 @@ struct fuse_writepage_args {
366366
static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi,
367367
pgoff_t idx_from, pgoff_t idx_to)
368368
{
369-
struct fuse_writepage_args *wpa;
369+
struct rb_node *n;
370+
371+
n = fi->writepages.rb_node;
370372

371-
list_for_each_entry(wpa, &fi->writepages, writepages_entry) {
373+
while (n) {
374+
struct fuse_writepage_args *wpa;
372375
pgoff_t curr_index;
373376

377+
wpa = rb_entry(n, struct fuse_writepage_args, writepages_entry);
374378
WARN_ON(get_fuse_inode(wpa->inode) != fi);
375379
curr_index = wpa->ia.write.in.offset >> PAGE_SHIFT;
376-
if (idx_from < curr_index + wpa->ia.ap.num_pages &&
377-
curr_index <= idx_to) {
380+
if (idx_from >= curr_index + wpa->ia.ap.num_pages)
381+
n = n->rb_right;
382+
else if (idx_to < curr_index)
383+
n = n->rb_left;
384+
else
378385
return wpa;
379-
}
380386
}
381387
return NULL;
382388
}
@@ -1624,7 +1630,7 @@ static void fuse_writepage_finish(struct fuse_conn *fc,
16241630
struct backing_dev_info *bdi = inode_to_bdi(inode);
16251631
int i;
16261632

1627-
list_del(&wpa->writepages_entry);
1633+
rb_erase(&wpa->writepages_entry, &fi->writepages);
16281634
for (i = 0; i < ap->num_pages; i++) {
16291635
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
16301636
dec_node_page_state(ap->pages[i], NR_WRITEBACK_TEMP);
@@ -1712,6 +1718,36 @@ __acquires(fi->lock)
17121718
}
17131719
}
17141720

1721+
static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
1722+
{
1723+
pgoff_t idx_from = wpa->ia.write.in.offset >> PAGE_SHIFT;
1724+
pgoff_t idx_to = idx_from + wpa->ia.ap.num_pages - 1;
1725+
struct rb_node **p = &root->rb_node;
1726+
struct rb_node *parent = NULL;
1727+
1728+
WARN_ON(!wpa->ia.ap.num_pages);
1729+
while (*p) {
1730+
struct fuse_writepage_args *curr;
1731+
pgoff_t curr_index;
1732+
1733+
parent = *p;
1734+
curr = rb_entry(parent, struct fuse_writepage_args,
1735+
writepages_entry);
1736+
WARN_ON(curr->inode != wpa->inode);
1737+
curr_index = curr->ia.write.in.offset >> PAGE_SHIFT;
1738+
1739+
if (idx_from >= curr_index + curr->ia.ap.num_pages)
1740+
p = &(*p)->rb_right;
1741+
else if (idx_to < curr_index)
1742+
p = &(*p)->rb_left;
1743+
else
1744+
return (void) WARN_ON(true);
1745+
}
1746+
1747+
rb_link_node(&wpa->writepages_entry, parent, p);
1748+
rb_insert_color(&wpa->writepages_entry, root);
1749+
}
1750+
17151751
static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_args *args,
17161752
int error)
17171753
{
@@ -1730,7 +1766,7 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_args *args,
17301766
wpa->next = next->next;
17311767
next->next = NULL;
17321768
next->ia.ff = fuse_file_get(wpa->ia.ff);
1733-
list_add(&next->writepages_entry, &fi->writepages);
1769+
tree_insert(&fi->writepages, next);
17341770

17351771
/*
17361772
* Skip fuse_flush_writepages() to make it easy to crop requests
@@ -1865,7 +1901,7 @@ static int fuse_writepage_locked(struct page *page)
18651901
inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
18661902

18671903
spin_lock(&fi->lock);
1868-
list_add(&wpa->writepages_entry, &fi->writepages);
1904+
tree_insert(&fi->writepages, wpa);
18691905
list_add_tail(&wpa->queue_entry, &fi->queued_writes);
18701906
fuse_flush_writepages(inode);
18711907
spin_unlock(&fi->lock);
@@ -1977,10 +2013,10 @@ static bool fuse_writepage_in_flight(struct fuse_writepage_args *new_wpa,
19772013
WARN_ON(new_ap->num_pages != 0);
19782014

19792015
spin_lock(&fi->lock);
1980-
list_del(&new_wpa->writepages_entry);
2016+
rb_erase(&new_wpa->writepages_entry, &fi->writepages);
19812017
old_wpa = fuse_find_writeback(fi, page->index, page->index);
19822018
if (!old_wpa) {
1983-
list_add(&new_wpa->writepages_entry, &fi->writepages);
2019+
tree_insert(&fi->writepages, new_wpa);
19842020
spin_unlock(&fi->lock);
19852021
return false;
19862022
}
@@ -2095,7 +2131,7 @@ static int fuse_writepages_fill(struct page *page,
20952131
wpa->inode = inode;
20962132

20972133
spin_lock(&fi->lock);
2098-
list_add(&wpa->writepages_entry, &fi->writepages);
2134+
tree_insert(&fi->writepages, wpa);
20992135
spin_unlock(&fi->lock);
21002136

21012137
data->wpa = wpa;
@@ -3405,5 +3441,5 @@ void fuse_init_file_inode(struct inode *inode)
34053441
INIT_LIST_HEAD(&fi->queued_writes);
34063442
fi->writectr = 0;
34073443
init_waitqueue_head(&fi->page_waitq);
3408-
INIT_LIST_HEAD(&fi->writepages);
3444+
fi->writepages = RB_ROOT;
34093445
}

fs/fuse/fuse_i.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ struct fuse_inode {
111111
wait_queue_head_t page_waitq;
112112

113113
/* List of writepage requestst (pending or sent) */
114-
struct list_head writepages;
114+
struct rb_root writepages;
115115
};
116116

117117
/* readdir cache (directory only) */

0 commit comments

Comments
 (0)