* [PATCH v2 01/11] fuse: convert readahead to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 02/11] fuse: convert fuse_send_write_pages " Josef Bacik
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
Currently we're using the __readahead_batch() helper which populates our
fuse_args_pages->pages array with pages. Convert this to use the newer
folio based pattern which is to call readahead_folio() to get the next
folio in the read ahead batch. I've updated the code to use things like
folio_size() and to take into account larger folio sizes, but this is
purely to make that eventual work easier to do, we currently will not
get large folios so this is more future proofing than actual support.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 43 ++++++++++++++++++++++++++++---------------
1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 88f872c02349..2fd6513ac53e 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -938,7 +938,6 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args,
struct folio *folio = page_folio(ap->pages[i]);
folio_end_read(folio, !err);
- folio_put(folio);
}
if (ia->ff)
fuse_file_put(ia->ff, false);
@@ -985,18 +984,36 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file)
static void fuse_readahead(struct readahead_control *rac)
{
struct inode *inode = rac->mapping->host;
+ struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = get_fuse_conn(inode);
- unsigned int i, max_pages, nr_pages = 0;
+ unsigned int max_pages, nr_pages;
+ pgoff_t first = readahead_index(rac);
+ pgoff_t last = first + readahead_count(rac) - 1;
if (fuse_is_bad(inode))
return;
+ wait_event(fi->page_waitq, !fuse_range_is_writeback(inode, first, last));
+
max_pages = min_t(unsigned int, fc->max_pages,
fc->max_read / PAGE_SIZE);
- for (;;) {
+ /*
+ * This is only accurate the first time through, since readahead_folio()
+ * doesn't update readahead_count() from the previous folio until the
+ * next call. Grab nr_pages here so we know how many pages we're going
+ * to have to process. This means that we will exit here with
+ * readahead_count() == folio_nr_pages(last_folio), but we will have
+ * consumed all of the folios, and read_pages() will call
+ * readahead_folio() again which will clean up the rac.
+ */
+ nr_pages = readahead_count(rac);
+
+ while (nr_pages) {
struct fuse_io_args *ia;
struct fuse_args_pages *ap;
+ struct folio *folio;
+ unsigned cur_pages = min(max_pages, nr_pages);
if (fc->num_background >= fc->congestion_threshold &&
rac->ra->async_size >= readahead_count(rac))
@@ -1006,23 +1023,19 @@ static void fuse_readahead(struct readahead_control *rac)
*/
break;
- nr_pages = readahead_count(rac) - nr_pages;
- if (nr_pages > max_pages)
- nr_pages = max_pages;
- if (nr_pages == 0)
- break;
- ia = fuse_io_alloc(NULL, nr_pages);
+ ia = fuse_io_alloc(NULL, cur_pages);
if (!ia)
return;
ap = &ia->ap;
- nr_pages = __readahead_batch(rac, ap->pages, nr_pages);
- for (i = 0; i < nr_pages; i++) {
- fuse_wait_on_page_writeback(inode,
- readahead_index(rac) + i);
- ap->descs[i].length = PAGE_SIZE;
+
+ while (ap->num_pages < cur_pages &&
+ (folio = readahead_folio(rac)) != NULL) {
+ ap->pages[ap->num_pages] = &folio->page;
+ ap->descs[ap->num_pages].length = folio_size(folio);
+ ap->num_pages++;
}
- ap->num_pages = nr_pages;
fuse_send_readpages(ia, rac->file);
+ nr_pages -= cur_pages;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 02/11] fuse: convert fuse_send_write_pages to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
2024-08-28 21:13 ` [PATCH v2 01/11] fuse: convert readahead to use folios Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 03/11] fuse: convert fuse_fill_write_pages " Josef Bacik
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
Convert this to grab the folio from the fuse_args_pages and use the
appropriate folio related functions.
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 2fd6513ac53e..275af3a7c50b 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1168,23 +1168,23 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
offset = ap->descs[0].offset;
count = ia->write.out.size;
for (i = 0; i < ap->num_pages; i++) {
- struct page *page = ap->pages[i];
+ struct folio *folio = page_folio(ap->pages[i]);
if (err) {
- ClearPageUptodate(page);
+ folio_clear_uptodate(folio);
} else {
- if (count >= PAGE_SIZE - offset)
- count -= PAGE_SIZE - offset;
+ if (count >= folio_size(folio) - offset)
+ count -= folio_size(folio) - offset;
else {
if (short_write)
- ClearPageUptodate(page);
+ folio_clear_uptodate(folio);
count = 0;
}
offset = 0;
}
if (ia->write.page_locked && (i == ap->num_pages - 1))
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
}
return err;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 03/11] fuse: convert fuse_fill_write_pages to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
2024-08-28 21:13 ` [PATCH v2 01/11] fuse: convert readahead to use folios Josef Bacik
2024-08-28 21:13 ` [PATCH v2 02/11] fuse: convert fuse_send_write_pages " Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 04/11] fuse: convert fuse_page_mkwrite " Josef Bacik
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
Convert this to grab the folio directly, and update all the helpers to
use the folio related functions.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 275af3a7c50b..08fc44419f01 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1206,7 +1206,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
do {
size_t tmp;
- struct page *page;
+ struct folio *folio;
pgoff_t index = pos >> PAGE_SHIFT;
size_t bytes = min_t(size_t, PAGE_SIZE - offset,
iov_iter_count(ii));
@@ -1218,25 +1218,27 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
if (fault_in_iov_iter_readable(ii, bytes))
break;
- err = -ENOMEM;
- page = grab_cache_page_write_begin(mapping, index);
- if (!page)
+ folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
+ mapping_gfp_mask(mapping));
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
break;
+ }
if (mapping_writably_mapped(mapping))
- flush_dcache_page(page);
+ flush_dcache_folio(folio);
- tmp = copy_page_from_iter_atomic(page, offset, bytes, ii);
- flush_dcache_page(page);
+ tmp = copy_folio_from_iter_atomic(folio, offset, bytes, ii);
+ flush_dcache_folio(folio);
if (!tmp) {
- unlock_page(page);
- put_page(page);
+ folio_unlock(folio);
+ folio_put(folio);
goto again;
}
err = 0;
- ap->pages[ap->num_pages] = page;
+ ap->pages[ap->num_pages] = &folio->page;
ap->descs[ap->num_pages].length = tmp;
ap->num_pages++;
@@ -1248,10 +1250,10 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
/* If we copied full page, mark it uptodate */
if (tmp == PAGE_SIZE)
- SetPageUptodate(page);
+ folio_mark_uptodate(folio);
- if (PageUptodate(page)) {
- unlock_page(page);
+ if (folio_test_uptodate(folio)) {
+ folio_unlock(folio);
} else {
ia->write.page_locked = true;
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 04/11] fuse: convert fuse_page_mkwrite to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (2 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 03/11] fuse: convert fuse_fill_write_pages " Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 05/11] fuse: use kiocb_modified in buffered write path Josef Bacik
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
Convert this to grab the folio directly, and update all the helpers to
use the folio related functions.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 08fc44419f01..830c09b024ae 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -483,6 +483,16 @@ static void fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index));
}
+static void fuse_wait_on_folio_writeback(struct inode *inode,
+ struct folio *folio)
+{
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ pgoff_t last = folio_next_index(folio) - 1;
+
+ wait_event(fi->page_waitq,
+ !fuse_range_is_writeback(inode, folio_index(folio), last));
+}
+
/*
* Wait for all pending writepages on the inode to finish.
*
@@ -2525,17 +2535,17 @@ static void fuse_vma_close(struct vm_area_struct *vma)
*/
static vm_fault_t fuse_page_mkwrite(struct vm_fault *vmf)
{
- struct page *page = vmf->page;
+ struct folio *folio = page_folio(vmf->page);
struct inode *inode = file_inode(vmf->vma->vm_file);
file_update_time(vmf->vma->vm_file);
- lock_page(page);
- if (page->mapping != inode->i_mapping) {
- unlock_page(page);
+ folio_lock(folio);
+ if (folio->mapping != inode->i_mapping) {
+ folio_unlock(folio);
return VM_FAULT_NOPAGE;
}
- fuse_wait_on_page_writeback(inode, page->index);
+ fuse_wait_on_folio_writeback(inode, folio);
return VM_FAULT_LOCKED;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 05/11] fuse: use kiocb_modified in buffered write path
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (3 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 04/11] fuse: convert fuse_page_mkwrite " Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-09-10 21:51 ` Bernd Schubert
2024-08-28 21:13 ` [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes Josef Bacik
` (5 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
This combines the file_remove_privs() and file_update_time() call into
one call.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 830c09b024ae..ab531a4694b3 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1454,11 +1454,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
task_io_account_write(count);
- err = file_remove_privs(file);
- if (err)
- goto out;
-
- err = file_update_time(file);
+ err = kiocb_modified(iocb);
if (err)
goto out;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 05/11] fuse: use kiocb_modified in buffered write path
2024-08-28 21:13 ` [PATCH v2 05/11] fuse: use kiocb_modified in buffered write path Josef Bacik
@ 2024-09-10 21:51 ` Bernd Schubert
0 siblings, 0 replies; 16+ messages in thread
From: Bernd Schubert @ 2024-09-10 21:51 UTC (permalink / raw)
To: Josef Bacik, linux-fsdevel, amir73il, miklos, joannelkoong,
bschubert, willy
On 8/28/24 23:13, Josef Bacik wrote:
> This combines the file_remove_privs() and file_update_time() call into
> one call.
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
> fs/fuse/file.c | 6 +-----
> 1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 830c09b024ae..ab531a4694b3 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -1454,11 +1454,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>
> task_io_account_write(count);
>
> - err = file_remove_privs(file);
> - if (err)
> - goto out;
> -
> - err = file_update_time(file);
> + err = kiocb_modified(iocb);
> if (err)
> goto out;
>
Hmm, I need to discuss with Miklos if there is any interest to merge my
DIO consolidation branch - it needs to be heavily updated and my main
motivation to have the shared inode lock for for O_DIRECT writes is not
possible. But it merges fuse_direct_write_iter (FOPEN_DIRECT_IO) and
fuse_cache_write_iter into one call path (and actually fixes some
FOPEN_DIRECT_IO xfstests failures). For that branch I need to have
file_remove_privs separated.
https://lore.kernel.org/all/20230918150313.3845114-6-bschubert@ddn.com/
As I said, it need discussion if there is interest for that branch.
For current code, looks good to me.
Reviewed-by: Bernd Schubert <bschubert@ddn.com>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (4 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 05/11] fuse: use kiocb_modified in buffered write path Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-29 3:50 ` Dave Chinner
2024-08-29 23:30 ` Bernd Schubert
2024-08-28 21:13 ` [PATCH v2 07/11] fuse: convert fuse_do_readpage to use folios Josef Bacik
` (4 subsequent siblings)
10 siblings, 2 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
We're currently using the old ->write_begin()/->write_end() method of
doing buffered writes. This isn't a huge deal for fuse since we
basically just want to copy the pages and move on, but the iomap
infrastructure gives us access to having huge folios. Rework the
buffered write path when we have writeback cache to use the iomap
buffered write code, the ->get_folio() callback now handles the work
that we did in ->write_begin(), the rest of the work is handled inside
of iomap so we don't need a replacement for ->write_end.
This does bring BLOCK as a dependency, as the buffered write part of
iomap requires CONFIG_BLOCK. This could be shed if we reworked the file
write iter portion of the buffered write path was separated out to not
need BLOCK.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/Kconfig | 2 +
fs/fuse/file.c | 154 +++++++++++++++++++++---------------------------
2 files changed, 68 insertions(+), 88 deletions(-)
diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
index 8674dbfbe59d..8a799324d7bd 100644
--- a/fs/fuse/Kconfig
+++ b/fs/fuse/Kconfig
@@ -1,7 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config FUSE_FS
tristate "FUSE (Filesystem in Userspace) support"
+ depends on BLOCK
select FS_POSIX_ACL
+ select FS_IOMAP
help
With FUSE it is possible to implement a fully functional filesystem
in a userspace program.
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index ab531a4694b3..af91043b44d7 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -21,6 +21,7 @@
#include <linux/filelock.h>
#include <linux/splice.h>
#include <linux/task_io_accounting_ops.h>
+#include <linux/iomap.h>
static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
unsigned int open_flags, int opcode,
@@ -1420,6 +1421,63 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
}
}
+static struct folio *fuse_iomap_get_folio(struct iomap_iter *iter,
+ loff_t pos, unsigned len)
+{
+ struct file *file = (struct file *)iter->private;
+ struct inode *inode = iter->inode;
+ struct folio *folio;
+ loff_t fsize;
+
+ folio = iomap_get_folio(iter, pos, len);
+ if (IS_ERR(folio))
+ return folio;
+
+ fuse_wait_on_folio_writeback(inode, folio);
+
+ if (folio_test_uptodate(folio))
+ return folio;
+
+ /*
+ * If we're going to write past EOF then avoid the read, but zero the
+ * whole thing and mark it uptodate so that if we get a short write we
+ * don't try to re-read this page, we just carry on.
+ */
+ fsize = i_size_read(inode);
+ if (fsize <= folio_pos(folio)) {
+ folio_zero_range(folio, 0, folio_size(folio));
+ } else {
+ int err = fuse_do_readpage(file, &folio->page);
+ if (err) {
+ folio_unlock(folio);
+ folio_put(folio);
+ return ERR_PTR(err);
+ }
+ }
+
+ return folio;
+}
+
+static const struct iomap_folio_ops fuse_iomap_folio_ops = {
+ .get_folio = fuse_iomap_get_folio,
+};
+
+static int fuse_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length,
+ unsigned flags, struct iomap *iomap,
+ struct iomap *srcmap)
+{
+ iomap->type = IOMAP_DELALLOC;
+ iomap->addr = IOMAP_NULL_ADDR;
+ iomap->offset = pos;
+ iomap->length = length;
+ iomap->folio_ops = &fuse_iomap_folio_ops;
+ return 0;
+}
+
+static const struct iomap_ops fuse_iomap_write_ops = {
+ .iomap_begin = fuse_iomap_begin_write,
+};
+
static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
@@ -1428,6 +1486,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct inode *inode = mapping->host;
ssize_t err, count;
struct fuse_conn *fc = get_fuse_conn(inode);
+ bool writethrough = (fc->writeback_cache == 0);
if (fc->writeback_cache) {
/* Update size (EOF optimization) and mode (SUID clearing) */
@@ -1438,14 +1497,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (fc->handle_killpriv_v2 &&
setattr_should_drop_suidgid(&nop_mnt_idmap,
- file_inode(file))) {
- goto writethrough;
- }
-
- return generic_file_write_iter(iocb, from);
+ file_inode(file)))
+ writethrough = true;
}
-writethrough:
inode_lock(inode);
err = count = generic_write_checks(iocb, from);
@@ -1464,8 +1519,12 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
goto out;
written = direct_write_fallback(iocb, from, written,
fuse_perform_write(iocb, from));
- } else {
+ } else if (writethrough) {
written = fuse_perform_write(iocb, from);
+ } else {
+ written = iomap_file_buffered_write(iocb, from,
+ &fuse_iomap_write_ops,
+ file);
}
out:
inode_unlock(inode);
@@ -2408,85 +2467,6 @@ static int fuse_writepages(struct address_space *mapping,
return err;
}
-/*
- * It's worthy to make sure that space is reserved on disk for the write,
- * but how to implement it without killing performance need more thinking.
- */
-static int fuse_write_begin(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, struct page **pagep, void **fsdata)
-{
- pgoff_t index = pos >> PAGE_SHIFT;
- struct fuse_conn *fc = get_fuse_conn(file_inode(file));
- struct page *page;
- loff_t fsize;
- int err = -ENOMEM;
-
- WARN_ON(!fc->writeback_cache);
-
- page = grab_cache_page_write_begin(mapping, index);
- if (!page)
- goto error;
-
- fuse_wait_on_page_writeback(mapping->host, page->index);
-
- if (PageUptodate(page) || len == PAGE_SIZE)
- goto success;
- /*
- * Check if the start this page comes after the end of file, in which
- * case the readpage can be optimized away.
- */
- fsize = i_size_read(mapping->host);
- if (fsize <= (pos & PAGE_MASK)) {
- size_t off = pos & ~PAGE_MASK;
- if (off)
- zero_user_segment(page, 0, off);
- goto success;
- }
- err = fuse_do_readpage(file, page);
- if (err)
- goto cleanup;
-success:
- *pagep = page;
- return 0;
-
-cleanup:
- unlock_page(page);
- put_page(page);
-error:
- return err;
-}
-
-static int fuse_write_end(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, unsigned copied,
- struct page *page, void *fsdata)
-{
- struct inode *inode = page->mapping->host;
-
- /* Haven't copied anything? Skip zeroing, size extending, dirtying. */
- if (!copied)
- goto unlock;
-
- pos += copied;
- if (!PageUptodate(page)) {
- /* Zero any unwritten bytes at the end of the page */
- size_t endoff = pos & ~PAGE_MASK;
- if (endoff)
- zero_user_segment(page, endoff, PAGE_SIZE);
- SetPageUptodate(page);
- }
-
- if (pos > inode->i_size)
- i_size_write(inode, pos);
-
- set_page_dirty(page);
-
-unlock:
- unlock_page(page);
- put_page(page);
-
- return copied;
-}
-
static int fuse_launder_folio(struct folio *folio)
{
int err = 0;
@@ -3352,8 +3332,6 @@ static const struct address_space_operations fuse_file_aops = {
.migrate_folio = filemap_migrate_folio,
.bmap = fuse_bmap,
.direct_IO = fuse_direct_IO,
- .write_begin = fuse_write_begin,
- .write_end = fuse_write_end,
};
void fuse_init_file_inode(struct inode *inode, unsigned int flags)
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes
2024-08-28 21:13 ` [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes Josef Bacik
@ 2024-08-29 3:50 ` Dave Chinner
2024-08-29 14:23 ` Josef Bacik
2024-08-29 23:30 ` Bernd Schubert
1 sibling, 1 reply; 16+ messages in thread
From: Dave Chinner @ 2024-08-29 3:50 UTC (permalink / raw)
To: Josef Bacik
Cc: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
On Wed, Aug 28, 2024 at 05:13:56PM -0400, Josef Bacik wrote:
> We're currently using the old ->write_begin()/->write_end() method of
> doing buffered writes. This isn't a huge deal for fuse since we
> basically just want to copy the pages and move on, but the iomap
> infrastructure gives us access to having huge folios. Rework the
> buffered write path when we have writeback cache to use the iomap
> buffered write code, the ->get_folio() callback now handles the work
> that we did in ->write_begin(), the rest of the work is handled inside
> of iomap so we don't need a replacement for ->write_end.
>
> This does bring BLOCK as a dependency, as the buffered write part of
> iomap requires CONFIG_BLOCK. This could be shed if we reworked the file
> write iter portion of the buffered write path was separated out to not
> need BLOCK.
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
> fs/fuse/Kconfig | 2 +
> fs/fuse/file.c | 154 +++++++++++++++++++++---------------------------
> 2 files changed, 68 insertions(+), 88 deletions(-)
>
> diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
> index 8674dbfbe59d..8a799324d7bd 100644
> --- a/fs/fuse/Kconfig
> +++ b/fs/fuse/Kconfig
> @@ -1,7 +1,9 @@
> # SPDX-License-Identifier: GPL-2.0-only
> config FUSE_FS
> tristate "FUSE (Filesystem in Userspace) support"
> + depends on BLOCK
> select FS_POSIX_ACL
> + select FS_IOMAP
> help
> With FUSE it is possible to implement a fully functional filesystem
> in a userspace program.
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index ab531a4694b3..af91043b44d7 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -21,6 +21,7 @@
> #include <linux/filelock.h>
> #include <linux/splice.h>
> #include <linux/task_io_accounting_ops.h>
> +#include <linux/iomap.h>
>
> static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
> unsigned int open_flags, int opcode,
> @@ -1420,6 +1421,63 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
> }
> }
>
> +static struct folio *fuse_iomap_get_folio(struct iomap_iter *iter,
> + loff_t pos, unsigned len)
> +{
> + struct file *file = (struct file *)iter->private;
> + struct inode *inode = iter->inode;
> + struct folio *folio;
> + loff_t fsize;
> +
> + folio = iomap_get_folio(iter, pos, len);
> + if (IS_ERR(folio))
> + return folio;
> +
> + fuse_wait_on_folio_writeback(inode, folio);
> +
> + if (folio_test_uptodate(folio))
> + return folio;
> +
> + /*
> + * If we're going to write past EOF then avoid the read, but zero the
> + * whole thing and mark it uptodate so that if we get a short write we
> + * don't try to re-read this page, we just carry on.
> + */
> + fsize = i_size_read(inode);
> + if (fsize <= folio_pos(folio)) {
> + folio_zero_range(folio, 0, folio_size(folio));
The comment doesn't match what this does - the folio is not marked
uptodate at all.
> + } else {
> + int err = fuse_do_readpage(file, &folio->page);
readpage on a large folio? does that work?
> + if (err) {
> + folio_unlock(folio);
> + folio_put(folio);
> + return ERR_PTR(err);
> + }
> + }
Also, why do this here when __iomap_write_begin() will do all the
sub-folio zeroing and read IO on the folio?
> +
> + return folio;
> +}
> +
> +static const struct iomap_folio_ops fuse_iomap_folio_ops = {
> + .get_folio = fuse_iomap_get_folio,
> +};
> +
> +static int fuse_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length,
> + unsigned flags, struct iomap *iomap,
> + struct iomap *srcmap)
> +{
> + iomap->type = IOMAP_DELALLOC;
> + iomap->addr = IOMAP_NULL_ADDR;
> + iomap->offset = pos;
> + iomap->length = length;
> + iomap->folio_ops = &fuse_iomap_folio_ops;
> + return 0;
> +}
What's the reason for using IOMAP_DELALLOC for these mappings? I'm
not saying it is wrong, I just don't know enough about fuse to
understand is this is valid or not because there are no iomap-based
writeback hooks being added here....
-Dave.
--
Dave Chinner
david@fromorbit.com
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes
2024-08-29 3:50 ` Dave Chinner
@ 2024-08-29 14:23 ` Josef Bacik
0 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-29 14:23 UTC (permalink / raw)
To: Dave Chinner
Cc: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
On Thu, Aug 29, 2024 at 01:50:59PM +1000, Dave Chinner wrote:
> On Wed, Aug 28, 2024 at 05:13:56PM -0400, Josef Bacik wrote:
> > We're currently using the old ->write_begin()/->write_end() method of
> > doing buffered writes. This isn't a huge deal for fuse since we
> > basically just want to copy the pages and move on, but the iomap
> > infrastructure gives us access to having huge folios. Rework the
> > buffered write path when we have writeback cache to use the iomap
> > buffered write code, the ->get_folio() callback now handles the work
> > that we did in ->write_begin(), the rest of the work is handled inside
> > of iomap so we don't need a replacement for ->write_end.
> >
> > This does bring BLOCK as a dependency, as the buffered write part of
> > iomap requires CONFIG_BLOCK. This could be shed if we reworked the file
> > write iter portion of the buffered write path was separated out to not
> > need BLOCK.
> >
> > Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> > ---
> > fs/fuse/Kconfig | 2 +
> > fs/fuse/file.c | 154 +++++++++++++++++++++---------------------------
> > 2 files changed, 68 insertions(+), 88 deletions(-)
> >
> > diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
> > index 8674dbfbe59d..8a799324d7bd 100644
> > --- a/fs/fuse/Kconfig
> > +++ b/fs/fuse/Kconfig
> > @@ -1,7 +1,9 @@
> > # SPDX-License-Identifier: GPL-2.0-only
> > config FUSE_FS
> > tristate "FUSE (Filesystem in Userspace) support"
> > + depends on BLOCK
> > select FS_POSIX_ACL
> > + select FS_IOMAP
> > help
> > With FUSE it is possible to implement a fully functional filesystem
> > in a userspace program.
> > diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> > index ab531a4694b3..af91043b44d7 100644
> > --- a/fs/fuse/file.c
> > +++ b/fs/fuse/file.c
> > @@ -21,6 +21,7 @@
> > #include <linux/filelock.h>
> > #include <linux/splice.h>
> > #include <linux/task_io_accounting_ops.h>
> > +#include <linux/iomap.h>
> >
> > static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
> > unsigned int open_flags, int opcode,
> > @@ -1420,6 +1421,63 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
> > }
> > }
> >
> > +static struct folio *fuse_iomap_get_folio(struct iomap_iter *iter,
> > + loff_t pos, unsigned len)
> > +{
> > + struct file *file = (struct file *)iter->private;
> > + struct inode *inode = iter->inode;
> > + struct folio *folio;
> > + loff_t fsize;
> > +
> > + folio = iomap_get_folio(iter, pos, len);
> > + if (IS_ERR(folio))
> > + return folio;
> > +
> > + fuse_wait_on_folio_writeback(inode, folio);
> > +
> > + if (folio_test_uptodate(folio))
> > + return folio;
> > +
> > + /*
> > + * If we're going to write past EOF then avoid the read, but zero the
> > + * whole thing and mark it uptodate so that if we get a short write we
> > + * don't try to re-read this page, we just carry on.
> > + */
> > + fsize = i_size_read(inode);
> > + if (fsize <= folio_pos(folio)) {
> > + folio_zero_range(folio, 0, folio_size(folio));
>
> The comment doesn't match what this does - the folio is not marked
> uptodate at all.
I'll update the comment, it gets marked uptodate in __iomap_write_end() once the
write is complete.
>
> > + } else {
> > + int err = fuse_do_readpage(file, &folio->page);
>
> readpage on a large folio? does that work?
I haven't done the work to enable large folios yet, this is just the prep stuff.
Supporting large folios is going to take a fair bit of work, so I'm getting the
ball rolling with this prep series.
>
> > + if (err) {
> > + folio_unlock(folio);
> > + folio_put(folio);
> > + return ERR_PTR(err);
> > + }
> > + }
>
> Also, why do this here when __iomap_write_begin() will do all the
> sub-folio zeroing and read IO on the folio?
>
I looked long and hard at iomap because I thought it would, but it turns out it
won't work for fuse. I could be totally wrong, but looking at iomap it will
allocate an ifs because it assumes this is sub-folio blocksize, but we aren't,
and don't really want to incur that pain. Additionally it does
iomap_read_folio_sync() to read in the folio, which just does a bio, which
obviously doesn't work on fuse. Again totally expecting to be told I'm stupid
in some way that I missed, but it seemed like iomap won't do what we need it to
do here, and it's simple enough to handle the zeroing here for ourselves.
> > +
> > + return folio;
> > +}
> > +
> > +static const struct iomap_folio_ops fuse_iomap_folio_ops = {
> > + .get_folio = fuse_iomap_get_folio,
> > +};
> > +
> > +static int fuse_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length,
> > + unsigned flags, struct iomap *iomap,
> > + struct iomap *srcmap)
> > +{
> > + iomap->type = IOMAP_DELALLOC;
> > + iomap->addr = IOMAP_NULL_ADDR;
> > + iomap->offset = pos;
> > + iomap->length = length;
> > + iomap->folio_ops = &fuse_iomap_folio_ops;
> > + return 0;
> > +}
>
> What's the reason for using IOMAP_DELALLOC for these mappings? I'm
> not saying it is wrong, I just don't know enough about fuse to
> understand is this is valid or not because there are no iomap-based
> writeback hooks being added here....
At the time it was "oh we're doing what equates to delalloc, clearly this should
be marked as delalloc". Now that I have been in this code longer I realize this
is supposed to be "what does this range look like now", so I suppose the "right"
thing to do here is use IOMAP_HOLE? Thanks,
Josef
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes
2024-08-28 21:13 ` [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes Josef Bacik
2024-08-29 3:50 ` Dave Chinner
@ 2024-08-29 23:30 ` Bernd Schubert
1 sibling, 0 replies; 16+ messages in thread
From: Bernd Schubert @ 2024-08-29 23:30 UTC (permalink / raw)
To: Josef Bacik, linux-fsdevel, amir73il, miklos, joannelkoong,
bschubert, willy
On 8/28/24 23:13, Josef Bacik wrote:
> We're currently using the old ->write_begin()/->write_end() method of
> doing buffered writes. This isn't a huge deal for fuse since we
> basically just want to copy the pages and move on, but the iomap
> infrastructure gives us access to having huge folios. Rework the
> buffered write path when we have writeback cache to use the iomap
> buffered write code, the ->get_folio() callback now handles the work
> that we did in ->write_begin(), the rest of the work is handled inside
> of iomap so we don't need a replacement for ->write_end.
>
> This does bring BLOCK as a dependency, as the buffered write part of
> iomap requires CONFIG_BLOCK. This could be shed if we reworked the file
> write iter portion of the buffered write path was separated out to not
> need BLOCK.
>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
> fs/fuse/Kconfig | 2 +
> fs/fuse/file.c | 154 +++++++++++++++++++++---------------------------
> 2 files changed, 68 insertions(+), 88 deletions(-)
>
> diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
> index 8674dbfbe59d..8a799324d7bd 100644
> --- a/fs/fuse/Kconfig
> +++ b/fs/fuse/Kconfig
> @@ -1,7 +1,9 @@
> # SPDX-License-Identifier: GPL-2.0-only
> config FUSE_FS
> tristate "FUSE (Filesystem in Userspace) support"
> + depends on BLOCK
> select FS_POSIX_ACL
> + select FS_IOMAP
> help
> With FUSE it is possible to implement a fully functional filesystem
> in a userspace program.
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index ab531a4694b3..af91043b44d7 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -21,6 +21,7 @@
> #include <linux/filelock.h>
> #include <linux/splice.h>
> #include <linux/task_io_accounting_ops.h>
> +#include <linux/iomap.h>
>
> static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
> unsigned int open_flags, int opcode,
> @@ -1420,6 +1421,63 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
> }
> }
>
> +static struct folio *fuse_iomap_get_folio(struct iomap_iter *iter,
> + loff_t pos, unsigned len)
> +{
> + struct file *file = (struct file *)iter->private;
> + struct inode *inode = iter->inode;
> + struct folio *folio;
> + loff_t fsize;
> +
> + folio = iomap_get_folio(iter, pos, len);
> + if (IS_ERR(folio))
> + return folio;
> +
> + fuse_wait_on_folio_writeback(inode, folio);
> +
> + if (folio_test_uptodate(folio))
> + return folio;
> +
> + /*
> + * If we're going to write past EOF then avoid the read, but zero the
> + * whole thing and mark it uptodate so that if we get a short write we
> + * don't try to re-read this page, we just carry on.
> + */
> + fsize = i_size_read(inode);
> + if (fsize <= folio_pos(folio)) {
> + folio_zero_range(folio, 0, folio_size(folio));
> + } else {
> + int err = fuse_do_readpage(file, &folio->page);
> + if (err) {
> + folio_unlock(folio);
> + folio_put(folio);
> + return ERR_PTR(err);
> + }
> + }
I wonder if we could optimize out the read when len == PAGE_SIZE (or
folio_size(folio)). Maybe not in this series, but later. I see that the
current page code also only acts on the file size, but I don't understand
why a page needs to be read when it gets completely overwritten.
> +
> + return folio;
> +}
> +
> +static const struct iomap_folio_ops fuse_iomap_folio_ops = {
> + .get_folio = fuse_iomap_get_folio,
> +};
> +
> +static int fuse_iomap_begin_write(struct inode *inode, loff_t pos, loff_t length,
> + unsigned flags, struct iomap *iomap,
> + struct iomap *srcmap)
> +{
> + iomap->type = IOMAP_DELALLOC;
> + iomap->addr = IOMAP_NULL_ADDR;
> + iomap->offset = pos;
> + iomap->length = length;
> + iomap->folio_ops = &fuse_iomap_folio_ops;
> + return 0;
> +}
> +
> +static const struct iomap_ops fuse_iomap_write_ops = {
> + .iomap_begin = fuse_iomap_begin_write,
> +};
> +
> static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
> {
> struct file *file = iocb->ki_filp;
> @@ -1428,6 +1486,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
> struct inode *inode = mapping->host;
> ssize_t err, count;
> struct fuse_conn *fc = get_fuse_conn(inode);
> + bool writethrough = (fc->writeback_cache == 0);
>
> if (fc->writeback_cache) {
> /* Update size (EOF optimization) and mode (SUID clearing) */
> @@ -1438,14 +1497,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
>
> if (fc->handle_killpriv_v2 &&
> setattr_should_drop_suidgid(&nop_mnt_idmap,
> - file_inode(file))) {
> - goto writethrough;
> - }
> -
> - return generic_file_write_iter(iocb, from);
> + file_inode(file)))
> + writethrough = true;
> }
>
> -writethrough:
> inode_lock(inode);
>
> err = count = generic_write_checks(iocb, from);
> @@ -1464,8 +1519,12 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
> goto out;
> written = direct_write_fallback(iocb, from, written,
> fuse_perform_write(iocb, from));
> - } else {
> + } else if (writethrough) {
> written = fuse_perform_write(iocb, from);
> + } else {
> + written = iomap_file_buffered_write(iocb, from,
> + &fuse_iomap_write_ops,
> + file);
> }
> out:
> inode_unlock(inode);
> @@ -2408,85 +2467,6 @@ static int fuse_writepages(struct address_space *mapping,
> return err;
> }
>
> -/*
> - * It's worthy to make sure that space is reserved on disk for the write,
> - * but how to implement it without killing performance need more thinking.
> - */
> -static int fuse_write_begin(struct file *file, struct address_space *mapping,
> - loff_t pos, unsigned len, struct page **pagep, void **fsdata)
> -{
> - pgoff_t index = pos >> PAGE_SHIFT;
> - struct fuse_conn *fc = get_fuse_conn(file_inode(file));
> - struct page *page;
> - loff_t fsize;
> - int err = -ENOMEM;
> -
> - WARN_ON(!fc->writeback_cache);
> -
> - page = grab_cache_page_write_begin(mapping, index);
> - if (!page)
> - goto error;
> -
> - fuse_wait_on_page_writeback(mapping->host, page->index);
> -
> - if (PageUptodate(page) || len == PAGE_SIZE)
> - goto success;
> - /*
> - * Check if the start this page comes after the end of file, in which
> - * case the readpage can be optimized away.
> - */
> - fsize = i_size_read(mapping->host);
> - if (fsize <= (pos & PAGE_MASK)) {
> - size_t off = pos & ~PAGE_MASK;
> - if (off)
> - zero_user_segment(page, 0, off);
> - goto success;
> - }
> - err = fuse_do_readpage(file, page);
> - if (err)
> - goto cleanup;
I mean here, I _think_ it could have additionally checked for
len == PAGE_SIZE.
> -success:
> - *pagep = page;
> - return 0;
> -
> -cleanup:
> - unlock_page(page);
> - put_page(page);
> -error:
> - return err;
> -}
> -
> -static int fuse_write_end(struct file *file, struct address_space *mapping,
> - loff_t pos, unsigned len, unsigned copied,
> - struct page *page, void *fsdata)
> -{
> - struct inode *inode = page->mapping->host;
> -
> - /* Haven't copied anything? Skip zeroing, size extending, dirtying. */
> - if (!copied)
> - goto unlock;
> -
> - pos += copied;
> - if (!PageUptodate(page)) {
> - /* Zero any unwritten bytes at the end of the page */
> - size_t endoff = pos & ~PAGE_MASK;
> - if (endoff)
> - zero_user_segment(page, endoff, PAGE_SIZE);
> - SetPageUptodate(page);
> - }
> -
> - if (pos > inode->i_size)
> - i_size_write(inode, pos);
> -
> - set_page_dirty(page);
> -
> -unlock:
> - unlock_page(page);
> - put_page(page);
> -
> - return copied;
> -}
> -
> static int fuse_launder_folio(struct folio *folio)
> {
> int err = 0;
> @@ -3352,8 +3332,6 @@ static const struct address_space_operations fuse_file_aops = {
> .migrate_folio = filemap_migrate_folio,
> .bmap = fuse_bmap,
> .direct_IO = fuse_direct_IO,
> - .write_begin = fuse_write_begin,
> - .write_end = fuse_write_end,
> };
>
> void fuse_init_file_inode(struct inode *inode, unsigned int flags)
Thanks,
Bernd
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 07/11] fuse: convert fuse_do_readpage to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (5 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 06/11] fuse: use iomap for writeback cache buffered writes Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 08/11] fuse: convert fuse_writepage_need_send to take a folio Josef Bacik
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
Now that the buffered write path is using folios, convert
fuse_do_readpage() to take a folio instead of a page, update it to use
the appropriate folio helpers, and update the callers to pass in the
folio directly instead of a page.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index af91043b44d7..7b44efbe9653 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -859,12 +859,13 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
}
}
-static int fuse_do_readpage(struct file *file, struct page *page)
+static int fuse_do_readpage(struct file *file, struct folio *folio)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
struct fuse_mount *fm = get_fuse_mount(inode);
- loff_t pos = page_offset(page);
+ loff_t pos = folio_pos(folio);
struct fuse_page_desc desc = { .length = PAGE_SIZE };
+ struct page *page = &folio->page;
struct fuse_io_args ia = {
.ap.args.page_zeroing = true,
.ap.args.out_pages = true,
@@ -876,11 +877,10 @@ static int fuse_do_readpage(struct file *file, struct page *page)
u64 attr_ver;
/*
- * Page writeback can extend beyond the lifetime of the
- * page-cache page, so make sure we read a properly synced
- * page.
+ * Folio writeback can extend beyond the lifetime of the
+ * folio, so make sure we read a properly synced folio.
*/
- fuse_wait_on_page_writeback(inode, page->index);
+ fuse_wait_on_folio_writeback(inode, folio);
attr_ver = fuse_get_attr_version(fm->fc);
@@ -898,25 +898,24 @@ static int fuse_do_readpage(struct file *file, struct page *page)
if (res < desc.length)
fuse_short_read(inode, attr_ver, res, &ia.ap);
- SetPageUptodate(page);
+ folio_mark_uptodate(folio);
return 0;
}
static int fuse_read_folio(struct file *file, struct folio *folio)
{
- struct page *page = &folio->page;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = folio->mapping->host;
int err;
err = -EIO;
if (fuse_is_bad(inode))
goto out;
- err = fuse_do_readpage(file, page);
+ err = fuse_do_readpage(file, folio);
fuse_invalidate_atime(inode);
out:
- unlock_page(page);
+ folio_unlock(folio);
return err;
}
@@ -1447,7 +1446,7 @@ static struct folio *fuse_iomap_get_folio(struct iomap_iter *iter,
if (fsize <= folio_pos(folio)) {
folio_zero_range(folio, 0, folio_size(folio));
} else {
- int err = fuse_do_readpage(file, &folio->page);
+ int err = fuse_do_readpage(file, folio);
if (err) {
folio_unlock(folio);
folio_put(folio);
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 08/11] fuse: convert fuse_writepage_need_send to take a folio
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (6 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 07/11] fuse: convert fuse_do_readpage to use folios Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:13 ` [PATCH v2 09/11] fuse: use the folio based vmstat helpers Josef Bacik
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
fuse_writepage_need_send is called by fuse_writepages_fill() which
already has a folio. Change fuse_writepage_need_send() to take a folio
instead, add a helper to check if the folio range is under writeback and
use this, as well as the appropriate folio helpers in the rest of the
function. Update fuse_writepage_need_send() to pass in the folio
directly.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 7b44efbe9653..8cbfdf4ba136 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -484,14 +484,19 @@ static void fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index));
}
+static inline bool fuse_folio_is_writeback(struct inode *inode,
+ struct folio *folio)
+{
+ pgoff_t last = folio_next_index(folio) - 1;
+ return fuse_range_is_writeback(inode, folio_index(folio), last);
+}
+
static void fuse_wait_on_folio_writeback(struct inode *inode,
struct folio *folio)
{
struct fuse_inode *fi = get_fuse_inode(inode);
- pgoff_t last = folio_next_index(folio) - 1;
- wait_event(fi->page_waitq,
- !fuse_range_is_writeback(inode, folio_index(folio), last));
+ wait_event(fi->page_waitq, !fuse_folio_is_writeback(inode, folio));
}
/*
@@ -2320,7 +2325,7 @@ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
return false;
}
-static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page,
+static bool fuse_writepage_need_send(struct fuse_conn *fc, struct folio *folio,
struct fuse_args_pages *ap,
struct fuse_fill_wb_data *data)
{
@@ -2332,7 +2337,7 @@ static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page,
* the pages are faulted with get_user_pages(), and then after the read
* completed.
*/
- if (fuse_page_is_writeback(data->inode, page->index))
+ if (fuse_folio_is_writeback(data->inode, folio))
return true;
/* Reached max pages */
@@ -2344,7 +2349,7 @@ static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page,
return true;
/* Discontinuity */
- if (data->orig_pages[ap->num_pages - 1]->index + 1 != page->index)
+ if (data->orig_pages[ap->num_pages - 1]->index + 1 != folio_index(folio))
return true;
/* Need to grow the pages array? If so, did the expansion fail? */
@@ -2366,7 +2371,7 @@ static int fuse_writepages_fill(struct folio *folio,
struct folio *tmp_folio;
int err;
- if (wpa && fuse_writepage_need_send(fc, &folio->page, ap, data)) {
+ if (wpa && fuse_writepage_need_send(fc, folio, ap, data)) {
fuse_writepages_send(data);
data->wpa = NULL;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 09/11] fuse: use the folio based vmstat helpers
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (7 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 08/11] fuse: convert fuse_writepage_need_send to take a folio Josef Bacik
@ 2024-08-28 21:13 ` Josef Bacik
2024-08-28 21:14 ` [PATCH v2 10/11] fuse: convert fuse_retrieve to use folios Josef Bacik
2024-08-28 21:14 ` [PATCH v2 11/11] fuse: convert fuse_notify_store " Josef Bacik
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:13 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
In order to make it easier to switch to folios in the fuse_args_pages
update the places where we update the vmstat counters for writeback to
use the folio related helpers. On the inc side this is easy as we
already have the folio, on the dec side we have to page_folio() the
pages for now.
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/file.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 8cbfdf4ba136..9556cd31039a 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1852,12 +1852,12 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa)
kfree(wpa);
}
-static void fuse_writepage_finish_stat(struct inode *inode, struct page *page)
+static void fuse_writepage_finish_stat(struct inode *inode, struct folio *folio)
{
struct backing_dev_info *bdi = inode_to_bdi(inode);
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
- dec_node_page_state(page, NR_WRITEBACK_TEMP);
+ node_stat_sub_folio(folio, NR_WRITEBACK_TEMP);
wb_writeout_inc(&bdi->wb);
}
@@ -1869,7 +1869,7 @@ static void fuse_writepage_finish(struct fuse_writepage_args *wpa)
int i;
for (i = 0; i < ap->num_pages; i++)
- fuse_writepage_finish_stat(inode, ap->pages[i]);
+ fuse_writepage_finish_stat(inode, page_folio(ap->pages[i]));
wake_up(&fi->page_waitq);
}
@@ -1924,7 +1924,8 @@ __acquires(fi->lock)
for (aux = wpa->next; aux; aux = next) {
next = aux->next;
aux->next = NULL;
- fuse_writepage_finish_stat(aux->inode, aux->ia.ap.pages[0]);
+ fuse_writepage_finish_stat(aux->inode,
+ page_folio(aux->ia.ap.pages[0]));
fuse_writepage_free(aux);
}
@@ -2144,7 +2145,7 @@ static void fuse_writepage_args_page_fill(struct fuse_writepage_args *wpa, struc
ap->descs[page_index].length = PAGE_SIZE;
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
- inc_node_page_state(&tmp_folio->page, NR_WRITEBACK_TEMP);
+ node_stat_add_folio(tmp_folio, NR_WRITEBACK_TEMP);
}
static struct fuse_writepage_args *fuse_writepage_args_setup(struct folio *folio,
@@ -2318,7 +2319,8 @@ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
spin_unlock(&fi->lock);
if (tmp) {
- fuse_writepage_finish_stat(new_wpa->inode, new_ap->pages[0]);
+ fuse_writepage_finish_stat(new_wpa->inode,
+ page_folio(new_ap->pages[0]));
fuse_writepage_free(new_wpa);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 10/11] fuse: convert fuse_retrieve to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (8 preceding siblings ...)
2024-08-28 21:13 ` [PATCH v2 09/11] fuse: use the folio based vmstat helpers Josef Bacik
@ 2024-08-28 21:14 ` Josef Bacik
2024-08-28 21:14 ` [PATCH v2 11/11] fuse: convert fuse_notify_store " Josef Bacik
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:14 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
We're just looking for pages in a mapping, use a folio and the folio
lookup function directly instead of using the page helper.
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/dev.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 7146038b2fe7..bcce75e07678 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1709,15 +1709,15 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
index = outarg->offset >> PAGE_SHIFT;
while (num && ap->num_pages < num_pages) {
- struct page *page;
+ struct folio *folio;
unsigned int this_num;
- page = find_get_page(mapping, index);
- if (!page)
+ folio = __filemap_get_folio(mapping, index, 0, 0);
+ if (IS_ERR(folio))
break;
this_num = min_t(unsigned, num, PAGE_SIZE - offset);
- ap->pages[ap->num_pages] = page;
+ ap->pages[ap->num_pages] = &folio->page;
ap->descs[ap->num_pages].offset = offset;
ap->descs[ap->num_pages].length = this_num;
ap->num_pages++;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 11/11] fuse: convert fuse_notify_store to use folios
2024-08-28 21:13 [PATCH v2 00/11] fuse: convert to using folios and iomap Josef Bacik
` (9 preceding siblings ...)
2024-08-28 21:14 ` [PATCH v2 10/11] fuse: convert fuse_retrieve to use folios Josef Bacik
@ 2024-08-28 21:14 ` Josef Bacik
10 siblings, 0 replies; 16+ messages in thread
From: Josef Bacik @ 2024-08-28 21:14 UTC (permalink / raw)
To: linux-fsdevel, amir73il, miklos, joannelkoong, bschubert, willy
This function creates pages in an inode and copies data into them,
update the function to use a folio instead of a page, and use the
appropriate folio helpers.
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
fs/fuse/dev.c | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index bcce75e07678..eeb5cea4b7e4 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1607,24 +1607,28 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
num = outarg.size;
while (num) {
+ struct folio *folio;
struct page *page;
unsigned int this_num;
- err = -ENOMEM;
- page = find_or_create_page(mapping, index,
- mapping_gfp_mask(mapping));
- if (!page)
+ folio = __filemap_get_folio(mapping, index,
+ FGP_LOCK|FGP_ACCESSED|FGP_CREAT,
+ mapping_gfp_mask(mapping));
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
goto out_iput;
-
- this_num = min_t(unsigned, num, PAGE_SIZE - offset);
- err = fuse_copy_page(cs, &page, offset, this_num, 0);
- if (!PageUptodate(page) && !err && offset == 0 &&
- (this_num == PAGE_SIZE || file_size == end)) {
- zero_user_segment(page, this_num, PAGE_SIZE);
- SetPageUptodate(page);
}
- unlock_page(page);
- put_page(page);
+
+ page = &folio->page;
+ this_num = min_t(unsigned, num, folio_size(folio) - offset);
+ err = fuse_copy_page(cs, &page, offset, this_num, 0);
+ if (!folio_test_uptodate(folio) && !err && offset == 0 &&
+ (this_num == folio_size(folio) || file_size == end)) {
+ folio_zero_range(folio, this_num, folio_size(folio));
+ folio_mark_uptodate(folio);
+ }
+ folio_unlock(folio);
+ folio_put(folio);
if (err)
goto out_iput;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread