mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-17 10:46:33 +00:00
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 <kent.overstreet@linux.dev>
This commit is contained in:
parent
a6f4d5bb24
commit
e1036a2a71
@ -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)
|
|||||||
{
|
{
|
||||||
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
|
|||||||
__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)
|
|||||||
__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)
|
|||||||
i_sectors_acct(c, inode, "a_res,
|
i_sectors_acct(c, inode, "a_res,
|
||||||
new.dirty_sectors - old.dirty_sectors);
|
new.dirty_sectors - old.dirty_sectors);
|
||||||
bch2_quota_reservation_put(c, inode, "a_res);
|
bch2_quota_reservation_put(c, inode, "a_res);
|
||||||
|
}
|
||||||
|
|
||||||
return filemap_dirty_folio(mapping, folio);
|
static void bch2_set_page_dirty(struct address_space *mapping, struct page *page)
|
||||||
|
{
|
||||||
|
struct folio *folio = page_folio(page);
|
||||||
|
|
||||||
|
__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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
|
ret = bch2_get_page_reservation(c, inode, page, true);
|
||||||
|
if (ret) {
|
||||||
|
SetPageError(page);
|
||||||
|
mapping_set_error(page->mapping, ret);
|
||||||
|
unlock_page(page);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__bch2_set_page_dirty(page->mapping, page_folio(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 =
|
nr_replicas_this_write =
|
||||||
max_t(unsigned,
|
max_t(unsigned,
|
||||||
new.replicas_reserved,
|
s->replicas_reserved,
|
||||||
(new.sectors == PAGE_SECTORS
|
(s->sectors == PAGE_SECTORS
|
||||||
? new.nr_replicas : 0));
|
? s->nr_replicas : 0));
|
||||||
|
|
||||||
BUG_ON(!nr_replicas_this_write);
|
s->nr_replicas = w->opts.compression
|
||||||
|
|
||||||
new.nr_replicas = w->opts.compression
|
|
||||||
? 0
|
? 0
|
||||||
: nr_replicas_this_write;
|
: nr_replicas_this_write;
|
||||||
|
|
||||||
new.replicas_reserved = 0;
|
/* Before unlocking the page, transfer reservation to w->io: */
|
||||||
|
replicas_reserved = s->replicas_reserved;
|
||||||
|
s->replicas_reserved = 0;
|
||||||
|
|
||||||
new.sectors += new.dirty_sectors;
|
dirty_sectors = s->dirty_sectors;
|
||||||
BUG_ON(new.sectors != PAGE_SECTORS);
|
s->dirty_sectors = 0;
|
||||||
new.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 @@ do_io:
|
|||||||
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,
|
|||||||
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 @@ out:
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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 = {
|
|||||||
.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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user