Skip to content

Commit eff5227

Browse files
committed
Fix max_keys reset limitation
Persist the max_keys value through a soft-reset, also prohibit max_keys set under predefined default value (16)
1 parent 85a741e commit eff5227

File tree

3 files changed

+85
-24
lines changed

3 files changed

+85
-24
lines changed

features/storage/nvstore/TESTS/nvstore/functionality/main.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,13 @@ static void thread_test_worker()
449449
static void nvstore_multi_thread_test()
450450
{
451451
#ifdef MBED_CONF_RTOS_PRESENT
452-
int i;
452+
int i, result;
453453
uint16_t size;
454454
uint16_t key;
455455
int ret;
456456
char *dummy;
457-
uint16_t max_possible_keys;
457+
uint16_t max_possible_keys, actual_len_bytes = 0;
458+
char test[] = "Test", read_buf[10] = {};
458459

459460
NVStore &nvstore = NVStore::get_instance();
460461

@@ -483,6 +484,10 @@ static void nvstore_multi_thread_test()
483484
TEST_SKIP_UNLESS_MESSAGE(max_possible_keys >= max_possible_keys_threshold,
484485
"Max possible keys below threshold. Test skipped.");
485486

487+
nvstore.set_max_keys(max_test_keys + 1);
488+
result = nvstore.set(max_test_keys, strlen(test), test);
489+
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
490+
486491
thr_test_data->stop_threads = false;
487492
for (key = 0; key < thr_test_data->max_keys; key++) {
488493
for (i = 0; i < thr_test_num_buffs; i++) {
@@ -527,6 +532,12 @@ static void nvstore_multi_thread_test()
527532
for (key = 0; key < thr_test_data->max_keys; key++) {
528533
thread_test_check_key(key);
529534
}
535+
536+
result = nvstore.get(max_test_keys, sizeof(read_buf), read_buf, actual_len_bytes);
537+
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
538+
TEST_ASSERT_EQUAL(strlen(test), actual_len_bytes);
539+
TEST_ASSERT_EQUAL_UINT8_ARRAY(test, read_buf, strlen(test));
540+
530541
goto clean;
531542

532543
mem_fail:

features/storage/nvstore/source/nvstore.cpp

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ static const unsigned int owner_bit_pos = 12;
6161

6262
typedef struct {
6363
uint16_t version;
64-
uint16_t reserved1;
65-
uint32_t reserved2;
64+
uint16_t max_keys;
65+
uint32_t reserved;
6666
} master_record_data_t;
6767

6868
static const uint32_t min_area_size = 4096;
@@ -171,11 +171,54 @@ uint16_t NVStore::get_max_possible_keys()
171171

172172
void NVStore::set_max_keys(uint16_t num_keys)
173173
{
174+
uint16_t key = 0, old_max_keys = 0;
175+
174176
MBED_ASSERT(num_keys < get_max_possible_keys());
177+
178+
if (num_keys < NVSTORE_NUM_PREDEFINED_KEYS) {
179+
return;
180+
}
181+
182+
if (!_init_done) {
183+
int ret = init();
184+
if (ret != NVSTORE_SUCCESS) {
185+
return;
186+
}
187+
}
188+
189+
_mutex->lock();
190+
191+
//check if there are values that might be discarded
192+
if (num_keys < _max_keys) {
193+
for (key = num_keys; key < _max_keys; key++) {
194+
if (_offset_by_key[key] != 0) {
195+
return;
196+
}
197+
}
198+
}
199+
200+
old_max_keys = _max_keys;
175201
_max_keys = num_keys;
176-
// User is allowed to change number of keys. As this affects init, need to deinitialize now.
177-
// Don't call init right away - it is lazily called by get/set functions if needed.
178-
deinit();
202+
203+
// Invoke GC to write new max_keys to master record
204+
garbage_collection(no_key, 0, 0, 0, NULL, std::min(_max_keys, old_max_keys));
205+
206+
// Reallocate _offset_by_key with new size
207+
if (_max_keys != old_max_keys) {
208+
// Reallocate _offset_by_key with new size
209+
uint32_t *old_offset_by_key = (uint32_t *) _offset_by_key;
210+
uint32_t *new_offset_by_key = new uint32_t[_max_keys];
211+
MBED_ASSERT(new_offset_by_key);
212+
213+
// Copy old content to new table
214+
memset(new_offset_by_key, 0, sizeof(uint32_t) * _max_keys);
215+
memcpy(new_offset_by_key, old_offset_by_key, sizeof(uint32_t) * std::min(_max_keys, old_max_keys));
216+
217+
_offset_by_key = new_offset_by_key;
218+
delete[] old_offset_by_key;
219+
}
220+
221+
_mutex->unlock();
179222
}
180223

181224
int NVStore::flash_read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf)
@@ -444,8 +487,8 @@ int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_
444487
master_record_data_t master_rec;
445488

446489
master_rec.version = version;
447-
master_rec.reserved1 = 0;
448-
master_rec.reserved2 = 0;
490+
master_rec.max_keys = _max_keys;
491+
master_rec.reserved = 0;
449492
return write_record(area, 0, master_record_key, 0, 0, sizeof(master_rec),
450493
&master_rec, next_offset);
451494
}
@@ -518,7 +561,7 @@ int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_of
518561
return NVSTORE_SUCCESS;
519562
}
520563

