From: Kent Overstreet Date: Tue, 2 Jul 2019 18:59:15 +0000 (-0400) Subject: bcachefs: Always touch page state with page locked X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=e1036a2a718f2cbd082568c881f677cf5fd9f442;p=linux.git bcachefs: Always touch page state with page locked 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 --- diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index 54b071b9ca2c5..bf03048252ec1 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -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, "a_res, new.dirty_sectors - old.dirty_sectors); bch2_quota_reservation_put(c, inode, "a_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); diff --git a/fs/bcachefs/fs-io.h b/fs/bcachefs/fs-io.h index 2e4bfee877d98..e263b515e9019 100644 --- a/fs/bcachefs/fs-io.h +++ b/fs/bcachefs/fs-io.h @@ -9,8 +9,6 @@ #include -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 *); diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index c806ebad9cde5..f69b535b1b82e 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -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,