From 7759e4de064b76137fdc122283e1e4e345f962e2 Mon Sep 17 00:00:00 2001 From: Dzianis Bely Date: Sun, 21 Jun 2026 15:40:31 +0200 Subject: [PATCH 1/2] Support page sizes larger than 4K in snapshot COW tracking --- src/bio_helper.c | 16 ++++++++++++---- src/bio_helper.h | 2 +- src/cow_manager.h | 2 +- src/tracer.c | 10 ++++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/bio_helper.c b/src/bio_helper.c index 56ac483a..15aced9b 100644 --- a/src/bio_helper.c +++ b/src/bio_helper.c @@ -430,7 +430,11 @@ static void __on_bio_read_complete(struct bio *bio, int err) #ifndef HAVE_BVEC_ITER //#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) for (i = 0; i < bio->bi_vcnt; i++) { - bio->bi_io_vec[i].bv_len = PAGE_SIZE; + if (map && (i + 1) * PAGE_SIZE > map->size) { + bio->bi_io_vec[i].bv_len = map->size - i * PAGE_SIZE; + } else { + bio->bi_io_vec[i].bv_len = PAGE_SIZE; + } bio->bi_io_vec[i].bv_offset = 0; } #endif @@ -695,13 +699,14 @@ void bio_free_clone(struct bio *bio) */ int bio_make_read_clone(struct bio_set *bs, struct tracing_params *tp, struct bio *orig_bio, sector_t sect, unsigned int pages, - struct bio **bio_out, unsigned int *bytes_added) + unsigned int tail, struct bio **bio_out, unsigned int *bytes_added) { int ret; struct bio *new_bio; struct page *pg; unsigned int i; unsigned int bytes; + unsigned int actual_size = PAGE_SIZE; unsigned int total = 0; #ifdef BIO_MAX_PAGES unsigned int actual_pages = @@ -758,9 +763,12 @@ int bio_make_read_clone(struct bio_set *bs, struct tracing_params *tp, goto error; } + if (i == pages - 1 && tail != 0) + actual_size = PAGE_SIZE - tail; + // add the page to the bio - bytes = bio_add_page(new_bio, pg, PAGE_SIZE, 0); - if (bytes != PAGE_SIZE) { + bytes = bio_add_page(new_bio, pg, actual_size, 0); + if (bytes != actual_size) { __free_page(pg); break; } diff --git a/src/bio_helper.h b/src/bio_helper.h index 7f47dc88..74b63aaf 100644 --- a/src/bio_helper.h +++ b/src/bio_helper.h @@ -149,7 +149,7 @@ void bio_free_clone(struct bio *bio); int bio_make_read_clone(struct bio_set *bs, struct tracing_params *tp, struct bio *orig_bio, sector_t sect, unsigned int pages, - struct bio **bio_out, unsigned int *bytes_added); + unsigned int tail, struct bio **bio_out, unsigned int *bytes_added); #ifdef HAVE_BIO_ENDIO_INT void dattobd_bio_endio(struct bio *bio, int err); diff --git a/src/cow_manager.h b/src/cow_manager.h index 99162e0f..a5656711 100644 --- a/src/cow_manager.h +++ b/src/cow_manager.h @@ -14,7 +14,7 @@ #include #endif -#define COW_SECTION_SIZE 4096 +#define COW_SECTION_SIZE PAGE_SIZE #define cow_write_filler_mapping(cm, pos) __cow_write_mapping(cm, pos, 1) extern const unsigned long dattobd_cow_ext_buf_size; diff --git a/src/tracer.c b/src/tracer.c index cc1af763..5c810f83 100644 --- a/src/tracer.c +++ b/src/tracer.c @@ -122,7 +122,7 @@ static int snap_trace_bio(struct snap_device *dev, struct bio *bio) struct bio *new_bio = NULL; struct tracing_params *tp = NULL; sector_t start_sect, end_sect; - unsigned int bytes, pages; + unsigned int bytes, pages, tail; // if we don't need to cow this bio just call the real mrf normally if (!bio_needs_cow(bio, dev->sd_cow_inode) || tracer_read_fail_state(dev)) @@ -144,7 +144,9 @@ static int snap_trace_bio(struct snap_device *dev, struct bio *bio) dev->sd_sect_off, SECTORS_PER_BLOCK) + dev->sd_sect_off; - pages = (end_sect - start_sect) / SECTORS_PER_PAGE; + + pages = ROUND_UP(end_sect - start_sect, SECTORS_PER_PAGE) / SECTORS_PER_PAGE; + tail = pages * PAGE_SIZE - (end_sect - start_sect) * SECTOR_SIZE; // allocate tracing_params struct to hold all pointers we will need // across contexts @@ -164,7 +166,7 @@ static int snap_trace_bio(struct snap_device *dev, struct bio *bio) // allocate and populate read bio clone. This bio may not have all the // pages we need due to queue restrictions ret = bio_make_read_clone(dev_bioset(dev), tp, bio, start_sect, pages, - &new_bio, &bytes); + tail, &new_bio, &bytes); if (ret) goto error; @@ -188,7 +190,7 @@ static int snap_trace_bio(struct snap_device *dev, struct bio *bio) // if our bio didn't cover the entire clone we must keep creating bios // until we have - if (bytes / PAGE_SIZE < pages) { + if (bytes < (end_sect - start_sect) * SECTOR_SIZE) { start_sect += bytes / SECTOR_SIZE; pages -= bytes / PAGE_SIZE; continue; From 3b831413b95d6fec341440c1bb8a8259f0fbe4b7 Mon Sep 17 00:00:00 2001 From: Dzianis Bely Date: Sun, 21 Jun 2026 15:40:42 +0200 Subject: [PATCH 2/2] Fix COW data corruption on kernels with PAGE_SIZE > 4K --- src/snap_handle.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/snap_handle.c b/src/snap_handle.c index 3d864ba9..77e30bf0 100644 --- a/src/snap_handle.c +++ b/src/snap_handle.c @@ -240,6 +240,7 @@ int snap_handle_write_bio(const struct snap_device *dev, struct bio *bio) { int ret; char *data; + char *block_data; sector_t start_block, end_block = SECTOR_TO_BLOCK(bio_sector(bio)); struct bio_vec *bvec; #ifdef HAVE_BVEC_ITER_ALL @@ -266,10 +267,10 @@ int snap_handle_write_bio(const struct snap_device *dev, struct bio *bio) // map the page into kernel space data = kmap(bvec->bv_page); - // loop through the blocks in the page - for (; start_block < end_block; start_block++) { + block_data = data + bvec->bv_offset; + for (; start_block < end_block; start_block++, block_data += COW_BLOCK_SIZE) { // pass the block to the cow manager to be handled - ret = cow_write_current(dev->sd_cow, start_block, data); + ret = cow_write_current(dev->sd_cow, start_block, block_data); if (ret) { LOG_ERROR(ret,"memory demands %llu, memory saved before crash %llu",number_of_blocks*COW_BLOCK_SIZE,saved_blocks*COW_BLOCK_SIZE); kunmap(bvec->bv_page);