diff --git a/src/barebox-state/backend_bucket_circular.c b/src/barebox-state/backend_bucket_circular.c index 926ef49..64e9be2 100644 --- a/src/barebox-state/backend_bucket_circular.c +++ b/src/barebox-state/backend_bucket_circular.c @@ -47,6 +47,8 @@ u8 *current_data; ssize_t current_data_len; + bool force_rewrite; /* In case of degradation, force a rewrite */ + /* For outputs */ struct device_d *dev; }; @@ -85,7 +87,20 @@ ret); return ret; } - } else if (ret < 0 && ret != -EUCLEAN) { + /* + * Fill with invalid data so that the next write is done + * behind this area + */ + memset(buf, 0, len); + circ->force_rewrite = true; + circ->write_area = 0; + dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n", + circ->eraseblock); + } else if (ret == -EUCLEAN) { + circ->force_rewrite = true; + dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n", + circ->eraseblock); + } else if (ret < 0) { dev_err(circ->dev, "Failed to read PEB %u, %d\n", circ->eraseblock, ret); return ret; @@ -111,6 +126,7 @@ ret); return ret; } + circ->force_rewrite = true; } else if (ret < 0 && ret != -EUCLEAN) { dev_err(circ->dev, "Failed to write PEB %u, %d\n", circ->eraseblock, ret); @@ -130,6 +146,8 @@ { int ret; off_t offset = suboffset; + struct mtd_ecc_stats stat1, stat2; + bool nostats = false; offset += (off_t)circ->eraseblock * circ->mtd->erasesize; @@ -143,6 +161,10 @@ dev_dbg(circ->dev, "Read state from %ld length %zd\n", offset, len); + ret = ioctl(circ->fd, ECCGETSTATS, &stat1); + if (ret) + nostats = true; + ret = read_full(circ->fd, buf, len); if (ret < 0) { dev_err(circ->dev, "Failed to read circular storage len %zd, %d\n", @@ -151,6 +173,23 @@ return ret; } + if (nostats) + return 0; + + ret = ioctl(circ->fd, ECCGETSTATS, &stat2); + if (ret) + return 0; + + if (stat2.failed - stat1.failed > 0) { + circ->force_rewrite = true; + dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n", + circ->eraseblock); + } else if (stat2.corrected - stat1.corrected > 0) { + circ->force_rewrite = true; + dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n", + circ->eraseblock); + } + return 0; } @@ -329,7 +368,8 @@ if (circ->current_data) { dev_dbg(circ->dev, "Comparing cached data, writing %zd bytes, cached %zd bytes\n", written_length, circ->current_data_len); - if (written_length == circ->current_data_len && + if (!circ->force_rewrite && + written_length == circ->current_data_len && !memcmp(circ->current_data, write_buf, written_length)) { dev_dbg(circ->dev, "Data already on device, not writing again\n"); goto out_free; @@ -382,6 +422,7 @@ circ->current_data = write_buf; circ->current_data_len = written_length; write_buf = NULL; + circ->force_rewrite = false; out_free: if (write_buf) @@ -518,12 +559,15 @@ goto out_free; } - circ->bucket.init = state_backend_bucket_circular_init; circ->bucket.read = state_backend_bucket_circular_read; circ->bucket.write = state_backend_bucket_circular_write; circ->bucket.free = state_backend_bucket_circular_free; *bucket = &circ->bucket; + ret = state_backend_bucket_circular_init(*bucket); + if (ret) + goto out_free; + return 0; out_free: