bcachefs: bch2_write_prep_encoded_data() now returns errcode

Prep work for killing off EIO and replacing them with proper private
error codes.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2025-03-20 10:30:51 -04:00
parent 2fe208303a
commit 127d90d282

View File

@ -434,6 +434,12 @@ void bch2_write_op_error(struct bch_write_op *op, u64 offset, const char *fmt, .
printbuf_exit(&buf); printbuf_exit(&buf);
} }
static void bch2_write_csum_err_msg(struct bch_write_op *op)
{
bch2_write_op_error(op, op->pos.offset,
"error verifying existing checksum while rewriting existing data (memory corruption?)");
}
void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c, void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
enum bch_data_type type, enum bch_data_type type,
const struct bkey_i *k, const struct bkey_i *k,
@ -809,7 +815,6 @@ static int bch2_write_rechecksum(struct bch_fs *c,
{ {
struct bio *bio = &op->wbio.bio; struct bio *bio = &op->wbio.bio;
struct bch_extent_crc_unpacked new_crc; struct bch_extent_crc_unpacked new_crc;
int ret;
/* bch2_rechecksum_bio() can't encrypt or decrypt data: */ /* bch2_rechecksum_bio() can't encrypt or decrypt data: */
@ -817,10 +822,10 @@ static int bch2_write_rechecksum(struct bch_fs *c,
bch2_csum_type_is_encryption(new_csum_type)) bch2_csum_type_is_encryption(new_csum_type))
new_csum_type = op->crc.csum_type; new_csum_type = op->crc.csum_type;
ret = bch2_rechecksum_bio(c, bio, op->version, op->crc, int ret = bch2_rechecksum_bio(c, bio, op->version, op->crc,
NULL, &new_crc, NULL, &new_crc,
op->crc.offset, op->crc.live_size, op->crc.offset, op->crc.live_size,
new_csum_type); new_csum_type);
if (ret) if (ret)
return ret; return ret;
@ -830,44 +835,12 @@ static int bch2_write_rechecksum(struct bch_fs *c,
return 0; return 0;
} }
static int bch2_write_decrypt(struct bch_write_op *op) static noinline int bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
{
struct bch_fs *c = op->c;
struct nonce nonce = extent_nonce(op->version, op->crc);
struct bch_csum csum;
int ret;
if (!bch2_csum_type_is_encryption(op->crc.csum_type))
return 0;
/*
* If we need to decrypt data in the write path, we'll no longer be able
* to verify the existing checksum (poly1305 mac, in this case) after
* it's decrypted - this is the last point we'll be able to reverify the
* checksum:
*/
csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
return -EIO;
ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
op->crc.csum_type = 0;
op->crc.csum = (struct bch_csum) { 0, 0 };
return ret;
}
static enum prep_encoded_ret {
PREP_ENCODED_OK,
PREP_ENCODED_ERR,
PREP_ENCODED_CHECKSUM_ERR,
PREP_ENCODED_DO_WRITE,
} bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
{ {
struct bch_fs *c = op->c; struct bch_fs *c = op->c;
struct bio *bio = &op->wbio.bio; struct bio *bio = &op->wbio.bio;
struct nonce nonce = extent_nonce(op->version, op->crc);
if (!(op->flags & BCH_WRITE_data_encoded)) int ret = 0;
return PREP_ENCODED_OK;
BUG_ON(bio_sectors(bio) != op->crc.compressed_size); BUG_ON(bio_sectors(bio) != op->crc.compressed_size);
@ -878,12 +851,13 @@ static enum prep_encoded_ret {
(op->crc.compression_type == bch2_compression_opt_to_type(op->compression_opt) || (op->crc.compression_type == bch2_compression_opt_to_type(op->compression_opt) ||
op->incompressible)) { op->incompressible)) {
if (!crc_is_compressed(op->crc) && if (!crc_is_compressed(op->crc) &&
op->csum_type != op->crc.csum_type && op->csum_type != op->crc.csum_type) {
bch2_write_rechecksum(c, op, op->csum_type) && ret = bch2_write_rechecksum(c, op, op->csum_type);
!c->opts.no_data_io) if (ret)
return PREP_ENCODED_CHECKSUM_ERR; return ret;
}
return PREP_ENCODED_DO_WRITE; return 1;
} }
/* /*
@ -891,20 +865,23 @@ static enum prep_encoded_ret {
* is, we have to decompress it: * is, we have to decompress it:
*/ */
if (crc_is_compressed(op->crc)) { if (crc_is_compressed(op->crc)) {
struct bch_csum csum;
if (bch2_write_decrypt(op))
return PREP_ENCODED_CHECKSUM_ERR;
/* Last point we can still verify checksum: */ /* Last point we can still verify checksum: */
csum = bch2_checksum_bio(c, op->crc.csum_type, struct bch_csum csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
extent_nonce(op->version, op->crc),
bio);
if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io) if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
return PREP_ENCODED_CHECKSUM_ERR; goto csum_err;
if (bch2_bio_uncompress_inplace(op, bio)) if (bch2_csum_type_is_encryption(op->crc.csum_type)) {
return PREP_ENCODED_ERR; ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
if (ret)
return ret;
op->crc.csum_type = 0;
op->crc.csum = (struct bch_csum) { 0, 0 };
}
ret = bch2_bio_uncompress_inplace(op, bio);
if (ret)
return ret;
} }
/* /*
@ -916,22 +893,34 @@ static enum prep_encoded_ret {
* If the data is checksummed and we're only writing a subset, * If the data is checksummed and we're only writing a subset,
* rechecksum and adjust bio to point to currently live data: * rechecksum and adjust bio to point to currently live data:
*/ */
if ((op->crc.live_size != op->crc.uncompressed_size || if (op->crc.live_size != op->crc.uncompressed_size ||
op->crc.csum_type != op->csum_type) && op->crc.csum_type != op->csum_type) {
bch2_write_rechecksum(c, op, op->csum_type) && ret = bch2_write_rechecksum(c, op, op->csum_type);
!c->opts.no_data_io) if (ret)
return PREP_ENCODED_CHECKSUM_ERR; return ret;
}
/* /*
* If we want to compress the data, it has to be decrypted: * If we want to compress the data, it has to be decrypted:
*/ */
if ((op->compression_opt || if (bch2_csum_type_is_encryption(op->crc.csum_type) &&
bch2_csum_type_is_encryption(op->crc.csum_type) != (op->compression_opt || op->crc.csum_type != op->csum_type)) {
bch2_csum_type_is_encryption(op->csum_type)) && struct bch_csum csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
bch2_write_decrypt(op)) if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
return PREP_ENCODED_CHECKSUM_ERR; goto csum_err;
return PREP_ENCODED_OK; ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
if (ret)
return ret;
op->crc.csum_type = 0;
op->crc.csum = (struct bch_csum) { 0, 0 };
}
return 0;
csum_err:
bch2_write_csum_err_msg(op);
return -EIO;
} }
static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp, static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
@ -950,25 +939,21 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
ec_buf = bch2_writepoint_ec_buf(c, wp); ec_buf = bch2_writepoint_ec_buf(c, wp);
switch (bch2_write_prep_encoded_data(op, wp)) { if (unlikely(op->flags & BCH_WRITE_data_encoded)) {
case PREP_ENCODED_OK: ret = bch2_write_prep_encoded_data(op, wp);
break; if (ret < 0)
case PREP_ENCODED_ERR: goto err;
ret = -EIO; if (ret) {
goto err; if (ec_buf) {
case PREP_ENCODED_CHECKSUM_ERR: dst = bch2_write_bio_alloc(c, wp, src,
goto csum_err; &page_alloc_failed,
case PREP_ENCODED_DO_WRITE: ec_buf);
/* XXX look for bug here */ bio_copy_data(dst, src);
if (ec_buf) { bounce = true;
dst = bch2_write_bio_alloc(c, wp, src, }
&page_alloc_failed, init_append_extent(op, wp, op->version, op->crc);
ec_buf); goto do_write;
bio_copy_data(dst, src);
bounce = true;
} }
init_append_extent(op, wp, op->version, op->crc);
goto do_write;
} }
if (ec_buf || if (ec_buf ||
@ -1141,9 +1126,7 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
*_dst = dst; *_dst = dst;
return more; return more;
csum_err: csum_err:
bch2_write_op_error(op, op->pos.offset, bch2_write_csum_err_msg(op);
"error verifying existing checksum while rewriting existing data (memory corruption?)");
ret = -EIO; ret = -EIO;
err: err:
if (to_wbio(dst)->bounce) if (to_wbio(dst)->bounce)