From aba554cc34bdfc92eb13ef852a73d6bd82329f37 Mon Sep 17 00:00:00 2001 From: Jocelyn Bohr Date: Tue, 22 Nov 2016 17:39:21 -0800 Subject: [PATCH] Handle slow MMC writes Flashing a 400Mb sparse system image takes ~10 minutes. The fastboot UDP protocol expects a response within 1 minute, so during long flash operations, the device must send fastboot "INFO" packets. This patch does the following: - Separate large writes into writes of size FASTBOOT_MAX_BLK_WRITE. This parameter was tuned by hand to result in a ~10 second write. - Keep a timer and send an INFO packet every 30 seconds. - Adjust the sequence number in the header of the fastboot OKAY packet to account for any INFO packets sent during flashing. - Reduce busywaiting in the bcm2835 MMC driver. This change is based on what the kernel does, and doesn't seem to corrupt the MMC. Without this change, "flashall" takes 25 minutes. Bug: 31887729 Test: "fastboot -s udp:$RPI_IP flashall" works, rpi3 boots - Compute CRC checksum over every write to verify written data was not corrupted. Change-Id: Ib17ef6a85715705a8b5f722a8b7d3e5fd1a6625d --- common/fb_common.c | 19 ++++++++++++++++++ common/fb_mmc.c | 32 ++++++++++++++++++++++++++--- drivers/mmc/bcm2835_sdhci.c | 8 +------- include/fastboot.h | 13 ++++++++++++ include/net/fastboot.h | 6 ++++++ net/fastboot.c | 40 +++++++++++++++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 10 deletions(-) diff --git a/common/fb_common.c b/common/fb_common.c index a1daaccd55..d6e41988e9 100644 --- a/common/fb_common.c +++ b/common/fb_common.c @@ -6,6 +6,9 @@ #include #include +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT +#include +#endif void fastboot_fail(const char *reason, char *response) { @@ -20,3 +23,19 @@ void fastboot_okay(const char *reason, char *response) strncpy(response, okay_str, FASTBOOT_RESPONSE_LEN); strncat(response, reason, FASTBOOT_RESPONSE_LEN - strlen(okay_str) - 1); } + +void timed_send_info(ulong *start, const char *msg) +{ +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT + /* Initialize timer */ + if (*start == 0) { + *start = get_timer(0); + } + ulong time = get_timer(*start); + /* Send INFO packet to host every 30 seconds */ + if (time >= 30000) { + *start = get_timer(0); + fastboot_send_info(msg); + } +#endif +} diff --git a/common/fb_mmc.c b/common/fb_mmc.c index 50cae9b418..a499bc4ffe 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -30,6 +30,8 @@ #endif #define BOOT_PARTITION_NAME "boot" +#define FASTBOOT_MAX_BLK_WRITE 16384 +static ulong timer; struct fb_mmc_sparse { struct blk_desc *dev_desc; @@ -57,13 +59,37 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc, return ret; } +static lbaint_t fb_mmc_blk_write(struct blk_desc *block_dev, lbaint_t start, + lbaint_t blkcnt, const void *buffer) +{ + lbaint_t blk = start; + lbaint_t blks_written; + lbaint_t cur_blkcnt; + lbaint_t blks = 0; + int i; + for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) { + cur_blkcnt = min((int)blkcnt-i, FASTBOOT_MAX_BLK_WRITE); + if (buffer != NULL) { + timed_send_info(&timer, "writing"); + blks_written = blk_dwrite(block_dev, blk, cur_blkcnt, + buffer+(i*block_dev->blksz)); + } else { + timed_send_info(&timer, "erasing"); + blks_written = blk_derase(block_dev, blk, cur_blkcnt); + } + blk += blks_written; + blks += blks_written; + } + return blks; +} + static lbaint_t fb_mmc_sparse_write(struct sparse_storage *info, lbaint_t blk, lbaint_t blkcnt, const void *buffer) { struct fb_mmc_sparse *sparse = info->priv; struct blk_desc *dev_desc = sparse->dev_desc; - return blk_dwrite(dev_desc, blk, blkcnt, buffer); + return fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer); } static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info, @@ -91,7 +117,7 @@ static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info, puts("Flashing Raw Image\n"); - blks = blk_dwrite(dev_desc, info->start, blkcnt, buffer); + blks = fb_mmc_blk_write(dev_desc, info->start, blkcnt, buffer); if (blks != blkcnt) { error("failed writing to device %d\n", dev_desc->devnum); fastboot_fail("failed writing to device", response); @@ -392,7 +418,7 @@ void fb_mmc_erase(const char *cmd, char *response) printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n", blks_start, blks_start + blks_size); - blks = blk_derase(dev_desc, blks_start, blks_size); + blks = fb_mmc_blk_write(dev_desc, blks_start, blks_size, NULL); if (blks != blks_size) { error("failed erasing from device %d", dev_desc->devnum); fastboot_fail("failed erasing from device", response); diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 3157354d2a..819bd88960 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -50,11 +50,6 @@ #define MIN_FREQ 400000 #define SDHCI_BUFFER 0x20 -struct bcm2835_sdhci_plat { - struct mmc_config cfg; - struct mmc mmc; -}; - struct bcm2835_sdhci_host { struct sdhci_host host; uint twoticks_delay; @@ -80,8 +75,7 @@ static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, * too) */ if (reg != SDHCI_BUFFER) { - while (timer_get_us() - bcm_host->last_write < - bcm_host->twoticks_delay) + while (timer_get_us() - bcm_host->last_write < bcm_host->twoticks_delay) ; } diff --git a/include/fastboot.h b/include/fastboot.h index 17bf12cac4..bc7ff2bb14 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -19,4 +19,17 @@ void fastboot_fail(const char *reason, char *response); void fastboot_okay(const char *reason, char *response); +/** + * Send an INFO packet during long commands based on timer. If + * CONFIG_UDP_FUNCTION_FASTBOOT is defined, an INFO packet is sent + * if the time is 30 seconds after start. Else, noop. + * + * TODO: Handle the situation where both UDP and USB fastboot are + * enabled. + * + * @param start: Time since last INFO packet was sent. + * @param msg: String describing the reason for waiting + */ +void timed_send_info(ulong *start, const char *msg); + #endif /* _FASTBOOT_H_ */ diff --git a/include/net/fastboot.h b/include/net/fastboot.h index 10a5e39be1..538af29323 100644 --- a/include/net/fastboot.h +++ b/include/net/fastboot.h @@ -16,6 +16,12 @@ * Wait for incoming fastboot comands. */ void fastboot_start_server(void); +/** + * Send an INFO packet during long commands + * + * @param msg: String describing the reason for waiting + */ +void fastboot_send_info(const char*); /**********************************************************************/ diff --git a/net/fastboot.c b/net/fastboot.c index e1a2f223f3..194c57471f 100644 --- a/net/fastboot.c +++ b/net/fastboot.c @@ -67,6 +67,41 @@ static void boot_downloaded_image(void); static void cleanup_command_data(void); static void write_fb_response(const char*, const char*, char*); +void fastboot_send_info(const char *msg) +{ + uchar *packet; + uchar *packet_base; + int len = 0; + char response[FASTBOOT_RESPONSE_LEN] = {0}; + + struct fastboot_header fb_response_header = + { + .id = FASTBOOT_FASTBOOT, + .flags = 0, + .seq = htons(fb_sequence_number) + }; + ++fb_sequence_number; + packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; + packet_base = packet; + + /* Write headers */ + memcpy(packet, &fb_response_header, sizeof(fb_response_header)); + packet += sizeof(fb_response_header); + /* Write response */ + write_fb_response("INFO", msg, response); + memcpy(packet, response, strlen(response)); + packet += strlen(response); + + len = packet-packet_base; + + /* Save packet for retransmitting */ + last_packet_len = len; + memcpy(last_packet, packet_base, last_packet_len); + + net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, + fastboot_remote_port, fastboot_our_port, len); +} + /** * Constructs and sends a packet in response to received fastboot packet * @@ -149,6 +184,11 @@ static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data, error("command %s not implemented.\n", cmd_string); write_fb_response("FAIL", "unrecognized command", response); } + /* Sent some INFO packets, need to update sequence number in header */ + if (fb_header.seq != fb_sequence_number) { + fb_response_header.seq = htons(fb_sequence_number); + memcpy(packet_base, &fb_response_header, sizeof(fb_response_header)); + } /* Write response to packet */ memcpy(packet, response, strlen(response)); packet += strlen(response);