diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2016-04-16 17:25:14 -0800 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2016-10-07 12:36:13 -0800 |
commit | 046380d08452eeb7e5d831c32128b786e238d5da (patch) | |
tree | 0467ba8095d4d6995bd0ea9380436b2cea4ce84b | |
parent | 9a1fd669bf6113fc078578054b9219b7962dfc92 (diff) |
bcachefs: page state improvements
-rw-r--r-- | drivers/md/bcache/fs-io.c | 136 | ||||
-rw-r--r-- | drivers/md/bcache/fs-io.h | 2 |
2 files changed, 77 insertions, 61 deletions
diff --git a/drivers/md/bcache/fs-io.c b/drivers/md/bcache/fs-io.c index bfa53ef182f5..5262262a7ef8 100644 --- a/drivers/md/bcache/fs-io.c +++ b/drivers/md/bcache/fs-io.c @@ -248,7 +248,7 @@ bchfs_extent_update_hook(struct extent_insert_hook *hook, le64_add_cpu(&h->new_inode.v.i_sectors, sectors); atomic64_add(sectors, &ei->i_sectors); - h->op->sectors += sectors; + h->op->sectors_added += sectors; if (h->op->is_dio) { spin_lock(&inode->i_lock); @@ -375,9 +375,11 @@ union { struct { enum { BCH_PAGE_UNALLOCATED = 0, BCH_PAGE_ALLOCATED, - BCH_PAGE_RESERVED, } alloc_state:2; + /* Owns PAGE_SECTORS sized reservation: */ + unsigned reserved:1; + /* * Number of sectors on disk - for i_blocks * Uncompressed size, not compressed size: @@ -420,29 +422,30 @@ static inline struct bch_page_state *page_state(struct page *page) static void bch_put_page_reservation(struct cache_set *c, struct page *page) { + struct disk_reservation res = { .sectors = PAGE_SECTORS }; struct bch_page_state s; s = page_state_cmpxchg(page_state(page), s, { - if (s.alloc_state == BCH_PAGE_RESERVED) - s.alloc_state = BCH_PAGE_UNALLOCATED; + if (!s.reserved) + return; + s.reserved = 0; }); - if (s.alloc_state == BCH_PAGE_RESERVED) { - struct disk_reservation res = { .sectors = PAGE_SECTORS }; - - /* hack */ - bch_disk_reservation_put(c, &res); - } + bch_disk_reservation_put(c, &res); } static int bch_get_page_reservation(struct cache_set *c, struct page *page, bool check_enospc) { - struct bch_page_state *s = page_state(page), old, new; + struct bch_page_state *s = page_state(page), new; struct disk_reservation res; int ret = 0; - if (s->alloc_state != BCH_PAGE_UNALLOCATED) + BUG_ON(s->alloc_state == BCH_PAGE_ALLOCATED && + s->sectors != PAGE_SECTORS); + + if (s->reserved || + s->alloc_state == BCH_PAGE_ALLOCATED) return 0; ret = __bch_disk_reservation_get(c, &res, PAGE_SECTORS, @@ -450,17 +453,22 @@ static int bch_get_page_reservation(struct cache_set *c, struct page *page, if (ret) return ret; - old = page_state_cmpxchg(s, new, new.alloc_state = BCH_PAGE_RESERVED); - - BUG_ON(old.alloc_state != BCH_PAGE_UNALLOCATED); + page_state_cmpxchg(s, new, { + if (new.reserved) { + bch_disk_reservation_put(c, &res); + return 0; + } + new.reserved = 1; + }); - return ret; + return 0; } static void bch_clear_page_bits(struct page *page) { struct inode *inode = page->mapping->host; struct cache_set *c = inode->i_sb->s_fs_info; + struct disk_reservation res = { .sectors = PAGE_SECTORS }; struct bch_page_state s; if (!PagePrivate(page)) @@ -475,12 +483,8 @@ static void bch_clear_page_bits(struct page *page) spin_unlock(&inode->i_lock); } - if (s.alloc_state == BCH_PAGE_RESERVED) { - struct disk_reservation res = { .sectors = PAGE_SECTORS }; - - /* hack */ + if (s.reserved) bch_disk_reservation_put(c, &res); - } } int bch_set_page_dirty(struct page *page) @@ -630,7 +634,7 @@ static void bchfs_read(struct cache_set *c, struct bch_read_bio *rbio, u64 inode bio_for_each_segment_all(bv, bio, i) { struct bch_page_state *s = page_state(bv->bv_page); - EBUG_ON(s->alloc_state == BCH_PAGE_RESERVED); + EBUG_ON(s->reserved); s->alloc_state = BCH_PAGE_ALLOCATED; s->sectors = 0; @@ -789,27 +793,41 @@ static void bch_writepage_io_done(struct closure *cl) SetPageError(page); if (page->mapping) set_bit(AS_EIO, &page->mapping->flags); - } else { + } + + if (io->op.op.written >= PAGE_SECTORS) { struct bch_page_state old, new; old = page_state_cmpxchg(page_state(page), new, { - new.sectors += new.dirty_sectors; + new.alloc_state = + (io->op.op.compression_type == + BCH_COMPRESSION_NONE) + ? BCH_PAGE_ALLOCATED + : BCH_PAGE_UNALLOCATED; + new.sectors = PAGE_SECTORS; new.dirty_sectors = 0; }); - io->op.sectors -= old.dirty_sectors; + io->op.sectors_added -= old.dirty_sectors; + io->op.op.written -= PAGE_SECTORS; } } /* + * racing with fallocate can cause us to add fewer sectors than + * expected - but we shouldn't add more sectors than expected: + */ + BUG_ON(io->op.sectors_added > 0); + + /* * PageWriteback is effectively our ref on the inode - fixup i_blocks * before calling end_page_writeback: */ - if (!io->op.op.error && io->op.sectors) { + if (io->op.sectors_added) { struct inode *inode = &io->op.ei->vfs_inode; spin_lock(&inode->i_lock); - inode->i_blocks += io->op.sectors; + inode->i_blocks += io->op.sectors_added; spin_unlock(&inode->i_lock); } @@ -847,7 +865,7 @@ alloc_io: closure_init(&w->io->cl, NULL); w->io->op.ei = ei; - w->io->op.sectors = 0; + w->io->op.sectors_added = 0; w->io->op.is_dio = false; bch_write_op_init(&w->io->op.op, w->c, &w->io->bio, (struct disk_reservation) { 0 }, NULL, @@ -875,7 +893,7 @@ static int __bch_writepage(struct page *page, struct writeback_control *wbc, struct inode *inode = page->mapping->host; struct bch_inode_info *ei = to_bch_ei(inode); struct bch_writepage *w = data; - struct bch_page_state old, new; + struct bch_page_state new; unsigned offset; loff_t i_size = i_size_read(inode); pgoff_t end_index = i_size >> PAGE_SHIFT; @@ -910,20 +928,19 @@ do_io: if (wbc->sync_mode == WB_SYNC_ALL) w->io->bio.bio.bio.bi_opf |= WRITE_SYNC; - /* - * Before unlocking the page, transfer refcounts to w->io: - */ - old = page_state_cmpxchg(page_state(page), new, { - new.alloc_state = w->io->op.op.compression_type == BCH_COMPRESSION_NONE - ? BCH_PAGE_ALLOCATED - : BCH_PAGE_UNALLOCATED; - }); - - BUG_ON(old.alloc_state == BCH_PAGE_UNALLOCATED); + /* Before unlocking the page, transfer reservation to w->io: */ + page_state_cmpxchg(page_state(page), new, { + BUG_ON(!new.reserved && + (new.sectors != PAGE_SECTORS || + new.alloc_state != BCH_PAGE_ALLOCATED)); - if (old.alloc_state == BCH_PAGE_RESERVED) - w->io->op.op.res.sectors += PAGE_SECTORS; + if (!new.reserved) + goto out; + new.reserved = 0; + }); + w->io->op.op.res.sectors += PAGE_SECTORS; +out: BUG_ON(PageWriteback(page)); set_page_writeback(page); unlock_page(page); @@ -1047,10 +1064,10 @@ out: if (ret) { if (!PageUptodate(page)) { /* - * If the page hasn't been read in, we won't know if we actually - * need a reservation - we don't actually need to read here, we - * just need to check if the page is fully backed by - * uncompressed data: + * If the page hasn't been read in, we won't know if we + * actually need a reservation - we don't actually need + * to read here, we just need to check if the page is + * fully backed by uncompressed data: */ goto readpage; } @@ -1075,35 +1092,32 @@ int bch_write_end(struct file *filp, struct address_space *mapping, { struct inode *inode = page->mapping->host; struct cache_set *c = inode->i_sb->s_fs_info; - struct bch_page_state *s = page_state(page); lockdep_assert_held(&inode->i_rwsem); - BUG_ON(s->alloc_state == BCH_PAGE_UNALLOCATED); if (unlikely(copied < len && !PageUptodate(page))) { /* * The page needs to be read in, but that would destroy * our partial write - simplest thing is to just force * userspace to redo the write: - * - * userspace doesn't _have_ to redo the write, so clear - * PageAllocated: */ - copied = 0; zero_user(page, 0, PAGE_SIZE); flush_dcache_page(page); - bch_put_page_reservation(c, page); - goto out; + copied = 0; } if (pos + copied > inode->i_size) i_size_write(inode, pos + copied); - if (!PageUptodate(page)) - SetPageUptodate(page); - if (!PageDirty(page)) - set_page_dirty(page); -out: + if (copied) { + if (!PageUptodate(page)) + SetPageUptodate(page); + if (!PageDirty(page)) + set_page_dirty(page); + } else { + bch_put_page_reservation(c, page); + } + unlock_page(page); put_page(page); pagecache_add_put(&mapping->add_lock); @@ -1290,7 +1304,7 @@ static void bch_do_direct_IO_write(struct dio_write *dio, bool sync) } dio->iop.ei = ei; - dio->iop.sectors = 0; + dio->iop.sectors_added = 0; dio->iop.is_dio = true; dio->iop.new_i_size = U64_MAX; bch_write_op_init(&dio->iop.op, c, &dio->bio, @@ -2139,8 +2153,10 @@ long bch_fallocate_dispatch(struct file *file, int mode, static bool page_is_data(struct page *page) { + /* XXX: should only have to check PageDirty */ return PagePrivate(page) && - page_state(page)->alloc_state != BCH_PAGE_UNALLOCATED; + (page_state(page)->sectors || + page_state(page)->dirty_sectors); } static loff_t bch_next_pagecache_data(struct inode *inode, diff --git a/drivers/md/bcache/fs-io.h b/drivers/md/bcache/fs-io.h index fa602febf3a8..590c93cfa179 100644 --- a/drivers/md/bcache/fs-io.h +++ b/drivers/md/bcache/fs-io.h @@ -43,7 +43,7 @@ struct i_sectors_hook { struct bchfs_write_op { struct bch_inode_info *ei; - s64 sectors; + s64 sectors_added; bool is_dio; u64 new_i_size; struct bch_write_op op; |