Commit 7554a8bb authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: Ensure buffered writes write as much as they can

This adds a new helper, bch2_folio_reservation_get_partial(), which
reserves as many blocks as possible and may return partial success.

__bch2_buffered_write() is switched to the new helper - this fixes
fstests generic/275, the write until -ENOSPC test.

generic/230 now fails: this appears to be a test bug, where xfs_io isn't
looping after a partial write to get the error code.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 95924420
...@@ -863,24 +863,26 @@ static int __bch2_buffered_write(struct bch_inode_info *inode, ...@@ -863,24 +863,26 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
f_pos = pos; f_pos = pos;
f_offset = pos - folio_pos(darray_first(fs)); f_offset = pos - folio_pos(darray_first(fs));
darray_for_each(fs, fi) { darray_for_each(fs, fi) {
ssize_t f_reserved;
f = *fi; f = *fi;
f_len = min(end, folio_end_pos(f)) - f_pos; f_len = min(end, folio_end_pos(f)) - f_pos;
f_reserved = bch2_folio_reservation_get_partial(c, inode, f, &res, f_offset, f_len);
if (unlikely(f_reserved != f_len)) {
if (f_reserved < 0) {
if (f == darray_first(fs)) {
ret = f_reserved;
goto out;
}
folios_trunc(&fs, fi);
end = min(end, folio_end_pos(darray_last(fs)));
} else {
folios_trunc(&fs, fi + 1);
end = f_pos + f_reserved;
}
/*
* XXX: per POSIX and fstests generic/275, on -ENOSPC we're
* supposed to write as much as we have disk space for.
*
* On failure here we should still write out a partial page if
* we aren't completely out of disk space - we don't do that
* yet:
*/
ret = bch2_folio_reservation_get(c, inode, f, &res, f_offset, f_len);
if (unlikely(ret)) {
folios_trunc(&fs, fi);
if (!fs.nr)
goto out;
end = min(end, folio_end_pos(darray_last(fs)));
break; break;
} }
......
...@@ -423,7 +423,7 @@ int bch2_folio_reservation_get(struct bch_fs *c, ...@@ -423,7 +423,7 @@ int bch2_folio_reservation_get(struct bch_fs *c,
struct bch_inode_info *inode, struct bch_inode_info *inode,
struct folio *folio, struct folio *folio,
struct bch2_folio_reservation *res, struct bch2_folio_reservation *res,
unsigned offset, unsigned len) size_t offset, size_t len)
{ {
struct bch_folio *s = bch2_folio_create(folio, 0); struct bch_folio *s = bch2_folio_create(folio, 0);
unsigned i, disk_sectors = 0, quota_sectors = 0; unsigned i, disk_sectors = 0, quota_sectors = 0;
...@@ -437,8 +437,7 @@ int bch2_folio_reservation_get(struct bch_fs *c, ...@@ -437,8 +437,7 @@ int bch2_folio_reservation_get(struct bch_fs *c,
for (i = round_down(offset, block_bytes(c)) >> 9; for (i = round_down(offset, block_bytes(c)) >> 9;
i < round_up(offset + len, block_bytes(c)) >> 9; i < round_up(offset + len, block_bytes(c)) >> 9;
i++) { i++) {
disk_sectors += sectors_to_reserve(&s->s[i], disk_sectors += sectors_to_reserve(&s->s[i], res->disk.nr_replicas);
res->disk.nr_replicas);
quota_sectors += s->s[i].state == SECTOR_unallocated; quota_sectors += s->s[i].state == SECTOR_unallocated;
} }
...@@ -449,12 +448,9 @@ int bch2_folio_reservation_get(struct bch_fs *c, ...@@ -449,12 +448,9 @@ int bch2_folio_reservation_get(struct bch_fs *c,
} }
if (quota_sectors) { if (quota_sectors) {
ret = bch2_quota_reservation_add(c, inode, &res->quota, ret = bch2_quota_reservation_add(c, inode, &res->quota, quota_sectors, true);
quota_sectors, true);
if (unlikely(ret)) { if (unlikely(ret)) {
struct disk_reservation tmp = { struct disk_reservation tmp = { .sectors = disk_sectors };
.sectors = disk_sectors
};
bch2_disk_reservation_put(c, &tmp); bch2_disk_reservation_put(c, &tmp);
res->disk.sectors -= disk_sectors; res->disk.sectors -= disk_sectors;
...@@ -465,6 +461,31 @@ int bch2_folio_reservation_get(struct bch_fs *c, ...@@ -465,6 +461,31 @@ int bch2_folio_reservation_get(struct bch_fs *c,
return 0; return 0;
} }
ssize_t bch2_folio_reservation_get_partial(struct bch_fs *c,
struct bch_inode_info *inode,
struct folio *folio,
struct bch2_folio_reservation *res,
size_t offset, size_t len)
{
size_t l, reserved = 0;
int ret;
while ((l = len - reserved)) {
while ((ret = bch2_folio_reservation_get(c, inode, folio, res, offset, l))) {
if ((offset & (block_bytes(c) - 1)) + l <= block_bytes(c))
return reserved ?: ret;
len = reserved + l;
l /= 2;
}
offset += l;
reserved += l;
}
return reserved;
}
static void bch2_clear_folio_bits(struct folio *folio) static void bch2_clear_folio_bits(struct folio *folio)
{ {
struct bch_inode_info *inode = to_bch_ei(folio->mapping->host); struct bch_inode_info *inode = to_bch_ei(folio->mapping->host);
......
...@@ -153,7 +153,12 @@ int bch2_folio_reservation_get(struct bch_fs *, ...@@ -153,7 +153,12 @@ int bch2_folio_reservation_get(struct bch_fs *,
struct bch_inode_info *, struct bch_inode_info *,
struct folio *, struct folio *,
struct bch2_folio_reservation *, struct bch2_folio_reservation *,
unsigned, unsigned); size_t, size_t);
ssize_t bch2_folio_reservation_get_partial(struct bch_fs *,
struct bch_inode_info *,
struct folio *,
struct bch2_folio_reservation *,
size_t, size_t);
void bch2_set_folio_dirty(struct bch_fs *, void bch2_set_folio_dirty(struct bch_fs *,
struct bch_inode_info *, struct bch_inode_info *,
......
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