Merge pull request #6480 from davidsaada/david_nvstore_alloc_key

NVStore: add the allocate_key API (instead of set_alloc_key)
pull/6976/head
Cruz Monrreal 2018-05-21 17:32:00 -05:00 committed by GitHub
commit db73ed0751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 167 additions and 63 deletions

View File

@ -133,9 +133,17 @@ static void nvstore_basic_functionality_test()
result = nvstore.set(19, 10, &(nvstore_testing_buf_set[3]));
TEST_ASSERT_EQUAL(NVSTORE_ALREADY_EXISTS, result);
result = nvstore.set_alloc_key(key, 17, &(nvstore_testing_buf_set[3]));
result = nvstore.allocate_key(key, 3);
TEST_ASSERT_EQUAL(NVSTORE_NUM_PREDEFINED_KEYS, key);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
result = nvstore.set(NVSTORE_NUM_PREDEFINED_KEYS, 17, &(nvstore_testing_buf_set[3]));
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
result = nvstore.allocate_key(key, 3);
TEST_ASSERT_EQUAL(NVSTORE_NUM_PREDEFINED_KEYS + 1, key);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
result = nvstore.set(NVSTORE_NUM_PREDEFINED_KEYS + 1, 17, &(nvstore_testing_buf_set[3]));
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
// Make sure set items are also gotten OK after reset
result = nvstore.deinit();
@ -168,6 +176,20 @@ static void nvstore_basic_functionality_test()
TEST_ASSERT_EQUAL(17, actual_len_bytes);
TEST_ASSERT_EQUAL_UINT8_ARRAY(&nvstore_testing_buf_set[3], nvstore_testing_buf_get, 17);
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
TEST_ASSERT_EQUAL(17, actual_len_bytes);
TEST_ASSERT_EQUAL_UINT8_ARRAY(&nvstore_testing_buf_set[3], nvstore_testing_buf_get, 17);
result = nvstore.free_all_keys_by_owner(3);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS, 64, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result);
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result);
result = nvstore.get(10, 65, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, result);
TEST_ASSERT_EQUAL(64, actual_len_bytes);
@ -346,6 +368,12 @@ static void nvstore_basic_functionality_test()
TEST_ASSERT_EQUAL(53, actual_len_bytes);
TEST_ASSERT_EQUAL_UINT8_ARRAY(&(nvstore_testing_buf_set[10]), nvstore_testing_buf_get, 53);
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS, 64, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result);
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result);
delete[] nvstore_testing_buf_set;
delete[] nvstore_testing_buf_get;
}

View File

