When erasing blocks as zero, we can use optimized block functions to achieve this.
These allow us to request a large rage to be zeroed, possible optimizing this operation and freeing disk space for sparsesly stored images. This only is possible when erase-blocks-as-zero=true is used and can provide a significant performance boost. The case where 0xFF is used during erase is as slow as before. Signed-off-by: Christian Speich <[email protected]> --- hw/sd/sd.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index af7e40faf67c66995b2b615080265dc31da150a6..7fa8e90d36b3b04666de9dfefad5830cb2252b1f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1083,6 +1083,17 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) sd_blk_write_direct(sd, sd->data, addr, len); } +/* Requires sd->buf to be filled with 0xFF */ +static void sd_erase_ff(SDState *sd, uint64_t addr, size_t len) +{ + int erase_len = 1 << HWBLOCK_SHIFT; + uint64_t erase_addr; + + for (erase_addr = addr; erase_addr < addr + len; erase_addr += erase_len) { + sd_blk_write(sd, erase_addr, erase_len); + } +} + static void sd_erase(SDState *sd) { uint64_t erase_start = sd->erase_start; @@ -1090,7 +1101,6 @@ static void sd_erase(SDState *sd) bool sdsc = true; uint64_t wpnum; uint64_t erase_addr; - int erase_len = 1 << HWBLOCK_SHIFT; trace_sdcard_erase(sd->erase_start, sd->erase_end); if (sd->erase_start == INVALID_ADDRESS @@ -1119,24 +1129,38 @@ static void sd_erase(SDState *sd) sd->erase_end = INVALID_ADDRESS; sd->csd[14] |= 0x40; - if (sd->erase_blocks_as_zero) { - memset(sd->data, 0x0, erase_len); - } else { - memset(sd->data, 0xFF, erase_len); + if (!sd->erase_blocks_as_zero) { + memset(sd->data, 0xFF, 1 << HWBLOCK_SHIFT); } - for (erase_addr = erase_start; erase_addr <= erase_end; - erase_addr += erase_len) { - if (sdsc) { - /* Only SDSC cards support write protect groups */ + /* Only SDSC cards support write protect groups */ + if (sdsc) { + for (erase_addr = erase_start; erase_addr <= erase_end; + erase_addr = ROUND_UP(erase_addr + 1, WPGROUP_SIZE)) { + uint64_t wp_group_end = ROUND_UP(erase_addr + 1, WPGROUP_SIZE) - 1; + size_t to_erase = MIN(erase_end, wp_group_end) - erase_addr; + wpnum = sd_addr_to_wpnum(erase_addr); assert(wpnum < sd->wp_group_bits); if (test_bit(wpnum, sd->wp_group_bmap)) { sd->card_status |= WP_ERASE_SKIP; continue; } + + if (sd->erase_blocks_as_zero) { + blk_pwrite_zeroes(sd->blk, erase_addr + sd_part_offset(sd), + to_erase, 0); + } else { + sd_erase_ff(sd, erase_addr, to_erase); + } + } + } else { + if (sd->erase_blocks_as_zero) { + blk_pwrite_zeroes(sd->blk, erase_start + sd_part_offset(sd), + erase_end - erase_start, 0); + } else { + sd_erase_ff(sd, erase_start, erase_end - erase_start); } - sd_blk_write(sd, erase_addr, erase_len); } } -- 2.43.0
