From nobody Sun Dec 14 05:53:26 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=avm.de ARC-Seal: i=1; a=rsa-sha256; t=1764686459; cv=none; d=zohomail.com; s=zohoarc; b=OwmSGqcpgwDmMQqRrn0cfJn1hnaTHuC8GrVlEGbKYO43ftP8taKmxsZMYFWpZ9NX1r28q2S1Z9C7VM1HeUD8oO4Vv0r2XfvAjlMgm6TZj9hE0PSgHARYrwbC7L3uXKUJwny6pFKF5AR6yOF2/ejmgoI8MbtF0GBt9dvN9/gfDok= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764686459; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Rrl7LR+aOxdFwQVfUCMo6Ow9QkdmYPjmXPwI2nyQnVg=; b=nNZIbmO/3edF9yCRB1b8/4cHkbvWvdbbx2PrIH3J5WXzXz1Xw3Fz0KILS5LiRlCnCAOiv+Un73cBiX9aqAIdb4BZjT9ka021Ib8852dFXEk3AVAz16jjizw6iGpxB/bnt7jTz3VD3XiDuuWexERuTmUH7BI1EXar4e8PqKjjEmE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1764686458853785.823422026998; Tue, 2 Dec 2025 06:40:58 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vQRY6-0005Dv-5b; Tue, 02 Dec 2025 09:40:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRY2-00058y-Qo; Tue, 02 Dec 2025 09:40:10 -0500 Received: from mail.avm.de ([2001:bf0:244:244::120]) by eggs.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRXy-0001Bp-So; Tue, 02 Dec 2025 09:40:10 -0500 Received: from [172.16.0.1] (helo=mail.avm.de) by mail.avm.de with ESMTP (eXpurgate 4.53.4) (envelope-from ) id 692efa3f-a0ff-7f0000032729-7f0000019630-1 for ; Tue, 02 Dec 2025 15:39:59 +0100 Received: from mail-notes.avm.de (mail-notes.avm.de [172.16.0.1]) by mail.avm.de (Postfix) with ESMTP; Tue, 2 Dec 2025 15:39:59 +0100 (CET) Received: from [127.0.1.1] ([172.17.89.139]) by mail-notes.avm.de (HCL Domino Release 14.0FP4) with ESMTP id 2025120215400010-18426 ; Tue, 2 Dec 2025 15:40:00 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=avm.de; s=mail; t=1764686399; bh=aKRkTVXly2VJrZBrFWevF8CUBqxGW2YarXf7kPGQFmY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kOSwPe8XO2riTY9Zbn7ucwQ/bGvxB/USuLF/JdtgnvkXyREgBxmMNwfBsRVVZDdlj rpHNi5kdLt+E5KfzmNQL5MZRkyEn1TwM0FnTr5P8d08vrRAUUysBgRcjJjKImbxLxY OihQoXtIP5/Hkln5o/HJpaLHjJ8iHJvQ+GmgiT0c= From: Christian Speich Date: Tue, 02 Dec 2025 15:39:31 +0100 Subject: [PATCH v2 1/4] hw/sd: Switch from byte-wise to buf+len read/writes MIME-Version: 1.0 Message-Id: <20251202-sdcard-performance-b4-v2-1-d42490b11322@avm.de> References: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> In-Reply-To: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> To: qemu-devel@nongnu.org Cc: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Christian Speich X-Mailer: b4 0.14.2 X-MIMETrack: Itemize by SMTP Server on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize by Router on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize complete at 02.12.2025 15:40:00 X-TNEFEvaluated: 1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-purgate-ID: 149429::1764686399-50E2DC3E-94747950/0/0 X-purgate-type: clean X-purgate-size: 19729 X-purgate-Ad: Categorized by eleven eXpurgate (R) https://www.eleven.de X-purgate: This mail is considered clean (visit https://www.eleven.de for further information) X-purgate: clean Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:bf0:244:244::120; envelope-from=c.speich@avm.de; helo=mail.avm.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @avm.de) X-ZM-MESSAGEID: 1764686464116019200 Currently, each read/write is eventually split into individual bytes and then later reconstructed into blocks. Byte-wise operation is always bad for performance. This patch switches from the read_byte/write_byte primitives to read_data/write_data which take a buf + length that should be filled. Byte-wise operation is still supported via sdbus_{read,write}_byte. As before, arbitrarily sized and aligned data can be passed. The new primitives try to pass the largest possible buffer down to the block layer. This enhances speed and if possible avoids the local bounce buffer. Signed-off-by: Christian Speich --- hw/sd/core.c | 16 +--- hw/sd/sd.c | 257 +++++++++++++++++++++++++++++++++++++++++--------= ---- include/hw/sd/sd.h | 13 +-- 3 files changed, 209 insertions(+), 77 deletions(-) diff --git a/hw/sd/core.c b/hw/sd/core.c index d3c9017445e01c2885a115656ecf23dc336d3b0f..5f05748b9942d5a359d7dee3e03= 69cf96fc3571b 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -113,22 +113,18 @@ void sdbus_write_byte(SDBus *sdbus, uint8_t value) if (card) { SDCardClass *sc =3D SDMMC_COMMON_GET_CLASS(card); =20 - sc->write_byte(card, value); + sc->write_data(card, &value, 1); } } =20 void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length) { SDState *card =3D get_card(sdbus); - const uint8_t *data =3D buf; =20 if (card) { SDCardClass *sc =3D SDMMC_COMMON_GET_CLASS(card); =20 - for (size_t i =3D 0; i < length; i++) { - trace_sdbus_write(sdbus_name(sdbus), data[i]); - sc->write_byte(card, data[i]); - } + sc->write_data(card, buf, length); } } =20 @@ -140,7 +136,7 @@ uint8_t sdbus_read_byte(SDBus *sdbus) if (card) { SDCardClass *sc =3D SDMMC_COMMON_GET_CLASS(card); =20 - value =3D sc->read_byte(card); + sc->read_data(card, &value, 1); } trace_sdbus_read(sdbus_name(sdbus), value); =20 @@ -150,15 +146,11 @@ uint8_t sdbus_read_byte(SDBus *sdbus) void sdbus_read_data(SDBus *sdbus, void *buf, size_t length) { SDState *card =3D get_card(sdbus); - uint8_t *data =3D buf; =20 if (card) { SDCardClass *sc =3D SDMMC_COMMON_GET_CLASS(card); =20 - for (size_t i =3D 0; i < length; i++) { - data[i] =3D sc->read_byte(card); - trace_sdbus_read(sdbus_name(sdbus), data[i]); - } + sc->read_data(card, buf, length); } } =20 diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d7a496d77c9200d45c267e2b8ee31d026ca34795..23764ed99f36cf39ee7abe02f08= e51897c05e718 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1049,24 +1049,36 @@ static const VMStateDescription sd_vmstate =3D { }, }; =20 -static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) +static void sd_blk_read_direct(SDState *sd, void* buf, uint64_t addr, + uint32_t len) { trace_sdcard_read_block(addr, len); addr +=3D sd_part_offset(sd); - if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { + if (!sd->blk || blk_pread(sd->blk, addr, len, buf, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } } =20 -static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) +static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) +{ + sd_blk_read_direct(sd, sd->data, addr, len); +} + +static void sd_blk_write_direct(SDState *sd, const void *buf, uint64_t add= r, + uint32_t len) { trace_sdcard_write_block(addr, len); addr +=3D sd_part_offset(sd); - if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { + if (!sd->blk || blk_pwrite(sd->blk, addr, len, buf, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } } =20 +static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) +{ + sd_blk_write_direct(sd, sd->data, addr, len); +} + static void sd_erase(SDState *sd) { uint64_t erase_start =3D sd->erase_start; @@ -1348,7 +1360,7 @@ static sd_rsp_type_t sd_cmd_optional(SDState *sd, SDR= equest req) return sd_illegal; } =20 -/* Configure fields for following sd_generic_write_byte() calls */ +/* Configure fields for following sd_generic_write_date() calls */ static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req, uint64_t start, size_t size) { @@ -1363,7 +1375,7 @@ static sd_rsp_type_t sd_cmd_to_receivingdata(SDState = *sd, SDRequest req, return sd_r1; } =20 -/* Configure fields for following sd_generic_read_byte() calls */ +/* Configure fields for following sd_generic_read_data() calls */ static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, uint64_t start, const void *data, size_t size) @@ -2352,23 +2364,35 @@ send_response: } =20 /* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdat= a() */ -static bool sd_generic_write_byte(SDState *sd, uint8_t value) +static bool sd_generic_write_data(SDState *sd, const void* buf, size_t len= gth) { - sd->data[sd->data_offset] =3D value; + size_t to_write =3D MIN(sd->data_size - sd->data_offset, length); =20 - if (++sd->data_offset >=3D sd->data_size) { + memcpy(sd->data, buf, to_write); + sd->data_offset +=3D to_write; + + if (sd->data_offset >=3D sd->data_size) { sd->state =3D sd_transfer_state; return true; } + return false; } =20 /* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdat= a() */ -static bool sd_generic_read_byte(SDState *sd, uint8_t *value) +static bool sd_generic_read_data(SDState *sd, void *buf, size_t length) { - *value =3D sd->data[sd->data_offset]; + size_t to_read =3D MIN(sd->data_size - sd->data_offset, length); + + memcpy(buf, sd->data, to_read); + sd->data_offset +=3D to_read; + + /* Fill remaining with zero, if requested to read more than requested.= */ + if (to_read < length) { + memset(buf + to_read, 0, length - to_read); + } =20 - if (++sd->data_offset >=3D sd->data_size) { + if (sd->data_offset >=3D sd->data_size) { sd->state =3D sd_transfer_state; return true; } @@ -2376,7 +2400,7 @@ static bool sd_generic_read_byte(SDState *sd, uint8_t= *value) return false; } =20 -static void sd_write_byte(SDState *sd, uint8_t value) +static void sd_write_data(SDState *sd, const void *buf, size_t length) { int i; =20 @@ -2395,10 +2419,10 @@ static void sd_write_byte(SDState *sd, uint8_t valu= e) =20 trace_sdcard_write_data(sd->proto->name, sd->last_cmd_name, - sd->current_cmd, sd->data_offset, value); + sd->current_cmd, sd->data_offset, 0); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - if (sd_generic_write_byte(sd, value)) { + if (sd_generic_write_data(sd, buf, length)) { /* TODO: Check CRC before committing */ sd->state =3D sd_programming_state; sd_blk_write(sd, sd->data_start, sd->data_offset); @@ -2410,32 +2434,76 @@ static void sd_write_byte(SDState *sd, uint8_t valu= e) break; =20 case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->data_offset =3D=3D 0) { - /* Start of the block - let's check the address is valid */ - if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK", - sd->data_start, sd->blk_len)) { - break; - } - if (sd->size <=3D SDSC_MAX_CAPACITY) { - if (sd_wp_addr(sd, sd->data_start)) { + if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK", + sd->data_start + sd->data_offset, length)) { + /* Limit writing data to our device size */ + length =3D sd->size - sd->data_start - sd->data_offset; + } + + if (sd->size <=3D SDSC_MAX_CAPACITY) { + uint64_t start =3D sd->data_start + sd->data_offset; + + /* + * Check if any covered address violates WP. If so, limit our = write + * up to the allowed address. + */ + for (uint64_t addr =3D start; addr < start + length; + addr =3D ROUND_UP(addr + 1, WPGROUP_SIZE)) { + if (sd_wp_addr(sd, addr)) { sd->card_status |=3D WP_VIOLATION; + + length =3D addr - start - 1; break; } } } - sd->data[sd->data_offset++] =3D value; - if (sd->data_offset >=3D sd->blk_len) { - /* TODO: Check CRC before committing */ + + /* Partial write */ + if (sd->data_offset > 0) { + size_t to_write =3D MIN(sd->blk_len - sd->data_offset, length); + + memcpy(sd->data + sd->data_offset, buf, to_write); + sd->data_offset +=3D to_write; + buf +=3D to_write; + length -=3D to_write; + + if (sd->data_offset >=3D sd->blk_len) { + sd->state =3D sd_programming_state; + sd_blk_write(sd, sd->data_start, sd->blk_len); + sd->blk_written++; + sd->data_start +=3D sd->blk_len; + sd->data_offset =3D 0; + sd->csd[14] |=3D 0x40; + + /* Bzzzzzzztt .... Operation complete. */ + if (sd->multi_blk_cnt !=3D 0) { + if (--sd->multi_blk_cnt =3D=3D 0) { + /* Stop! */ + sd->state =3D sd_transfer_state; + break; + } + } + + sd->state =3D sd_receivingdata_state; + } + } + + /* Try to write multiple of block sizes */ + if (length >=3D sd->blk_len) { + size_t to_write =3D QEMU_ALIGN_DOWN(length, sd->blk_len); + sd->state =3D sd_programming_state; - sd_blk_write(sd, sd->data_start, sd->data_offset); - sd->blk_written++; - sd->data_start +=3D sd->blk_len; - sd->data_offset =3D 0; + sd_blk_write_direct(sd, buf, sd->data_start, to_write); + sd->blk_written +=3D to_write / sd->blk_len; + sd->data_start +=3D to_write; sd->csd[14] |=3D 0x40; + buf +=3D to_write; + length -=3D to_write; =20 - /* Bzzzzzzztt .... Operation complete. */ if (sd->multi_blk_cnt !=3D 0) { - if (--sd->multi_blk_cnt =3D=3D 0) { + sd->multi_blk_cnt -=3D to_write / sd->blk_len; + + if (sd->multi_blk_cnt =3D=3D 0) { /* Stop! */ sd->state =3D sd_transfer_state; break; @@ -2444,10 +2512,16 @@ static void sd_write_byte(SDState *sd, uint8_t valu= e) =20 sd->state =3D sd_receivingdata_state; } + + /* Partial write */ + if (length > 0) { + memcpy(sd->data, buf, length); + sd->data_offset =3D length; + } break; =20 case 26: /* CMD26: PROGRAM_CID */ - if (sd_generic_write_byte(sd, value)) { + if (sd_generic_write_data(sd, buf, length)) { /* TODO: Check CRC before committing */ sd->state =3D sd_programming_state; for (i =3D 0; i < sizeof(sd->cid); i ++) @@ -2465,7 +2539,7 @@ static void sd_write_byte(SDState *sd, uint8_t value) break; =20 case 27: /* CMD27: PROGRAM_CSD */ - if (sd_generic_write_byte(sd, value)) { + if (sd_generic_write_data(sd, buf, length)) { /* TODO: Check CRC before committing */ sd->state =3D sd_programming_state; for (i =3D 0; i < sizeof(sd->csd); i ++) @@ -2488,7 +2562,7 @@ static void sd_write_byte(SDState *sd, uint8_t value) break; =20 case 42: /* CMD42: LOCK_UNLOCK */ - if (sd_generic_write_byte(sd, value)) { + if (sd_generic_write_data(sd, buf, length)) { /* TODO: Check CRC before committing */ sd->state =3D sd_programming_state; sd_lock_command(sd); @@ -2498,7 +2572,7 @@ static void sd_write_byte(SDState *sd, uint8_t value) break; =20 case 56: /* CMD56: GEN_CMD */ - sd_generic_write_byte(sd, value); + sd_generic_write_data(sd, buf, length); break; =20 default: @@ -2506,25 +2580,28 @@ static void sd_write_byte(SDState *sd, uint8_t valu= e) } } =20 -static uint8_t sd_read_byte(SDState *sd) +static void sd_read_data(SDState *sd, void *data, size_t length) { /* TODO: Append CRCs */ const uint8_t dummy_byte =3D 0x00; - uint8_t ret; uint32_t io_len; + void *fill_end =3D data + length; =20 if (!sd->blk || !blk_is_inserted(sd->blk)) { - return dummy_byte; + memset(data, dummy_byte, length); + return; } =20 if (sd->state !=3D sd_sendingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, "%s: not in Sending-Data state\n", __func__); - return dummy_byte; + memset(data, dummy_byte, length); + return; } =20 if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) { - return dummy_byte; + memset(data, dummy_byte, length); + return; } =20 io_len =3D sd_blk_len(sd); @@ -2544,40 +2621,102 @@ static uint8_t sd_read_byte(SDState *sd) case 30: /* CMD30: SEND_WRITE_PROT */ case 51: /* ACMD51: SEND_SCR */ case 56: /* CMD56: GEN_CMD */ - sd_generic_read_byte(sd, &ret); + sd_generic_read_data(sd, data, length); break; =20 case 18: /* CMD18: READ_MULTIPLE_BLOCK */ - if (sd->data_offset =3D=3D 0) { - if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", - sd->data_start, io_len)) { - return dummy_byte; + if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", + sd->data_start + sd->data_offset, length)) { + /* Limit reading data to our device size */ + length =3D sd->size - sd->data_start - sd->data_offset; + } + + /* We have a partially read block. */ + if (sd->data_offset > 0) { + size_t to_read =3D MIN(sd->data_size - sd->data_offset, length= ); + + memcpy(data, sd->data + sd->data_offset, to_read); + + sd->data_offset +=3D to_read; + data +=3D to_read; + length -=3D to_read; + + /* Partial read is complete, clear state. */ + if (sd->data_offset >=3D sd->data_size) { + sd->data_start +=3D io_len; + sd->data_size =3D 0; + sd->data_offset =3D 0; + + if (sd->multi_blk_cnt !=3D 0) { + if (--sd->multi_blk_cnt =3D=3D 0) { + sd->state =3D sd_transfer_state; + } + } } - sd_blk_read(sd, sd->data_start, io_len); } - ret =3D sd->data[sd->data_offset ++]; =20 - if (sd->data_offset >=3D io_len) { - sd->data_start +=3D io_len; - sd->data_offset =3D 0; + /* + * Try to read multiples of the block size directly bypassing the = local + * bounce buffer. + */ + if (sd->state =3D=3D sd_sendingdata_state && length >=3D io_len) { + size_t to_read =3D QEMU_ALIGN_DOWN(length, io_len); =20 + /* For limited reads, only read the requested block count. */ if (sd->multi_blk_cnt !=3D 0) { - if (--sd->multi_blk_cnt =3D=3D 0) { - /* Stop! */ + to_read =3D MIN(to_read, sd->multi_blk_cnt * io_len); + } + + sd_blk_read_direct(sd, data, sd->data_start, + to_read); + + sd->data_start +=3D to_read; + data +=3D to_read; + length -=3D to_read; + + if (sd->multi_blk_cnt !=3D 0) { + sd->multi_blk_cnt -=3D to_read / io_len; + + if (sd->multi_blk_cnt =3D=3D 0) { sd->state =3D sd_transfer_state; - break; } } } + + /* Read partial at the end */ + if (sd->state =3D=3D sd_sendingdata_state && length > 0) { + /* Fill the buffer */ + sd_blk_read(sd, sd->data_start, io_len); + + memcpy(data, sd->data, length); + + sd->data_size =3D io_len; + sd->data_offset =3D length; + data +=3D length; + length =3D 0; + + /* + * No need to check multi_blk_cnt, as to_read will always be + * < io_len and we will never finish a block here. + */ + } + + /* + * We always need to fill the supplied buffer fully, recalulate + * remaining length based on the actual buffer end and not a possi= ble + * early end due to a read past the device size. + */ + length =3D fill_end - data; + if (length > 0) { + memset(data, 0, length); + } break; =20 default: qemu_log_mask(LOG_GUEST_ERROR, "%s: DAT read illegal for command %= s\n", __func__, sd->last_cmd_name); - return dummy_byte; + memset(data, dummy_byte, length); } - - return ret; } =20 static bool sd_receive_ready(SDState *sd) @@ -2859,8 +2998,8 @@ static void sdmmc_common_class_init(ObjectClass *klas= s, const void *data) sc->get_dat_lines =3D sd_get_dat_lines; sc->get_cmd_line =3D sd_get_cmd_line; sc->do_command =3D sd_do_command; - sc->write_byte =3D sd_write_byte; - sc->read_byte =3D sd_read_byte; + sc->write_data =3D sd_write_data; + sc->read_data =3D sd_read_data; sc->receive_ready =3D sd_receive_ready; sc->data_ready =3D sd_data_ready; sc->get_inserted =3D sd_get_inserted; diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 91b5c40a5f893ee41cff21ebbe78d7dfa753d94e..56cd30391bfefd867f4ce97a58d= e5cc0acd84e97 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -107,22 +107,23 @@ struct SDCardClass { size_t (*do_command)(SDState *sd, SDRequest *req, uint8_t *resp, size_t respsz); /** - * Write a byte to a SD card. + * Write data to a SD card. * @sd: card - * @value: byte to write + * @value: data to write * * Write a byte on the data lines of a SD card. */ - void (*write_byte)(SDState *sd, uint8_t value); + void (*write_data)(SDState *sd, const void* buf, size_t len); /** - * Read a byte from a SD card. + * Read data from a SD card. * @sd: card * - * Read a byte from the data lines of a SD card. + * Read data from the data lines of a SD card. The requestes length is + * always filled even if an error occours. * * Return: byte value read */ - uint8_t (*read_byte)(SDState *sd); + void (*read_data)(SDState *sd, void* buf, size_t len); bool (*receive_ready)(SDState *sd); bool (*data_ready)(SDState *sd); void (*set_voltage)(SDState *sd, uint16_t millivolts); --=20 2.43.0 From nobody Sun Dec 14 05:53:26 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=avm.de ARC-Seal: i=1; a=rsa-sha256; t=1764686452; cv=none; d=zohomail.com; s=zohoarc; b=kRfGuD0Py51+jEFl1qyYkqos7M+axxM8LdrUUxaT/v+IRnDCZSEbIno0IbHv3Es3q57NGiaE+uA/w/5ikX7Pfbi9WFEUgH+nWCGda5QUzj8Mmp97wQcX4YuO7sEkQPC8ql9neayPOeTufIx+X36FTHbW/kpUcPfzr5Rx0vJHEXE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764686452; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=s5w5ErvaTBvpOyrJQVkmq68jjF83mTLcdPnCh39K2WU=; b=aySWwFSz6tusl8qcs5/JLj17e6SC8IyEHnrj38RBd9M6rbrYbsM3gxWM3JNyhQiMXR0/dooVrdTFRKQlloIB0YU81InL6QYqPiBcl/LXcgcdTIVXZQShm8q10V3RMhvtb+aePHk7Vv12RP7dwiyPxJh3jAWpj/cGg57mYuHIWbU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1764686451790107.19784897231386; Tue, 2 Dec 2025 06:40:51 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vQRY8-0005GK-LT; Tue, 02 Dec 2025 09:40:16 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRY2-00058B-2A; Tue, 02 Dec 2025 09:40:10 -0500 Received: from mail.avm.de ([2001:bf0:244:244::120]) by eggs.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRXz-0001OP-8y; Tue, 02 Dec 2025 09:40:09 -0500 Received: from [172.16.0.1] (helo=mail.avm.de) by mail.avm.de with ESMTP (eXpurgate 4.53.4) (envelope-from ) id 692efa3f-a0ff-7f0000032729-7f000001964e-1 for ; Tue, 02 Dec 2025 15:39:59 +0100 Received: from mail-notes.avm.de (mail-notes.avm.de [172.16.0.1]) by mail.avm.de (Postfix) with ESMTP; Tue, 2 Dec 2025 15:39:59 +0100 (CET) Received: from [127.0.1.1] ([172.17.89.139]) by mail-notes.avm.de (HCL Domino Release 14.0FP4) with ESMTP id 2025120215400011-18427 ; Tue, 2 Dec 2025 15:40:00 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=avm.de; s=mail; t=1764686403; bh=jiNBsQqL3y3YuuTa2svOPt+IvT9nmWDVRYocIZVDxks=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=gxsLNnNZlclGC61clgOQ+O/PgdjEOTBVTRwn7a0LOj3JcSevm3JZaYk9/LGJr94kx YUW8b504gSaAVtbQYl5lpb6JtO5F4/hiZO863AfvPUg3QZwiX0VUdoK0Yrq0B9KDrj 2b/1TY56C4O1g8SqSl6HQabiCcblhKnDg9oBjaVo= From: Christian Speich Date: Tue, 02 Dec 2025 15:39:32 +0100 Subject: [PATCH v2 2/4] hw/sd/sdhci: Don't use bounce buffer for ADMA MIME-Version: 1.0 Message-Id: <20251202-sdcard-performance-b4-v2-2-d42490b11322@avm.de> References: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> In-Reply-To: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> To: qemu-devel@nongnu.org Cc: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Christian Speich X-Mailer: b4 0.14.2 X-MIMETrack: Itemize by SMTP Server on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize by Router on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize complete at 02.12.2025 15:40:00 X-TNEFEvaluated: 1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-purgate-ID: 149429::1764686399-23E0FD7E-E06BB620/0/0 X-purgate-type: clean X-purgate-size: 6515 X-purgate-Ad: Categorized by eleven eXpurgate (R) https://www.eleven.de X-purgate: This mail is considered clean (visit https://www.eleven.de for further information) X-purgate: clean Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:bf0:244:244::120; envelope-from=c.speich@avm.de; helo=mail.avm.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @avm.de) X-ZM-MESSAGEID: 1764686456831019200 Currently, ADMA will temporarily store data into a local bounce buffer when transferring it. This will produce unneeded copies of the data and limit us to the bounce buffer size for each step. This patch now maps the requested DMA address and passes this buffer directly to sdbus_{read,write}_data. This allows to pass much larger buffers down to increase the performance. sdbus_{read,write}_data is already able to handle arbitrary length and alignments, so we do not need to ensure this. Signed-off-by: Christian Speich --- hw/sd/sdhci.c | 102 +++++++++++++++++++++++++++++++-----------------------= ---- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 3c897e54b721075a3ebd215e027fb73a65ff39b2..94ba23a8da990e69fd59c039e4f= dd25b98929dfd 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -774,7 +774,7 @@ static void get_adma_description(SDHCIState *s, ADMADes= cr *dscr) =20 static void sdhci_do_adma(SDHCIState *s) { - unsigned int begin, length; + unsigned int length; const uint16_t block_size =3D s->blksize & BLOCK_SIZE_MASK; const MemTxAttrs attrs =3D { .memory =3D true }; ADMADescr dscr =3D {}; @@ -816,66 +816,74 @@ static void sdhci_do_adma(SDHCIState *s) if (s->trnmod & SDHC_TRNS_READ) { s->prnsts |=3D SDHC_DOING_READ; while (length) { - if (s->data_count =3D=3D 0) { - sdbus_read_data(&s->sdbus, s->fifo_buffer, block_s= ize); - } - begin =3D s->data_count; - if ((length + begin) < block_size) { - s->data_count =3D length + begin; - length =3D 0; - } else { - s->data_count =3D block_size; - length -=3D block_size - begin; - } - res =3D dma_memory_write(s->dma_as, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin, - attrs); - if (res !=3D MEMTX_OK) { + dma_addr_t dma_len =3D length; + + void *buf =3D dma_memory_map(s->dma_as, dscr.addr, &dm= a_len, + DMA_DIRECTION_FROM_DEVICE, + attrs); + + if (buf =3D=3D NULL) { + res =3D MEMTX_ERROR; break; + } else { + res =3D MEMTX_OK; } - dscr.addr +=3D s->data_count - begin; - if (s->data_count =3D=3D block_size) { - s->data_count =3D 0; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - if (s->blkcnt =3D=3D 0) { - break; - } + + sdbus_read_data(&s->sdbus, buf, dma_len); + length -=3D dma_len; + dscr.addr +=3D dma_len; + + dma_memory_unmap(s->dma_as, buf, dma_len, + DMA_DIRECTION_FROM_DEVICE, dma_len); + + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + size_t transfered =3D s->data_count + dma_len; + + s->blkcnt -=3D transfered / block_size; + s->data_count =3D transfered % block_size; + + if (s->blkcnt =3D=3D 0) { + s->data_count =3D 0; + break; } } } } else { s->prnsts |=3D SDHC_DOING_WRITE; while (length) { - begin =3D s->data_count; - if ((length + begin) < block_size) { - s->data_count =3D length + begin; - length =3D 0; - } else { - s->data_count =3D block_size; - length -=3D block_size - begin; - } - res =3D dma_memory_read(s->dma_as, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin, - attrs); - if (res !=3D MEMTX_OK) { + dma_addr_t dma_len =3D length; + + void *buf =3D dma_memory_map(s->dma_as, dscr.addr, &dm= a_len, + DMA_DIRECTION_TO_DEVICE, at= trs); + + if (buf =3D=3D NULL) { + res =3D MEMTX_ERROR; break; + } else { + res =3D MEMTX_OK; } - dscr.addr +=3D s->data_count - begin; - if (s->data_count =3D=3D block_size) { - sdbus_write_data(&s->sdbus, s->fifo_buffer, block_= size); - s->data_count =3D 0; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - if (s->blkcnt =3D=3D 0) { - break; - } + + sdbus_write_data(&s->sdbus, buf, dma_len); + length -=3D dma_len; + dscr.addr +=3D dma_len; + + dma_memory_unmap(s->dma_as, buf, dma_len, + DMA_DIRECTION_TO_DEVICE, dma_len); + + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + size_t transfered =3D s->data_count + dma_len; + + s->blkcnt -=3D transfered / block_size; + s->data_count =3D transfered % block_size; + + if (s->blkcnt =3D=3D 0) { + s->data_count =3D 0; + break; } } } } + if (res !=3D MEMTX_OK) { s->data_count =3D 0; if (s->errintstsen & SDHC_EISEN_ADMAERR) { --=20 2.43.0 From nobody Sun Dec 14 05:53:26 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=avm.de ARC-Seal: i=1; a=rsa-sha256; t=1764686476; cv=none; d=zohomail.com; s=zohoarc; b=IsN9g6ujgXoJh8iWhefW90eQRquaJIETlUtVWUTH9oL6LHPcqp7ccg/y1jBVkzwcUF1NMiCEAGygRipYxNKmrFwTwzsigLNoGsia3MJwpwx/Iu7x+ZrKohyleM2P/7m9jGCm2MMJZse1JZRmoMpOhYjyxRz9oDWu3Fy1VUsP/B4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764686476; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=ADInqD+0ZNA6xikLNDzzenDnvU5XAoo5ayylCRBF1/o=; b=Ga+F1b7LZjQAeRleIsYL0uaMVC5DcpVebb9q8USZNwbW6UWfVttwPe5C4f48pnG/HWw0WEm7UELNyIrZPY5jpagYsbGY/ei/5gbxyO5Wf5jb39YwJVnMjjMXwY9USZ/WlR7tDU3n8wd6rdo0zg2p4LD4xNEG4eXXKLpHeiwSYMg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1764686476959953.843156326925; Tue, 2 Dec 2025 06:41:16 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vQRY4-0005CW-6E; Tue, 02 Dec 2025 09:40:12 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRY2-00058C-3K; Tue, 02 Dec 2025 09:40:10 -0500 Received: from mail.avm.de ([2001:bf0:244:244::120]) by eggs.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRXy-0001Bx-Pi; Tue, 02 Dec 2025 09:40:09 -0500 Received: from [172.16.0.1] (helo=mail.avm.de) by mail.avm.de with ESMTP (eXpurgate 4.53.4) (envelope-from ) id 692efa3f-a0ff-7f0000032729-7f000001966e-1 for ; Tue, 02 Dec 2025 15:39:59 +0100 Received: from mail-notes.avm.de (mail-notes.avm.de [172.16.0.1]) by mail.avm.de (Postfix) with ESMTP; Tue, 2 Dec 2025 15:39:59 +0100 (CET) Received: from [127.0.1.1] ([172.17.89.139]) by mail-notes.avm.de (HCL Domino Release 14.0FP4) with ESMTP id 2025120215400011-18428 ; Tue, 2 Dec 2025 15:40:00 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=avm.de; s=mail; t=1764686399; bh=lTNxsXa8TYK2BjPGrbbEcsQCqGwe1ZFE2/2qSJiNLqE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=g+VlaWzbegmXgjy6qv35AJuwLR/RpMFr5Y/O9d7qTiTgygq0EQ/LXt+k8xdVbP6uZ 95QhH7PuyFjQKcpDukb2UiqnhndhZDv6+MLPhSiVGsL2M18SLojWEzG5FwOhoiIz3F NIxMopWO+Gs8WArd7EvS6Sfmw7wyKewRuUhoNtSA= From: Christian Speich Date: Tue, 02 Dec 2025 15:39:33 +0100 Subject: [PATCH v2 3/4] hw/sd/sdcard: Add erase-blocks-as-zero option. MIME-Version: 1.0 Message-Id: <20251202-sdcard-performance-b4-v2-3-d42490b11322@avm.de> References: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> In-Reply-To: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> To: qemu-devel@nongnu.org Cc: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Christian Speich X-Mailer: b4 0.14.2 X-MIMETrack: Itemize by SMTP Server on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize by Router on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize complete at 02.12.2025 15:40:00 X-TNEFEvaluated: 1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-purgate-ID: 149429::1764686399-36E21D7E-56A0AF24/0/0 X-purgate-type: clean X-purgate-size: 2781 X-purgate-Ad: Categorized by eleven eXpurgate (R) https://www.eleven.de X-purgate: This mail is considered clean (visit https://www.eleven.de for further information) X-purgate: clean Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:bf0:244:244::120; envelope-from=c.speich@avm.de; helo=mail.avm.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @avm.de) X-ZM-MESSAGEID: 1764686478111019200 Currently, erased blocks are filled with 0xFF. However SCR Bit 55 (DATA_STAT_AFTER_ERASE) indicates that an erase produces zeros. One of them is wrong. This patch does two things. First it fixes the reporting of DATA_STAT_AFTER_ERASE in SCR to correctly reflect the content of erased blocks. We also increase the Product Revision (REV in CID) to indicate to the guest that DATA_STAT_AFTER_ERASE is now reliable. Secondly, we introduce a erase-blocks-as-zero option, which allows the user to choose if erased blocks should contain 0xFF or 0x00. The default is still 0xFF to remain compatible with current users. Signed-off-by: Christian Speich --- hw/sd/sd.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 23764ed99f36cf39ee7abe02f08e51897c05e718..af7e40faf67c66995b2b6150802= 65dc31da150a6 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -139,6 +139,7 @@ struct SDState { /* Static properties */ =20 uint8_t spec_version; + bool erase_blocks_as_zero; uint64_t boot_part_size; BlockBackend *blk; uint8_t boot_config; @@ -409,6 +410,9 @@ static void sd_set_scr(SDState *sd) sd->scr[0] |=3D 2; /* Spec Version 2.00 or Version 3.0X */ sd->scr[1] =3D (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ + if (!sd->erase_blocks_as_zero) { + sd->scr[1] |=3D (1 << 7); /* DATA_STAT_AFTER_ERASE: Erase produces= 0xFF */ + } sd->scr[2] =3D 0x00; /* Extended Security is not supported. */ if (sd->spec_version >=3D SD_PHY_SPECv3_01_VERS) { sd->scr[2] |=3D 1 << 7; /* Spec Version 3.0X */ @@ -426,7 +430,7 @@ static void sd_set_scr(SDState *sd) #define MID 0xaa #define OID "XY" #define PNM "QEMU!" -#define PRV 0x01 +#define PRV 0x02 #define MDT_YR 2006 #define MDT_MON 2 =20 @@ -1115,7 +1119,12 @@ static void sd_erase(SDState *sd) sd->erase_end =3D INVALID_ADDRESS; sd->csd[14] |=3D 0x40; =20 - memset(sd->data, 0xff, erase_len); + if (sd->erase_blocks_as_zero) { + memset(sd->data, 0x0, erase_len); + } else { + memset(sd->data, 0xFF, erase_len); + } + for (erase_addr =3D erase_start; erase_addr <=3D erase_end; erase_addr +=3D erase_len) { if (sdsc) { @@ -2971,6 +2980,8 @@ static void emmc_realize(DeviceState *dev, Error **er= rp) =20 static const Property sdmmc_common_properties[] =3D { DEFINE_PROP_DRIVE("drive", SDState, blk), + DEFINE_PROP_BOOL("erase-blocks-as-zero", SDState, erase_blocks_as_zero, + false), }; =20 static const Property sd_properties[] =3D { --=20 2.43.0 From nobody Sun Dec 14 05:53:26 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=avm.de ARC-Seal: i=1; a=rsa-sha256; t=1764686477; cv=none; d=zohomail.com; s=zohoarc; b=dqdI1NeD2ztTs2GA2PYi1EUMh8ttL/8WCiJ/v5c/6HKxRDeJziff45PZ//qqaAc1S9f/dzy4p33OWG/jE8XffTj5QTBikOQq21nWsYRKxvAj2aWmneaDvi6vGdvvVURZsDE79vmlphHSHDtD6bE6Zq0S9WbOxCJcN4Bj3ZLbMUo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764686477; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=kceri7g1Qn5qGk9Z4X13T2iNwakZ6yJrpWUNKce0kZc=; b=YWPompproReRvjeb2BL8MaLc0S6ip+DgyvjNc+BhHY1Y/+XcDUQqt7ByC68l70XY5dLnrnkyJYKNEWwfmirmS/1lTuOSsDEJ+WQdnjSE1bwPjAuQkeYVqstC65Uv7hXhO4Uu+Dj6Qx6pLgdj6GrD8BoDuiNTyOrd/R6oFq9RttU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1764686476955525.5726282044843; Tue, 2 Dec 2025 06:41:16 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vQRY4-0005AG-0O; Tue, 02 Dec 2025 09:40:12 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRY2-00058A-0s; Tue, 02 Dec 2025 09:40:10 -0500 Received: from mail.avm.de ([2001:bf0:244:244::120]) by eggs.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vQRXz-0001OI-7w; Tue, 02 Dec 2025 09:40:09 -0500 Received: from [172.16.0.1] (helo=mail.avm.de) by mail.avm.de with ESMTP (eXpurgate 4.53.4) (envelope-from ) id 692efa3f-a0ff-7f0000032729-7f000001967c-1 for ; Tue, 02 Dec 2025 15:39:59 +0100 Received: from mail-notes.avm.de (mail-notes.avm.de [172.16.0.1]) by mail.avm.de (Postfix) with ESMTP; Tue, 2 Dec 2025 15:39:59 +0100 (CET) Received: from [127.0.1.1] ([172.17.89.139]) by mail-notes.avm.de (HCL Domino Release 14.0FP4) with ESMTP id 2025120215400012-18429 ; Tue, 2 Dec 2025 15:40:00 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=avm.de; s=mail; t=1764686403; bh=W0C+B/HQ6E2rn8f69u0hr2rqispXBPZi8uNq7mykuhg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Y6+U46lHZ2GrR8CHuSI+ek7+oTstm2phLfLEAcnDdBTNALb5u17MysFpzcnTBcHCu KDUwVLEXtzSjZBo3Z0vstHnFZEHAKrQjRABgGy8SxZe2PliKpef2pbf71qtAhpMtpN Vxf2kkgnH+xKR/T3aGeD59a9HEcQfr3cTIs6n9sA= From: Christian Speich Date: Tue, 02 Dec 2025 15:39:34 +0100 Subject: [PATCH v2 4/4] hw/sd/sdcard: Optimize erase blocks as zero. MIME-Version: 1.0 Message-Id: <20251202-sdcard-performance-b4-v2-4-d42490b11322@avm.de> References: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> In-Reply-To: <20251202-sdcard-performance-b4-v2-0-d42490b11322@avm.de> To: qemu-devel@nongnu.org Cc: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Christian Speich X-Mailer: b4 0.14.2 X-MIMETrack: Itemize by SMTP Server on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize by Router on ANIS1/AVM(Release 14.0FP4|March 10, 2025) at 02.12.2025 15:40:00, Serialize complete at 02.12.2025 15:40:00 X-TNEFEvaluated: 1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-purgate-ID: 149429::1764686399-C5E23D7E-CF67ABBC/0/0 X-purgate-type: clean X-purgate-size: 3451 X-purgate-Ad: Categorized by eleven eXpurgate (R) https://www.eleven.de X-purgate: This mail is considered clean (visit https://www.eleven.de for further information) X-purgate: clean Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:bf0:244:244::120; envelope-from=c.speich@avm.de; helo=mail.avm.de X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @avm.de) X-ZM-MESSAGEID: 1764686480106019200 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=3Dtrue 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 --- 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..7fa8e90d36b3b04666de9dfefad= 5830cb2252b1f 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); } =20 +/* Requires sd->buf to be filled with 0xFF */ +static void sd_erase_ff(SDState *sd, uint64_t addr, size_t len) +{ + int erase_len =3D 1 << HWBLOCK_SHIFT; + uint64_t erase_addr; + + for (erase_addr =3D addr; erase_addr < addr + len; erase_addr +=3D era= se_len) { + sd_blk_write(sd, erase_addr, erase_len); + } +} + static void sd_erase(SDState *sd) { uint64_t erase_start =3D sd->erase_start; @@ -1090,7 +1101,6 @@ static void sd_erase(SDState *sd) bool sdsc =3D true; uint64_t wpnum; uint64_t erase_addr; - int erase_len =3D 1 << HWBLOCK_SHIFT; =20 trace_sdcard_erase(sd->erase_start, sd->erase_end); if (sd->erase_start =3D=3D INVALID_ADDRESS @@ -1119,24 +1129,38 @@ static void sd_erase(SDState *sd) sd->erase_end =3D INVALID_ADDRESS; sd->csd[14] |=3D 0x40; =20 - 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); } =20 - for (erase_addr =3D erase_start; erase_addr <=3D erase_end; - erase_addr +=3D erase_len) { - if (sdsc) { - /* Only SDSC cards support write protect groups */ + /* Only SDSC cards support write protect groups */ + if (sdsc) { + for (erase_addr =3D erase_start; erase_addr <=3D erase_end; + erase_addr =3D ROUND_UP(erase_addr + 1, WPGROUP_SIZE)) { + uint64_t wp_group_end =3D ROUND_UP(erase_addr + 1, WPGROUP_SIZ= E) - 1; + size_t to_erase =3D MIN(erase_end, wp_group_end) - erase_addr; + wpnum =3D sd_addr_to_wpnum(erase_addr); assert(wpnum < sd->wp_group_bits); if (test_bit(wpnum, sd->wp_group_bmap)) { sd->card_status |=3D 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); } } =20 --=20 2.43.0