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
This commit is contained in:
Jocelyn Bohr 2016-11-22 17:39:21 -08:00 committed by Kever Yang
parent 0eda6822d0
commit aba554cc34
6 changed files with 108 additions and 10 deletions

View File

@ -6,6 +6,9 @@
#include <common.h>
#include <fastboot.h>
#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
#include <net/fastboot.h>
#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
}

View File

@ -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);

View File

@ -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)
;
}

View File

@ -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_ */

View File

@ -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*);
/**********************************************************************/

View File

@ -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);