521-
int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf)
564+
int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf, uint16_t num_keys)
522565
{
523566
uint32_t curr_offset, new_area_offset, next_offset, curr_owner;
524567
int ret;
@@ -542,7 +585,7 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uin
542585

543586
// Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist)
544587
// to the other area.
545-
for (key = 0; key < _max_keys; key++) {
588+
for (key = 0; key < num_keys; key++) {
546589
curr_offset = _offset_by_key[key];
547590
uint16_t save_flags = curr_offset & offs_by_key_flag_mask & ~offs_by_key_area_mask;
548591
curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1;
@@ -579,7 +622,6 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uin
579622
return ret;
580623
}
581624

582-
583625
int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size,
584626
int validate_only)
585627
{
@@ -684,7 +726,7 @@ int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t f
684726

685727
// If we cross the area limit, we need to invoke GC.
686728
if (new_free_space >= _size) {
687-
ret = garbage_collection(key, flags, owner, buf_size, buf);
729+
ret = garbage_collection(key, flags, owner, buf_size, buf, _max_keys);
688730
_mutex->unlock();
689731
return ret;
690732
}
@@ -800,6 +842,7 @@ int NVStore::init()
800842
uint16_t key;
801843
uint16_t flags;
802844
uint16_t versions[NVSTORE_NUM_AREAS];
845+
uint16_t keys[NVSTORE_NUM_AREAS];
803846
uint16_t actual_size;
804847
uint8_t owner;
805848

@@ -818,13 +861,6 @@ int NVStore::init()
818861
return NVSTORE_SUCCESS;
819862
}
820863

821-
_offset_by_key = new uint32_t[_max_keys];
822-
MBED_ASSERT(_offset_by_key);
823-
824-
for (key = 0; key < _max_keys; key++) {
825-
_offset_by_key[key] = 0;
826-
}
827-
828864
_mutex = new PlatformMutex;
829865
MBED_ASSERT(_mutex);
830866

@@ -841,10 +877,12 @@ int NVStore::init()
841877

842878
calc_validate_area_params();
843879

880+
//retrieve max keys from master record
844881
for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) {
845882
area_state[area] = NVSTORE_AREA_STATE_NONE;
846883
free_space_offset_of_area[area] = 0;
847884
versions[area] = 0;
885+
keys[area] = 0;
848886

849887
_size = std::min(_size, _flash_area_params[area].size);
850888

@@ -878,6 +916,7 @@ int NVStore::init()
878916
continue;
879917
}
880918
versions[area] = master_rec.version;
919+
keys[area] = master_rec.max_keys;
881920

882921
// Place _free_space_offset after the master record (for the traversal,
883922
// which takes place after this loop).
@@ -888,6 +927,17 @@ int NVStore::init()
888927
// that we found our active area.
889928
_active_area = area;
890929
_active_area_version = versions[area];
930+
if (!keys[area]) {
931+
keys[area] = NVSTORE_NUM_PREDEFINED_KEYS;
932+
}
933+
_max_keys = keys[area];
934+
}
935+
936+
_offset_by_key = new uint32_t[_max_keys];
937+
MBED_ASSERT(_offset_by_key);
938+
939+
for (key = 0; key < _max_keys; key++) {
940+
_offset_by_key[key] = 0;
891941
}
892942

893943
// In case we have two empty areas, arbitrarily assign 0 to the active one.
@@ -920,9 +970,9 @@ int NVStore::init()
920970
MBED_ASSERT(ret == NVSTORE_SUCCESS);
921971

922972
// In case we have a faulty record, this probably means that the system crashed when written.
923-
// Perform a garbage collection, to make the the other area valid.
973+
// Perform a garbage collection, to make the other area valid.
924974
if (!valid) {
925-
ret = garbage_collection(no_key, 0, 0, 0, NULL);
975+
ret = garbage_collection(no_key, 0, 0, 0, NULL, _max_keys);
926976
break;
927977
}
928978
if (flags & delete_item_flag) {

features/storage/nvstore/source/nvstore.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,6 @@ class NVStore : private mbed::NonCopyable<NVStore> {
272272
*/
273273
int get_area_params(uint8_t area, uint32_t &address, size_t &size);
274274

275-
276275
private:
277276
typedef struct {
278277
uint32_t address;
@@ -417,10 +416,11 @@ class NVStore : private mbed::NonCopyable<NVStore> {
417416
* @param[in] owner Owner.
418417
* @param[in] buf_size Data size (bytes).
419418
* @param[in] buf Data buffer.
419+
* @param[in] num_keys number of keys.
420420
*
421421
* @returns 0 for success, nonzero for failure.
422422
*/
423-
int garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf);
423+
int garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf, uint16_t num_keys);
424424

425425
/**
426426
* @brief Actual logics of get API (covers also get size API).

0 commit comments

Comments
 (0)