From 35c474e8c0c6dcbdb10e6065184ed4516228d9dd Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 8 Jul 2013 09:42:54 -0700 Subject: recovery: try to write EMMC partitions more reliably Nexus 4 has flash errors that manifest during large writes (eg, of the radio partition). Writes of some blocks seem to be dropped silently, without any errors being returned to the user level. Make two changes to the partition-writing code: - break it up into 1MB writes instead of writing partitions with a single fwrite() call. Pause for 50ms in between every chunk. - read the partition back after writing and verify that we read what we wrote. Drop caches before reading so we (hopefully) are reading off the actual flash and not some cache. Neither of these should be necessary. Bug: 9602014 Change-Id: Ice2e24dd4c11f1a57968277b5eb1468c772f6f63 --- applypatch/applypatch.c | 78 +++++++++++++++++++++++++++++++++++++++++++++---- applypatch/applypatch.h | 2 +- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 69f8633ab..2f134df05 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -421,18 +421,84 @@ int WriteToPartition(unsigned char* data, size_t len, break; case EMMC: - ; - FILE* f = fopen(partition, "wb"); - if (fwrite(data, 1, len, f) != len) { - printf("short write writing to %s (%s)\n", - partition, strerror(errno)); + { + size_t start = 0; + int success = 0; + FILE* f = fopen(partition, "r+b"); + if (f == NULL) { + printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } + int attempt; + + for (attempt = 0; attempt < 3; ++attempt) { + printf("write %s attempt %d start at %d\n", partition, attempt+1, start); + fseek(f, start, SEEK_SET); + while (start < len) { + size_t to_write = len - start; + if (to_write > (1<<20)) to_write = 1<<20; + + if (fwrite(data+start, 1, to_write, f) != to_write) { + printf("short write writing to %s (%s)\n", + partition, strerror(errno)); + return -1; + } + start += to_write; + if (start < len) { + usleep(50000); // 50 ms + } + } + + // drop caches so our subsequent verification read + // won't just be reading the cache. + sync(); + FILE* dc = fopen("/proc/sys/vm/drop_caches", "w"); + fwrite("3\n", 2, 1, dc); + fclose(dc); + sleep(1); + printf(" caches dropped\n"); + + // verify + fseek(f, 0, SEEK_SET); + unsigned char buffer[4096]; + start = len; + size_t p; + for (p = 0; p < len; p += sizeof(buffer)) { + size_t to_read = len - p; + if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + + if (fread(buffer, 1, to_read, f) != to_read) { + printf("short verify read %s at %d: %s\n", + partition, p, strerror(errno)); + start = p; + break; + } + + if (memcmp(buffer, data+p, to_read)) { + printf("verification failed starting at %d\n", p); + start = p; + break; + } + } + + if (start == len) { + printf("verification read succeeded (attempt %d)\n", attempt+1); + success = true; + break; + } + } + + if (!success) { + printf("failed to verify after all attempts\n"); + return -1; + } + if (fclose(f) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } break; + } } free(copy); @@ -473,7 +539,7 @@ int ParseSha1(const char* str, uint8_t* digest) { // Search an array of sha1 strings for one matching the given sha1. // Return the index of the match on success, or -1 if no match is // found. -int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches) { int i; uint8_t patch_sha1[SHA_DIGEST_SIZE]; diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index d1a023293..f1f13a100 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -65,7 +65,7 @@ int LoadFileContents(const char* filename, FileContents* file, int retouch_flag); int SaveFileContents(const char* filename, const FileContents* file); void FreeFileContents(FileContents* file); -int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches); // bsdiff.c -- cgit v1.2.3 From c6ab95e9d17fd174df1dd07076fbf6251480ba09 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 9 Jul 2013 10:34:46 -0700 Subject: recovery: write partitions more conservatively Write and verify partitions using write(2) and read(2) rather than the stdio functions. Read and write in 4kb blocks. When writing, fsync() every 1MB. Bug: 9602014 Change-Id: Ie98ce38e857786fc0f4ebf36bb5ffc93b41bc96f --- applypatch/applypatch.c | 70 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 2f134df05..3f67c32df 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,42 +424,50 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - FILE* f = fopen(partition, "r+b"); - if (f == NULL) { + int fd = open(partition, O_RDWR); + if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } int attempt; - for (attempt = 0; attempt < 3; ++attempt) { - printf("write %s attempt %d start at %d\n", partition, attempt+1, start); - fseek(f, start, SEEK_SET); + for (attempt = 0; attempt < 10; ++attempt) { + off_t next_sync = start + (1<<20); + printf("raw write %s attempt %d start at %d\n", partition, attempt+1, start); + lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; - if (to_write > (1<<20)) to_write = 1<<20; - - if (fwrite(data+start, 1, to_write, f) != to_write) { - printf("short write writing to %s (%s)\n", - partition, strerror(errno)); - return -1; + if (to_write > 4096) to_write = 4096; + + ssize_t written = write(fd, data+start, to_write); + if (written < 0) { + if (errno == EINTR) { + written = 0; + } else { + printf("failed write writing to %s (%s)\n", + partition, strerror(errno)); + return -1; + } } - start += to_write; - if (start < len) { - usleep(50000); // 50 ms + start += written; + if (start >= next_sync) { + fsync(fd); + next_sync = start + (1<<20); } } + fsync(fd); // drop caches so our subsequent verification read // won't just be reading the cache. sync(); - FILE* dc = fopen("/proc/sys/vm/drop_caches", "w"); - fwrite("3\n", 2, 1, dc); - fclose(dc); + int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); + write(dc, "3\n", 2); + close(dc); sleep(1); printf(" caches dropped\n"); // verify - fseek(f, 0, SEEK_SET); + lseek(fd, 0, SEEK_SET); unsigned char buffer[4096]; start = len; size_t p; @@ -467,11 +475,23 @@ int WriteToPartition(unsigned char* data, size_t len, size_t to_read = len - p; if (to_read > sizeof(buffer)) to_read = sizeof(buffer); - if (fread(buffer, 1, to_read, f) != to_read) { - printf("short verify read %s at %d: %s\n", - partition, p, strerror(errno)); - start = p; - break; + size_t so_far = 0; + while (so_far < to_read) { + ssize_t read_count = read(fd, buffer+so_far, to_read-so_far); + if (read_count < 0) { + if (errno == EINTR) { + read_count = 0; + } else { + printf("verify read error %s at %d: %s\n", + partition, p, strerror(errno)); + return -1; + } + } + if (read_count < to_read) { + printf("short verify read %s at %d: %d %d %s\n", + partition, p, read_count, to_read, strerror(errno)); + } + so_far += read_count; } if (memcmp(buffer, data+p, to_read)) { @@ -486,6 +506,8 @@ int WriteToPartition(unsigned char* data, size_t len, success = true; break; } + + sleep(2); } if (!success) { @@ -493,7 +515,7 @@ int WriteToPartition(unsigned char* data, size_t len, return -1; } - if (fclose(f) != 0) { + if (close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } -- cgit v1.2.3