bcachefs: Always touch page state with page locked
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 2 Jul 2019 18:59:15 +0000 (14:59 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:23 +0000 (17:08 -0400)
This will mean we don't have to use cmpxchg for modifying page state,
which will simplify a fair amount of code

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fs-io.c
fs/bcachefs/fs-io.h
fs/bcachefs/fs.c

index 54b071b9ca2c520cc4cfe5d6e9c549bb54731536..bf03048252ec117818e33bcb44e1c6cba82d8978 100644 (file)
@@ -500,11 +500,6 @@ static inline struct bch_io_opts io_opts(struct bch_fs *c, struct bch_inode_info
 
 /* stored in page->private: */
 
-/*
- * bch_page_state has to (unfortunately) be manipulated with cmpxchg - we could
- * almost protected it with the page lock, except that bch2_writepage_io_done has
- * to update the sector counts (and from interrupt/bottom half context).
- */
 struct bch_page_state {
 union { struct {
        /* existing data: */
@@ -550,6 +545,7 @@ static inline struct bch_page_state *page_state(struct page *page)
 {
        struct bch_page_state *s = (void *) &page->private;
 
+       EBUG_ON(!PageLocked(page));
        BUILD_BUG_ON(sizeof(*s) > sizeof(page->private));
 
        if (!PagePrivate(page))
@@ -589,15 +585,20 @@ static void bch2_put_page_reservation(struct bch_fs *c, struct bch_inode_info *i
        __bch2_put_page_reservation(c, inode, s);
 }
 
+static inline unsigned inode_nr_replicas(struct bch_fs *c, struct bch_inode_info *inode)
+{
+       /* XXX: this should not be open coded */
+       return inode->ei_inode.bi_data_replicas
+               ? inode->ei_inode.bi_data_replicas - 1
+               : c->opts.data_replicas;
+}
+
 static int bch2_get_page_reservation(struct bch_fs *c, struct bch_inode_info *inode,
                                     struct page *page, bool check_enospc)
 {
        struct bch_page_state *s = page_state(page), new;
 
-       /* XXX: this should not be open coded */
-       unsigned nr_replicas = inode->ei_inode.bi_data_replicas
-               ? inode->ei_inode.bi_data_replicas - 1
-               : c->opts.data_replicas;
+       unsigned nr_replicas = inode_nr_replicas(c, inode);
        struct disk_reservation disk_res;
        struct quota_res quota_res = { 0 };
        int ret;
@@ -655,7 +656,7 @@ static void bch2_clear_page_bits(struct page *page)
        __bch2_put_page_reservation(c, inode, s);
 }
 
-bool bch2_dirty_folio(struct address_space *mapping, struct folio *folio)
+static void __bch2_set_page_dirty(struct address_space *mapping, struct folio *folio)
 {
        struct bch_inode_info *inode = to_bch_ei(mapping->host);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
@@ -673,8 +674,14 @@ bool bch2_dirty_folio(struct address_space *mapping, struct folio *folio)
                i_sectors_acct(c, inode, &quota_res,
                               new.dirty_sectors - old.dirty_sectors);
        bch2_quota_reservation_put(c, inode, &quota_res);
+}
+
+static void bch2_set_page_dirty(struct address_space *mapping, struct page *page)
+{
+       struct folio *folio = page_folio(page);
 
-       return filemap_dirty_folio(mapping, folio);
+       __bch2_set_page_dirty(mapping, folio);
+       filemap_dirty_folio(mapping, folio);
 }
 
 vm_fault_t bch2_page_fault(struct vm_fault *vmf)
@@ -725,7 +732,7 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf)
        }
 
        if (!PageDirty(page))
-               set_page_dirty(page);
+               bch2_set_page_dirty(mapping, page);
        wait_for_stable_page(page);
 out:
        bch2_pagecache_add_put(&inode->ei_pagecache_lock);
@@ -1210,10 +1217,12 @@ static int __bch2_writepage(struct folio *folio,
        struct bch_inode_info *inode = to_bch_ei(page->mapping->host);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct bch_writepage_state *w = data;
-       struct bch_page_state new, old;
+       struct bch_page_state *s;
        unsigned offset, nr_replicas_this_write;
+       unsigned dirty_sectors, replicas_reserved;
        loff_t i_size = i_size_read(&inode->v);
        pgoff_t end_index = i_size >> PAGE_SHIFT;
+       int ret;
 
        EBUG_ON(!PageUptodate(page));
 
@@ -1237,33 +1246,37 @@ static int __bch2_writepage(struct folio *folio,
         */
        zero_user_segment(page, offset, PAGE_SIZE);
 do_io:
-       EBUG_ON(!PageLocked(page));
+       s = page_state(page);
 
-       /* Before unlocking the page, transfer reservation to w->io: */
-       old = page_state_cmpxchg(page_state(page), new, {
-               /*
-                * If we didn't get a reservation, we can only write out the
-                * number of (fully allocated) replicas that currently exist,
-                * and only if the entire page has been written:
-                */
-               nr_replicas_this_write =
-                       max_t(unsigned,
-                             new.replicas_reserved,
-                             (new.sectors == PAGE_SECTORS
-                              ? new.nr_replicas : 0));
+       ret = bch2_get_page_reservation(c, inode, page, true);
+       if (ret) {
+               SetPageError(page);
+               mapping_set_error(page->mapping, ret);
+               unlock_page(page);
+               return 0;
+       }
 
-               BUG_ON(!nr_replicas_this_write);
+       __bch2_set_page_dirty(page->mapping, page_folio(page));
 
-               new.nr_replicas = w->opts.compression
-                       ? 0
-                       : nr_replicas_this_write;
+       nr_replicas_this_write =
+               max_t(unsigned,
+                     s->replicas_reserved,
+                     (s->sectors == PAGE_SECTORS
+                      ? s->nr_replicas : 0));
 
-               new.replicas_reserved = 0;
+       s->nr_replicas = w->opts.compression
+               ? 0
+               : nr_replicas_this_write;
 
-               new.sectors += new.dirty_sectors;
-               BUG_ON(new.sectors != PAGE_SECTORS);
-               new.dirty_sectors = 0;
-       });
+       /* Before unlocking the page, transfer reservation to w->io: */
+       replicas_reserved = s->replicas_reserved;
+       s->replicas_reserved = 0;
+
+       dirty_sectors = s->dirty_sectors;
+       s->dirty_sectors = 0;
+
+       s->sectors += dirty_sectors;
+       BUG_ON(s->sectors != PAGE_SECTORS);
 
        BUG_ON(PageWriteback(page));
        set_page_writeback(page);
@@ -1278,12 +1291,12 @@ do_io:
                bch2_writepage_io_alloc(c, w, inode, page,
                                        nr_replicas_this_write);
 
-       w->io->new_sectors += new.sectors - old.sectors;
+       w->io->new_sectors += dirty_sectors;
 
        BUG_ON(inode != w->io->op.inode);
        BUG_ON(bio_add_page_contig(&w->io->op.op.wbio.bio, page));
 
-       w->io->op.op.res.sectors += old.replicas_reserved * PAGE_SECTORS;
+       w->io->op.op.res.sectors += replicas_reserved * PAGE_SECTORS;
        w->io->op.new_i_size = i_size;
 
        if (wbc->sync_mode == WB_SYNC_ALL)
@@ -1421,7 +1434,7 @@ int bch2_write_end(struct file *file, struct address_space *mapping,
                if (!PageUptodate(page))
                        SetPageUptodate(page);
                if (!PageDirty(page))
-                       set_page_dirty(page);
+                       bch2_set_page_dirty(mapping, page);
 
                inode->ei_last_dirtied = (unsigned long) current;
        } else {
@@ -1538,7 +1551,7 @@ out:
                if (!PageUptodate(pages[i]))
                        SetPageUptodate(pages[i]);
                if (!PageDirty(pages[i]))
-                       set_page_dirty(pages[i]);
+                       bch2_set_page_dirty(mapping, pages[i]);
                unlock_page(pages[i]);
                put_page(pages[i]);
        }
@@ -2212,7 +2225,7 @@ static int __bch2_truncate_page(struct bch_inode_info *inode,
                zero_user_segment(page, 0, end_offset);
 
        if (!PageDirty(page))
-               set_page_dirty(page);
+               bch2_set_page_dirty(mapping, page);
 unlock:
        unlock_page(page);
        put_page(page);
index 2e4bfee877d98523bacce5c20ab61fd953cbecbf..e263b515e9019046709454d5cd7f6f105920faec 100644 (file)
@@ -9,8 +9,6 @@
 
 #include <linux/uio.h>
 
-bool bch2_dirty_folio(struct address_space *, struct folio *);
-
 int bch2_writepage(struct page *, struct writeback_control *);
 int bch2_read_folio(struct file *, struct folio *);
 
index c806ebad9cde5ef2743eaf7804ebf245d54b270c..f69b535b1b82ec9a37db6a51a2974d4bc4292ec8 100644 (file)
@@ -1349,7 +1349,7 @@ static const struct address_space_operations bch_address_space_operations = {
        .read_folio     = bch2_read_folio,
        .writepages     = bch2_writepages,
        .readahead      = bch2_readahead,
-       .dirty_folio    = bch2_dirty_folio,
+       .dirty_folio    = filemap_dirty_folio,
        .write_begin    = bch2_write_begin,
        .write_end      = bch2_write_end,
        .invalidate_folio = bch2_invalidate_folio,