@ -41,15 +41,24 @@ static const uint16_t last_reserved_key = master_record_key;
typedef struct
{
uint16_t key_and_flags;
uint16_t size;
uint16_t size_and_owner;
uint32_t crc;
} nvstore_record_header_t;
static const uint32_t offs_by_key_area_mask = 0x80000000UL;
static const uint32_t offs_by_key_set_once_mask = 0x40000000UL;
static const uint32_t offs_by_key_flag_mask = 0xC0000000UL;
static const unsigned int offs_by_key_area_bit_pos = 31;
static const unsigned int offs_by_key_set_once_bit_pos = 30;
static const uint32_t offs_by_key_area_mask = 0x00000001UL;
static const uint32_t offs_by_key_set_once_mask = 0x00000002UL;
static const uint32_t offs_by_key_allocated_mask = 0x00000004UL;
static const uint32_t offs_by_key_flag_mask = 0x00000007UL;
static const uint32_t offs_by_key_offset_mask = 0x0FFFFFF8UL;
static const uint32_t offs_by_key_owner_mask = 0xF0000000UL;
static const unsigned int offs_by_key_area_bit_pos = 0;
static const unsigned int offs_by_key_set_once_bit_pos = 1;
static const unsigned int offs_by_key_owner_bit_pos = 28;
static const uint16_t size_mask = 0x0FFF;
static const uint16_t owner_mask = 0xF000;
static const unsigned int owner_bit_pos = 12;
typedef struct {
uint16_t version;
@ -58,6 +67,7 @@ typedef struct {
} master_record_data_t;
static const uint32_t min_area_size = 4096;
static const uint32_t max_data_size = 4096;
static const int num_write_retries = 16;
@ -133,7 +143,6 @@ NVStore::NVStore() : _init_done(0), _init_attempts(0), _active_area(0), _max_key
_active_area_version(0), _free_space_offset(0), _size(0), _mutex(0), _offset_by_key(0), _flash(0),
_min_prog_size(0), _page_buf(0)
{
memset(_flash_area_params, 0, sizeof(_flash_area_params));
}
NVStore::~NVStore()
@ -305,7 +314,7 @@ int NVStore::calc_empty_space(uint8_t area, uint32_t &offset)
int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf,
uint16_t &actual_size, int validate_only, int &valid,
uint16_t &key, uint16_t &flags, uint32_t &next_offset)
uint16_t &key, uint16_t &flags, uint8_t &owner, uint32_t &next_offset)
{
uint8_t int_buf[128];
void *buf_ptr;
@ -326,13 +335,14 @@ int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void
actual_size = 0;
key = header.key_and_flags & ~header_flag_mask;
flags = header.key_and_flags & header_flag_mask;
owner = (header.size_and_owner & owner_mask) >> owner_bit_pos;
if ((key >= _max_keys) && (key != master_record_key)) {
valid = 0;
return NVSTORE_SUCCESS;
}
data_size = header.size;
data_size = header.size_and_owner & size_mask;
offset += sizeof(header);
// In case of validate only enabled, we use our internal buffer for data reading,
@ -367,13 +377,13 @@ int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void
return NVSTORE_SUCCESS;
}
actual_size = header.size;
actual_size = header.size_and_owner & size_mask;
next_offset = align_up(offset, _min_prog_size);
return NVSTORE_SUCCESS;
}
int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags,
int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, uint8_t owner,
uint32_t data_size, const void *data_buf, uint32_t &next_offset)
{
nvstore_record_header_t header;
@ -382,7 +392,7 @@ int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t
uint8_t *prog_buf;
header.key_and_flags = key | flags;
header.size = data_size;
header.size_and_owner = data_size | (owner << owner_bit_pos);
header.crc = 0; // Satisfy compiler
crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header);
if (data_size) {
@ -435,7 +445,7 @@ int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_
master_rec.version = version;
master_rec.reserved1 = 0;
master_rec.reserved2 = 0;
return write_record(area, 0, master_record_key, 0, sizeof(master_rec),
return write_record(area, 0, master_record_key, 0, 0, sizeof(master_rec),
&master_rec, next_offset);
}
@ -465,7 +475,7 @@ int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_of
}
header = (nvstore_record_header_t *) read_buf;
record_size = sizeof(nvstore_record_header_t) + header->size;
record_size = sizeof(nvstore_record_header_t) + (header->size_and_owner & size_mask);
// No need to copy records whose flags indicate deletion
if (header->key_and_flags & delete_item_flag) {
@ -507,7 +517,7 @@ int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_of
return NVSTORE_SUCCESS;
}
int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf)
int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf)
{
uint32_t curr_offset, new_area_offset, next_offset;
int ret;
@ -519,7 +529,7 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size,
// otherwise we may either write it twice (if already included), or lose it in case we decide
// to skip it at garbage collection phase (and the system crashes).
if ((key != no_key) && !(flags & delete_item_flag)) {
ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset);
ret = write_record(1 - _active_area, new_area_offset, key, 0, owner, buf_size, buf, next_offset);
if (ret != NVSTORE_SUCCESS) {
return ret;
}
@ -532,7 +542,7 @@ int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size,
// to the other area.
for (key = 0; key < _max_keys; key++) {
curr_offset = _offset_by_key[key];
uint16_t save_flags = curr_offset & offs_by_key_area_mask;
uint16_t save_flags = curr_offset & offs_by_key_flag_mask & ~offs_by_key_area_mask;
curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1;
curr_offset &= ~offs_by_key_flag_mask;
if ((!curr_offset) || (curr_area != _active_area)) {
@ -574,7 +584,7 @@ int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual
int valid;
uint32_t record_offset, next_offset;
uint16_t read_type, flags;
uint8_t area;
uint8_t area, owner;
if (!_init_done) {
ret = init();
@ -596,19 +606,19 @@ int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual
}
_mutex->lock();
record_offset = _offset_by_key[key];
area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1;
record_offset &= offs_by_key_offset_mask;
if (!record_offset) {
_mutex->unlock();
return NVSTORE_NOT_FOUND;
}
area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1;
record_offset &= ~offs_by_key_flag_mask;
ret = read_record(area, record_offset, buf_size, buf,
actual_size, validate_only, valid,
read_type, flags, next_offset);
read_type, flags, owner, next_offset);
if ((ret == NVSTORE_SUCCESS) && !valid) {
ret = NVSTORE_DATA_CORRUPT;
}
@ -627,11 +637,12 @@ int NVStore::get_item_size(uint16_t key, uint16_t &actual_size)
return do_get(key, 0, NULL, actual_size, 1);
}
int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags)
int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags)
{
int ret = NVSTORE_SUCCESS;
uint32_t record_offset, record_size, new_free_space;
uint32_t next_offset;
uint8_t owner;
if (!_init_done) {
ret = init();
@ -640,11 +651,11 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t
}
}
if ((key != no_key) && (key >= _max_keys)) {
if (key >= _max_keys) {
return NVSTORE_BAD_VALUE;
}
if ((key == no_key) && (flags & delete_item_flag)) {
if (buf_size >= max_data_size) {
return NVSTORE_BAD_VALUE;
}
@ -652,11 +663,11 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t
buf_size = 0;
}
if ((flags & delete_item_flag) && !_offset_by_key[key]) {
if ((flags & delete_item_flag) && !(_offset_by_key[key] & offs_by_key_offset_mask)) {
return NVSTORE_NOT_FOUND;
}
if ((key != no_key) && (_offset_by_key[key] & offs_by_key_set_once_mask)) {
if (_offset_by_key[key] & offs_by_key_set_once_mask) {
return NVSTORE_ALREADY_EXISTS;
}
@ -664,40 +675,31 @@ int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t
_mutex->lock();
if (key == no_key) {
for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) {
if (!_offset_by_key[key]) {
break;
}
}
if (key == _max_keys) {
return NVSTORE_NO_FREE_KEY;
}
}
owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos;
new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size);
record_offset = new_free_space - record_size;
// If we cross the area limit, we need to invoke GC.
if (new_free_space >= _size) {
ret = garbage_collection(key, flags, buf_size, buf);
ret = garbage_collection(key, flags, owner, buf_size, buf);
_mutex->unlock();
return ret;
}
// Now write the record
ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset);
ret = write_record(_active_area, record_offset, key, flags, owner, buf_size, buf, next_offset);
if (ret != NVSTORE_SUCCESS) {
_mutex->unlock();
return ret;
}
// Update _offset_by_key. High bit indicates area.
// Update _offset_by_key
if (flags & delete_item_flag) {
_offset_by_key[key] = 0;
} else {
_offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) |
(((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
(((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) |
(owner << offs_by_key_owner_bit_pos);
}
_mutex->unlock();
@ -715,10 +717,67 @@ int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf)
return do_set(key, buf_size, buf, set_once_flag);
}
int NVStore::set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf)
int NVStore::allocate_key(uint16_t &key, uint8_t owner)
{
key = no_key;
return do_set(key, buf_size, buf, 0);
int ret = NVSTORE_SUCCESS;
if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) {
return NVSTORE_BAD_VALUE;
}
if (!_init_done) {
ret = init();
if (ret != NVSTORE_SUCCESS) {
return ret;
}
}
_mutex->lock();
for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) {
if (!_offset_by_key[key]) {
break;
}
}
if (key == _max_keys) {
ret = NVSTORE_NO_FREE_KEY;
} else {
_offset_by_key[key] |= offs_by_key_allocated_mask | (owner << offs_by_key_owner_bit_pos);
}
_mutex->unlock();
return ret;
}
int NVStore::free_all_keys_by_owner(uint8_t owner)
{
int ret = NVSTORE_SUCCESS;
if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) {
return NVSTORE_BAD_VALUE;
}
if (!_init_done) {
ret = init();
if (ret != NVSTORE_SUCCESS) {
return ret;
}
}
_mutex->lock();
for (uint16_t key = 0; key < _max_keys; key++) {
uint8_t curr_owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos;
if (curr_owner != owner) {
continue;
}
ret = remove(key);
if (ret) {
break;
}
}
_mutex->unlock();
return ret;
}
int NVStore::remove(uint16_t key)
@ -739,6 +798,7 @@ int NVStore::init()
uint16_t flags;
uint16_t versions[NVSTORE_NUM_AREAS];
uint16_t actual_size;
uint8_t owner;
if (_init_done) {
return NVSTORE_SUCCESS;
@ -799,7 +859,7 @@ int NVStore::init()
master_record_data_t master_rec;
ret = read_record(area, 0, sizeof(master_rec), &master_rec,
actual_size, 0, valid,
key, flags, next_offset);
key, flags, owner, next_offset);
MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL));
if (ret == NVSTORE_BUFF_TOO_SMALL) {
// Buf too small error means that we have a corrupt master record -
@ -853,20 +913,21 @@ int NVStore::init()
while (_free_space_offset < free_space_offset_of_area[_active_area]) {
ret = read_record(_active_area, _free_space_offset, 0, NULL,
actual_size, 1, valid,
key, flags, next_offset);
key, flags, owner, next_offset);
MBED_ASSERT(ret == NVSTORE_SUCCESS);
// In case we have a faulty record, this probably means that the system crashed when written.
// Perform a garbage collection, to make the the other area valid.
if (!valid) {
ret = garbage_collection(no_key, 0, 0, NULL);
ret = garbage_collection(no_key, 0, 0, 0, NULL);
break;
}
if (flags & delete_item_flag) {
_offset_by_key[key] = 0;
} else {
_offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) |
(((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos);
(((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) |
(owner << offs_by_key_owner_bit_pos);
}
_free_space_offset = next_offset;
}

View File

@ -53,6 +53,12 @@ typedef enum {
NVSTORE_NUM_PREDEFINED_KEYS
} nvstore_predefined_keys_e;
typedef enum {
NVSTORE_UNSPECIFIED_OWNER = 0,
// All owners (by features) should be specified here.
NVSTORE_MAX_OWNERS = 16
} nvstore_owner_e;
#ifndef NVSTORE_MAX_KEYS
#define NVSTORE_MAX_KEYS ((uint16_t)NVSTORE_NUM_PREDEFINED_KEYS)
#endif
@ -159,22 +165,28 @@ public:
int set(uint16_t key, uint16_t buf_size, const void *buf);
/**
* @brief Programs one item of data on Flash, allocating a key.
* @brief Allocate a free key (to be used later in a set operation).
*
* @param[out] key Returned key of stored item.
* @param[in] buf_size Item size in bytes.
* @param[in] buf Buffer containing data.
* @param[in] owner Owner of allocated key.
*
* @returns NVSTORE_SUCCESS Key was successfully allocated.
* NVSTORE_NO_FREE_KEY Couldn't allocate a key for this call.
*
*/
int allocate_key(uint16_t &key, uint8_t owner = NVSTORE_UNSPECIFIED_OWNER);
/**
* @brief Free all allocated keys that belong to a specific owner.
*
* @param[in] owner Owner.
*
* @returns NVSTORE_SUCCESS Value was successfully written on Flash.
* NVSTORE_WRITE_ERROR Physical error writing data.
* NVSTORE_BAD_VALUE Bad value in any of the parameters.
* NVSTORE_FLASH_AREA_TOO_SMALL
* Not enough space in Flash area.
* NVSTORE_ALREADY_EXISTS Item set with write once API already exists.
* NVSTORE_NO_FREE_KEY Couldn't allocate a key for this call.
*
*/
int set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf);
int free_all_keys_by_owner(uint8_t owner);
/**
* @brief Programs one item of data on Flash, given key, allowing no consequent sets to this key.
@ -344,13 +356,14 @@ private:
* @param[out] valid Is the record valid.
* @param[out] key Record key.
* @param[out] flags Record flags.
* @param[out] owner Owner.
* @param[out] next_offset Offset of next record.
*
* @returns 0 for success, nonzero for failure.
*/
int read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf,
uint16_t &actual_size, int validate_only, int &valid,
uint16_t &key, uint16_t &flags, uint32_t &next_offset);
uint16_t &key, uint16_t &flags, uint8_t &owner, uint32_t &next_offset);
/**
* @brief Write an NVStore record from a given location.
@ -359,13 +372,14 @@ private:
* @param[in] offset Offset of record in area.
* @param[in] key Record key.
* @param[in] flags Record flags.
* @param[in] owner Owner.
* @param[in] data_size Data size (bytes).
* @param[in] data_buf Data buffer.
* @param[out] next_offset Offset of next record.
*
* @returns 0 for success, nonzero for failure.
*/
int write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags,
int write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, uint8_t owner,
uint32_t data_size, const void *data_buf, uint32_t &next_offset);
/**
@ -398,12 +412,13 @@ private:
*
* @param[in] key Record key.
* @param[in] flags Record flags.
* @param[in] owner Owner.
* @param[in] buf_size Data size (bytes).
* @param[in] buf Data buffer.
*
* @returns 0 for success, nonzero for failure.
*/
int garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf);
int garbage_collection(uint16_t key, uint16_t flags, uint8_t owner, uint16_t buf_size, const void *buf);
/**
* @brief Actual logics of get API (covers also get size API).
@ -422,14 +437,14 @@ private:
/**
* @brief Actual logics of set API (covers also set_once and remove APIs).
*
* @param[out] key key (both input and output).
* @param[in] key key.
* @param[in] buf_size Buffer size (bytes).
* @param[in] buf Input Buffer.
* @param[in] flags Record flags.
*
* @returns 0 for success, nonzero for failure.
*/
int do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags);
int do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags);
};
/** @}*/