Commit c42b57c4 authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: bch2_buffered_write large folio conversion

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 49fe78ff
...@@ -54,6 +54,11 @@ static inline int __darray_make_room(darray_void *d, size_t t_size, size_t more, ...@@ -54,6 +54,11 @@ static inline int __darray_make_room(darray_void *d, size_t t_size, size_t more,
#define darray_push(_d, _item) darray_push_gfp(_d, _item, GFP_KERNEL) #define darray_push(_d, _item) darray_push_gfp(_d, _item, GFP_KERNEL)
#define darray_pop(_d) ((_d)->data[--(_d)->nr])
#define darray_first(_d) ((_d).data[0])
#define darray_last(_d) ((_d).data[(_d).nr - 1])
#define darray_insert_item(_d, _pos, _item) \ #define darray_insert_item(_d, _pos, _item) \
({ \ ({ \
size_t pos = (_pos); \ size_t pos = (_pos); \
......
...@@ -1719,7 +1719,17 @@ int bch2_write_end(struct file *file, struct address_space *mapping, ...@@ -1719,7 +1719,17 @@ int bch2_write_end(struct file *file, struct address_space *mapping,
return copied; return copied;
} }
#define WRITE_BATCH_PAGES 32 typedef DARRAY(struct folio *) folios;
static noinline void folios_trunc(folios *folios, struct folio **fi)
{
while (folios->data + folios->nr > fi) {
struct folio *f = darray_pop(folios);
folio_unlock(f);
folio_put(f);
}
}
static int __bch2_buffered_write(struct bch_inode_info *inode, static int __bch2_buffered_write(struct bch_inode_info *inode,
struct address_space *mapping, struct address_space *mapping,
...@@ -1727,64 +1737,73 @@ static int __bch2_buffered_write(struct bch_inode_info *inode, ...@@ -1727,64 +1737,73 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
loff_t pos, unsigned len) loff_t pos, unsigned len)
{ {
struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct folio *folios[WRITE_BATCH_PAGES];
struct bch2_folio_reservation res; struct bch2_folio_reservation res;
unsigned long index = pos >> PAGE_SHIFT; folios folios;
unsigned offset = pos & (PAGE_SIZE - 1); struct folio **fi, *f;
unsigned nr_folios = DIV_ROUND_UP(offset + len, PAGE_SIZE); unsigned copied = 0, f_offset;
unsigned i, reserved = 0, set_dirty = 0; loff_t end = pos + len, f_pos;
unsigned copied = 0, nr_folios_copied = 0;
int ret = 0; int ret = 0;
BUG_ON(!len); BUG_ON(!len);
BUG_ON(nr_folios > ARRAY_SIZE(folios));
bch2_folio_reservation_init(c, inode, &res); bch2_folio_reservation_init(c, inode, &res);
darray_init(&folios);
for (i = 0; i < nr_folios; i++) { f_pos = pos;
folios[i] = __filemap_get_folio(mapping, index + i, while (f_pos < end) {
FGP_LOCK|FGP_WRITE|FGP_CREAT|FGP_STABLE, unsigned fgp_flags = FGP_LOCK|FGP_WRITE|FGP_STABLE;
mapping_gfp_mask(mapping));
if (!folios[i]) { if ((u64) f_pos < (u64) pos + (1U << 20))
nr_folios = i; fgp_flags |= FGP_CREAT;
if (!i) {
ret = -ENOMEM; if (darray_make_room_gfp(&folios, 1,
goto out; mapping_gfp_mask(mapping) & GFP_KERNEL))
}
len = min_t(unsigned, len,
nr_folios * PAGE_SIZE - offset);
break; break;
}
f = __filemap_get_folio(mapping, f_pos >> PAGE_SHIFT,
fgp_flags, mapping_gfp_mask(mapping));
if (!f)
break;
BUG_ON(folios.nr && folio_pos(f) != f_pos);
f_pos = folio_end_pos(f);
darray_push(&folios, f);
}
end = min(end, f_pos);
if (end == pos) {
ret = -ENOMEM;
goto out;
} }
if (offset && !folio_test_uptodate(folios[0])) { f = darray_first(folios);
ret = bch2_read_single_folio(folios[0], mapping); if (pos != folio_pos(f) && !folio_test_uptodate(f)) {
ret = bch2_read_single_folio(f, mapping);
if (ret) if (ret)
goto out; goto out;
} }
if ((pos + len) & (PAGE_SIZE - 1) && f = darray_last(folios);
!folio_test_uptodate(folios[nr_folios - 1])) { if (end != folio_end_pos(f) && !folio_test_uptodate(f)) {
if ((index + nr_folios - 1) << PAGE_SHIFT >= inode->v.i_size) { if (end >= inode->v.i_size) {
folio_zero_range(folios[nr_folios - 1], 0, folio_zero_range(f, 0, folio_size(f));
folio_size(folios[nr_folios - 1]));
} else { } else {
ret = bch2_read_single_folio(folios[nr_folios - 1], mapping); ret = bch2_read_single_folio(f, mapping);
if (ret) if (ret)
goto out; goto out;
} }
} }
while (reserved < len) { f_pos = pos;
unsigned i = (offset + reserved) >> PAGE_SHIFT; f_offset = pos - folio_pos(darray_first(folios));
struct folio *folio = folios[i]; darray_for_each(folios, fi) {
unsigned folio_offset = (offset + reserved) & (PAGE_SIZE - 1); struct folio *f = *fi;
unsigned folio_len = min_t(unsigned, len - reserved, unsigned f_len = min(end, folio_end_pos(f)) - f_pos;
PAGE_SIZE - folio_offset);
if (!bch2_folio_create(folio, __GFP_NOFAIL)->uptodate) { if (!bch2_folio_create(f, __GFP_NOFAIL)->uptodate) {
ret = bch2_folio_set(c, inode_inum(inode), ret = bch2_folio_set(c, inode_inum(inode), fi,
folios + i, nr_folios - i); folios.data + folios.nr - fi);
if (ret) if (ret)
goto out; goto out;
} }
...@@ -1797,78 +1816,89 @@ static int __bch2_buffered_write(struct bch_inode_info *inode, ...@@ -1797,78 +1816,89 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
* we aren't completely out of disk space - we don't do that * we aren't completely out of disk space - we don't do that
* yet: * yet:
*/ */
ret = bch2_folio_reservation_get(c, inode, folio, &res, ret = bch2_folio_reservation_get(c, inode, f, &res, f_offset, f_len);
folio_offset, folio_len);
if (unlikely(ret)) { if (unlikely(ret)) {
if (!reserved) folios_trunc(&folios, fi);
if (!folios.nr)
goto out; goto out;
end = min(end, folio_end_pos(darray_last(folios)));
break; break;
} }
reserved += folio_len; f_pos = folio_end_pos(f);
f_offset = 0;
} }
if (mapping_writably_mapped(mapping)) if (mapping_writably_mapped(mapping))
for (i = 0; i < nr_folios; i++) darray_for_each(folios, fi)
flush_dcache_folio(folios[i]); flush_dcache_folio(*fi);
while (copied < reserved) { f_pos = pos;
struct folio *folio = folios[(offset + copied) >> PAGE_SHIFT]; f_offset = pos - folio_pos(darray_first(folios));
unsigned folio_offset = (offset + copied) & (PAGE_SIZE - 1); darray_for_each(folios, fi) {
unsigned folio_len = min_t(unsigned, reserved - copied, struct folio *f = *fi;
PAGE_SIZE - folio_offset); unsigned f_len = min(end, folio_end_pos(f)) - f_pos;
unsigned folio_copied = copy_page_from_iter_atomic(&folio->page, unsigned f_copied = copy_page_from_iter_atomic(&f->page, f_offset, f_len, iter);
folio_offset, folio_len, iter);
if (!f_copied) {
if (!folio_copied) folios_trunc(&folios, fi);
break; break;
}
if (!folio_test_uptodate(folio) && if (!folio_test_uptodate(f) &&
folio_copied != PAGE_SIZE && f_copied != folio_size(f) &&
pos + copied + folio_copied < inode->v.i_size) { pos + copied + f_copied < inode->v.i_size) {
folio_zero_range(folio, 0, folio_size(folio)); folio_zero_range(f, 0, folio_size(f));
folios_trunc(&folios, fi);
break; break;
} }
flush_dcache_folio(folio); flush_dcache_folio(f);
copied += folio_copied; copied += f_copied;
if (folio_copied != folio_len) if (f_copied != f_len) {
folios_trunc(&folios, fi + 1);
break; break;
}
f_pos = folio_end_pos(f);
f_offset = 0;
} }
if (!copied) if (!copied)
goto out; goto out;
end = pos + copied;
spin_lock(&inode->v.i_lock); spin_lock(&inode->v.i_lock);
if (pos + copied > inode->v.i_size) if (end > inode->v.i_size)
i_size_write(&inode->v, pos + copied); i_size_write(&inode->v, end);
spin_unlock(&inode->v.i_lock); spin_unlock(&inode->v.i_lock);
while (set_dirty < copied) { f_pos = pos;
struct folio *folio = folios[(offset + set_dirty) >> PAGE_SHIFT]; f_offset = pos - folio_pos(darray_first(folios));
unsigned folio_offset = (offset + set_dirty) & (PAGE_SIZE - 1); darray_for_each(folios, fi) {
unsigned folio_len = min_t(unsigned, copied - set_dirty, struct folio *f = *fi;
PAGE_SIZE - folio_offset); unsigned f_len = min(end, folio_end_pos(f)) - f_pos;
if (!folio_test_uptodate(folio)) if (!folio_test_uptodate(f))
folio_mark_uptodate(folio); folio_mark_uptodate(f);
bch2_set_folio_dirty(c, inode, folio, &res, folio_offset, folio_len); bch2_set_folio_dirty(c, inode, f, &res, f_offset, f_len);
folio_unlock(folio);
folio_put(folio);
set_dirty += folio_len; f_pos = folio_end_pos(f);
f_offset = 0;
} }
nr_folios_copied = DIV_ROUND_UP(offset + copied, PAGE_SIZE);
inode->ei_last_dirtied = (unsigned long) current; inode->ei_last_dirtied = (unsigned long) current;
out: out:
for (i = nr_folios_copied; i < nr_folios; i++) { darray_for_each(folios, fi) {
folio_unlock(folios[i]); folio_unlock(*fi);
folio_put(folios[i]); folio_put(*fi);
} }
darray_exit(&folios);
bch2_folio_reservation_put(c, inode, &res); bch2_folio_reservation_put(c, inode, &res);
return copied ?: ret; return copied ?: ret;
...@@ -1887,8 +1917,7 @@ static ssize_t bch2_buffered_write(struct kiocb *iocb, struct iov_iter *iter) ...@@ -1887,8 +1917,7 @@ static ssize_t bch2_buffered_write(struct kiocb *iocb, struct iov_iter *iter)
do { do {
unsigned offset = pos & (PAGE_SIZE - 1); unsigned offset = pos & (PAGE_SIZE - 1);
unsigned bytes = min_t(unsigned long, iov_iter_count(iter), unsigned bytes = iov_iter_count(iter);
PAGE_SIZE * WRITE_BATCH_PAGES - offset);
again: again:
/* /*
* Bring in the user page that we will copy from _first_. * Bring in the user page that we will copy from _first_.
......
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