Commit e1036a2a authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

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: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent a6f4d5bb
...@@ -500,11 +500,6 @@ static inline struct bch_io_opts io_opts(struct bch_fs *c, struct bch_inode_info ...@@ -500,11 +500,6 @@ static inline struct bch_io_opts io_opts(struct bch_fs *c, struct bch_inode_info
/* stored in page->private: */ /* 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 { struct bch_page_state {
union { struct { union { struct {
/* existing data: */ /* existing data: */
...@@ -550,6 +545,7 @@ static inline struct bch_page_state *page_state(struct page *page) ...@@ -550,6 +545,7 @@ static inline struct bch_page_state *page_state(struct page *page)
{ {
struct bch_page_state *s = (void *) &page->private; struct bch_page_state *s = (void *) &page->private;
EBUG_ON(!PageLocked(page));
BUILD_BUG_ON(sizeof(*s) > sizeof(page->private)); BUILD_BUG_ON(sizeof(*s) > sizeof(page->private));
if (!PagePrivate(page)) if (!PagePrivate(page))
...@@ -589,15 +585,20 @@ static void bch2_put_page_reservation(struct bch_fs *c, struct bch_inode_info *i ...@@ -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); __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, static int bch2_get_page_reservation(struct bch_fs *c, struct bch_inode_info *inode,
struct page *page, bool check_enospc) struct page *page, bool check_enospc)
{ {
struct bch_page_state *s = page_state(page), new; struct bch_page_state *s = page_state(page), new;
/* XXX: this should not be open coded */ unsigned nr_replicas = inode_nr_replicas(c, inode);
unsigned nr_replicas = inode->ei_inode.bi_data_replicas
? inode->ei_inode.bi_data_replicas - 1
: c->opts.data_replicas;
struct disk_reservation disk_res; struct disk_reservation disk_res;
struct quota_res quota_res = { 0 }; struct quota_res quota_res = { 0 };
int ret; int ret;
...@@ -655,7 +656,7 @@ static void bch2_clear_page_bits(struct page *page) ...@@ -655,7 +656,7 @@ static void bch2_clear_page_bits(struct page *page)
__bch2_put_page_reservation(c, inode, s); __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_inode_info *inode = to_bch_ei(mapping->host);
struct bch_fs *c = inode->v.i_sb->s_fs_info; 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) ...@@ -673,8 +674,14 @@ bool bch2_dirty_folio(struct address_space *mapping, struct folio *folio)
i_sectors_acct(c, inode, &quota_res, i_sectors_acct(c, inode, &quota_res,
new.dirty_sectors - old.dirty_sectors); new.dirty_sectors - old.dirty_sectors);
bch2_quota_reservation_put(c, inode, &quota_res); 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) vm_fault_t bch2_page_fault(struct vm_fault *vmf)
...@@ -725,7 +732,7 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf) ...@@ -725,7 +732,7 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf)
} }
if (!PageDirty(page)) if (!PageDirty(page))
set_page_dirty(page); bch2_set_page_dirty(mapping, page);
wait_for_stable_page(page); wait_for_stable_page(page);
out: out:
bch2_pagecache_add_put(&inode->ei_pagecache_lock); bch2_pagecache_add_put(&inode->ei_pagecache_lock);
...@@ -1210,10 +1217,12 @@ static int __bch2_writepage(struct folio *folio, ...@@ -1210,10 +1217,12 @@ static int __bch2_writepage(struct folio *folio,
struct bch_inode_info *inode = to_bch_ei(page->mapping->host); struct bch_inode_info *inode = to_bch_ei(page->mapping->host);
struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct bch_writepage_state *w = data; struct bch_writepage_state *w = data;
struct bch_page_state new, old; struct bch_page_state *s;
unsigned offset, nr_replicas_this_write; unsigned offset, nr_replicas_this_write;
unsigned dirty_sectors, replicas_reserved;
loff_t i_size = i_size_read(&inode->v); loff_t i_size = i_size_read(&inode->v);
pgoff_t end_index = i_size >> PAGE_SHIFT; pgoff_t end_index = i_size >> PAGE_SHIFT;
int ret;
EBUG_ON(!PageUptodate(page)); EBUG_ON(!PageUptodate(page));
...@@ -1237,33 +1246,37 @@ static int __bch2_writepage(struct folio *folio, ...@@ -1237,33 +1246,37 @@ static int __bch2_writepage(struct folio *folio,
*/ */
zero_user_segment(page, offset, PAGE_SIZE); zero_user_segment(page, offset, PAGE_SIZE);
do_io: do_io:
EBUG_ON(!PageLocked(page)); s = page_state(page);
/* Before unlocking the page, transfer reservation to w->io: */ ret = bch2_get_page_reservation(c, inode, page, true);
old = page_state_cmpxchg(page_state(page), new, { if (ret) {
/* SetPageError(page);
* If we didn't get a reservation, we can only write out the mapping_set_error(page->mapping, ret);
* number of (fully allocated) replicas that currently exist, unlock_page(page);
* and only if the entire page has been written: return 0;
*/ }
nr_replicas_this_write =
max_t(unsigned,
new.replicas_reserved,
(new.sectors == PAGE_SECTORS
? new.nr_replicas : 0));
BUG_ON(!nr_replicas_this_write); __bch2_set_page_dirty(page->mapping, page_folio(page));
new.nr_replicas = w->opts.compression nr_replicas_this_write =
? 0 max_t(unsigned,
: nr_replicas_this_write; 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; /* Before unlocking the page, transfer reservation to w->io: */
BUG_ON(new.sectors != PAGE_SECTORS); replicas_reserved = s->replicas_reserved;
new.dirty_sectors = 0; 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)); BUG_ON(PageWriteback(page));
set_page_writeback(page); set_page_writeback(page);
...@@ -1278,12 +1291,12 @@ static int __bch2_writepage(struct folio *folio, ...@@ -1278,12 +1291,12 @@ static int __bch2_writepage(struct folio *folio,
bch2_writepage_io_alloc(c, w, inode, page, bch2_writepage_io_alloc(c, w, inode, page,
nr_replicas_this_write); 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(inode != w->io->op.inode);
BUG_ON(bio_add_page_contig(&w->io->op.op.wbio.bio, page)); 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; w->io->op.new_i_size = i_size;
if (wbc->sync_mode == WB_SYNC_ALL) if (wbc->sync_mode == WB_SYNC_ALL)
...@@ -1421,7 +1434,7 @@ int bch2_write_end(struct file *file, struct address_space *mapping, ...@@ -1421,7 +1434,7 @@ int bch2_write_end(struct file *file, struct address_space *mapping,
if (!PageUptodate(page)) if (!PageUptodate(page))
SetPageUptodate(page); SetPageUptodate(page);
if (!PageDirty(page)) if (!PageDirty(page))
set_page_dirty(page); bch2_set_page_dirty(mapping, page);
inode->ei_last_dirtied = (unsigned long) current; inode->ei_last_dirtied = (unsigned long) current;
} else { } else {
...@@ -1538,7 +1551,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode, ...@@ -1538,7 +1551,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
if (!PageUptodate(pages[i])) if (!PageUptodate(pages[i]))
SetPageUptodate(pages[i]); SetPageUptodate(pages[i]);
if (!PageDirty(pages[i])) if (!PageDirty(pages[i]))
set_page_dirty(pages[i]); bch2_set_page_dirty(mapping, pages[i]);
unlock_page(pages[i]); unlock_page(pages[i]);
put_page(pages[i]); put_page(pages[i]);
} }
...@@ -2212,7 +2225,7 @@ static int __bch2_truncate_page(struct bch_inode_info *inode, ...@@ -2212,7 +2225,7 @@ static int __bch2_truncate_page(struct bch_inode_info *inode,
zero_user_segment(page, 0, end_offset); zero_user_segment(page, 0, end_offset);
if (!PageDirty(page)) if (!PageDirty(page))
set_page_dirty(page); bch2_set_page_dirty(mapping, page);
unlock: unlock:
unlock_page(page); unlock_page(page);
put_page(page); put_page(page);
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
#include <linux/uio.h> #include <linux/uio.h>
bool bch2_dirty_folio(struct address_space *, struct folio *);
int bch2_writepage(struct page *, struct writeback_control *); int bch2_writepage(struct page *, struct writeback_control *);
int bch2_read_folio(struct file *, struct folio *); int bch2_read_folio(struct file *, struct folio *);
......
...@@ -1349,7 +1349,7 @@ static const struct address_space_operations bch_address_space_operations = { ...@@ -1349,7 +1349,7 @@ static const struct address_space_operations bch_address_space_operations = {
.read_folio = bch2_read_folio, .read_folio = bch2_read_folio,
.writepages = bch2_writepages, .writepages = bch2_writepages,
.readahead = bch2_readahead, .readahead = bch2_readahead,
.dirty_folio = bch2_dirty_folio, .dirty_folio = filemap_dirty_folio,
.write_begin = bch2_write_begin, .write_begin = bch2_write_begin,
.write_end = bch2_write_end, .write_end = bch2_write_end,
.invalidate_folio = bch2_invalidate_folio, .invalidate_folio = bch2_invalidate_folio,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment