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


Reply via email to