diff options
Diffstat (limited to 'src/barebox-state/backend_storage.c')
-rw-r--r-- | src/barebox-state/backend_storage.c | 112 |
1 files changed, 67 insertions, 45 deletions
diff --git a/src/barebox-state/backend_storage.c b/src/barebox-state/backend_storage.c index 42792ef..dbc403d 100644 --- a/src/barebox-state/backend_storage.c +++ b/src/barebox-state/backend_storage.c @@ -70,22 +70,29 @@ int state_storage_write(struct state_backend_storage *storage, return -EIO; } -/** - * state_storage_restore_consistency - Restore consistency on all storage backends - * @param storage Storage object - * @param buf Buffer with valid data that should be on all buckets after this operation - * @param len Length of the buffer - * @return 0 on success, -errno otherwise - * - * This function brings valid data onto all buckets we have to ensure that all - * data copies are in sync. In the current implementation we just write the data - * to all buckets. Bucket implementations that need to keep the number of writes - * low, can read their own copy first and compare it. - */ -int state_storage_restore_consistency(struct state_backend_storage *storage, - const void * buf, ssize_t len) +static int bucket_refresh(struct state_backend_storage *storage, + struct state_backend_storage_bucket *bucket, void *buf, ssize_t len) { - return state_storage_write(storage, buf, len); + int ret; + + if (bucket->needs_refresh) + goto refresh; + + if (bucket->len != len) + goto refresh; + + if (memcmp(bucket->buf, buf, len)) + goto refresh; + + return 0; + +refresh: + ret = bucket->write(bucket, buf, len); + + if (ret) + dev_warn(storage->dev, "Failed to restore bucket\n"); + + return ret; } /** @@ -95,7 +102,6 @@ int state_storage_restore_consistency(struct state_backend_storage *storage, * @param magic state magic value * @param buf The newly allocated data area will be stored in this pointer * @param len The resulting length of the buffer - * @param len_hint Hint of how big the data may be. * @return 0 on success, -errno otherwise. buf and len will be set to valid * values on success. * @@ -108,12 +114,18 @@ int state_storage_read(struct state_backend_storage *storage, struct state_backend_format *format, uint32_t magic, void **buf, ssize_t *len) { - struct state_backend_storage_bucket *bucket; + struct state_backend_storage_bucket *bucket, *bucket_used = NULL; int ret; + /* + * Iterate over all buckets. The first valid one we find is the + * one we want to use. + */ list_for_each_entry(bucket, &storage->buckets, bucket_list) { - ret = bucket->read(bucket, buf, len); - if (ret) { + ret = bucket->read(bucket, &bucket->buf, &bucket->len); + if (ret == -EUCLEAN) { + bucket->needs_refresh = 1; + } else if (ret) { dev_warn(storage->dev, "Failed to read from state backend bucket, trying next, %d\n", ret); continue; @@ -123,22 +135,46 @@ int state_storage_read(struct state_backend_storage *storage, * Verify the buffer crcs. The buffer length is passed in the len argument, * .verify overwrites it with the length actually used. */ - ret = format->verify(format, magic, *buf, len); - if (!ret) { - goto found; - } - free(*buf); - dev_warn(storage->dev, "Failed to verify read copy, trying next bucket, %d\n", - ret); + ret = format->verify(format, magic, bucket->buf, &bucket->len); + if (!ret && !bucket_used) + bucket_used = bucket; + + if (ret) + dev_warn(storage->dev, "Failed to verify read copy, trying next bucket, %d\n", + ret); + } + + if (!bucket_used) { + dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); + + return -ENOENT; } - dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); + /* + * Restore/refresh all buckets except the one we currently use (in case + * it's the only usable bucket at the moment) + */ + list_for_each_entry(bucket, &storage->buckets, bucket_list) { + if (bucket == bucket_used) + continue; + + ret = bucket_refresh(storage, bucket, bucket_used->buf, bucket_used->len); + + /* Free buffer from the unused buckets */ + free(bucket->buf); + bucket->buf = NULL; + } - return -ENOENT; + /* + * Restore/refresh the bucket we currently use + */ + ret = bucket_refresh(storage, bucket_used, bucket_used->buf, bucket_used->len); -found: - /* A failed restore consistency is not a failure of reading the state */ - state_storage_restore_consistency(storage, *buf, *len); + *buf = bucket_used->buf; + *len = bucket_used->len; + + /* buffer from the used bucket is passed to the caller, do not free */ + bucket_used->buf = NULL; return 0; } @@ -219,13 +255,6 @@ static int state_storage_mtd_buckets_init(struct state_backend_storage *storage, continue; } - ret = state_backend_bucket_cached_create(storage->dev, bucket, - &bucket); - if (ret) { - dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n", - ret); - } - list_add_tail(&bucket->bucket_list, &storage->buckets); ++nr_copies; if (nr_copies >= desired_copies) @@ -285,13 +314,6 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage continue; } - ret = state_backend_bucket_cached_create(storage->dev, bucket, - &bucket); - if (ret) { - dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n", - ret); - } - list_add_tail(&bucket->bucket_list, &storage->buckets); ++nr_copies; } |