mirror of https://github.com/ARMmbed/mbed-os.git
TDBStore: optimize erase algorithms
Currently `TDBStore::offset_in_erase_unit()` and `TDBStore::check_erase_before_write()` loop through erase units one-by-one, until the entire range is covered. This is very inefficient when the erase size is tiny, e.g. one-byte on a non-flash device for which we use program as erase. This commit reworks the algorithms, based on the fact that a block device can erase or program as many units as needed in one go.pull/14483/head
parent
e8070514b1
commit
fb4e5e80b4
|
@ -346,14 +346,15 @@ private:
|
||||||
int reset_area(uint8_t area);
|
int reset_area(uint8_t area);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Erase an erase unit.
|
* @brief Erase an area.
|
||||||
*
|
*
|
||||||
* @param[in] area Area.
|
* @param[in] area Area.
|
||||||
* @param[in] offset Offset in area.
|
* @param[in] offset Offset in area.
|
||||||
|
* @param[in] size Number of bytes to erase.
|
||||||
*
|
*
|
||||||
* @returns 0 for success, nonzero for failure.
|
* @returns 0 for success, nonzero for failure.
|
||||||
*/
|
*/
|
||||||
int erase_erase_unit(uint8_t area, uint32_t offset);
|
int erase_area(uint8_t area, uint32_t offset, uint32_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculate addresses and sizes of areas.
|
* @brief Calculate addresses and sizes of areas.
|
||||||
|
|
|
@ -170,24 +170,28 @@ int TDBStore::write_area(uint8_t area, uint32_t offset, uint32_t size, const voi
|
||||||
return MBED_SUCCESS;
|
return MBED_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TDBStore::erase_erase_unit(uint8_t area, uint32_t offset)
|
int TDBStore::erase_area(uint8_t area, uint32_t offset, uint32_t size)
|
||||||
{
|
{
|
||||||
uint32_t bd_offset = _area_params[area].address + offset;
|
uint32_t bd_offset = _area_params[area].address + offset;
|
||||||
uint32_t eu_size = _buff_bd->get_erase_size(bd_offset);
|
|
||||||
|
|
||||||
if (_buff_bd->get_erase_value() != -1) {
|
if (_buff_bd->get_erase_value() != -1) {
|
||||||
return _buff_bd->erase(bd_offset, eu_size);
|
return _buff_bd->erase(bd_offset, size);
|
||||||
} else {
|
} else {
|
||||||
// We need to simulate erase, as our block device
|
// We need to simulate erase to wipe records, as our block device
|
||||||
// does not do it. We can do this one byte at a time
|
// may not do it. Program in chunks of _work_buf_size if the minimum
|
||||||
// because we use BufferedBlockDevice that has page buffers
|
// program size is too small (e.g. one-byte) to avoid performance
|
||||||
uint8_t val = 0xff;
|
// issues.
|
||||||
int ret;
|
MBED_ASSERT(_work_buf != nullptr);
|
||||||
for (; eu_size; --eu_size) {
|
MBED_ASSERT(_work_buf_size != 0);
|
||||||
ret = _buff_bd->program(&val, bd_offset++, 1);
|
memset(_work_buf, 0xFF, _work_buf_size);
|
||||||
|
while (size) {
|
||||||
|
uint32_t chunk = std::min<uint32_t>(_work_buf_size, size);
|
||||||
|
int ret = _buff_bd->program(_work_buf, bd_offset, chunk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
size -= chunk;
|
||||||
|
bd_offset += chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MBED_SUCCESS;
|
return MBED_SUCCESS;
|
||||||
|
@ -1458,19 +1462,24 @@ void TDBStore::offset_in_erase_unit(uint8_t area, uint32_t offset,
|
||||||
uint32_t &offset_from_start, uint32_t &dist_to_end)
|
uint32_t &offset_from_start, uint32_t &dist_to_end)
|
||||||
{
|
{
|
||||||
uint32_t bd_offset = _area_params[area].address + offset;
|
uint32_t bd_offset = _area_params[area].address + offset;
|
||||||
uint32_t agg_offset = 0;
|
|
||||||
|
|
||||||
while (bd_offset >= agg_offset + _buff_bd->get_erase_size(agg_offset)) {
|
// The parameter of `BlockDevice::get_erase_size(bd_addr_t addr)`
|
||||||
agg_offset += _buff_bd->get_erase_size(agg_offset);
|
// does not need to be aligned.
|
||||||
}
|
uint32_t erase_unit = _buff_bd->get_erase_size(bd_offset);
|
||||||
offset_from_start = bd_offset - agg_offset;
|
|
||||||
dist_to_end = _buff_bd->get_erase_size(agg_offset) - offset_from_start;
|
// Even on a flash device with multiple regions, the start address of
|
||||||
|
// an erase unit is aligned to the current region's unit size.
|
||||||
|
offset_from_start = bd_offset % erase_unit;
|
||||||
|
dist_to_end = erase_unit - offset_from_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TDBStore::check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size, bool force_check)
|
int TDBStore::check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size, bool force_check)
|
||||||
{
|
{
|
||||||
// In order to save init time, we don't check that the entire area is erased.
|
// In order to save init time, we don't check that the entire area is erased.
|
||||||
// Instead, whenever reaching an erase unit start erase it.
|
// Instead, whenever reaching an erase unit start erase it.
|
||||||
|
bool erase = false;
|
||||||
|
uint32_t start_offset;
|
||||||
|
uint32_t end_offset;
|
||||||
while (size) {
|
while (size) {
|
||||||
uint32_t dist, offset_from_start;
|
uint32_t dist, offset_from_start;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1478,13 +1487,22 @@ int TDBStore::check_erase_before_write(uint8_t area, uint32_t offset, uint32_t s
|
||||||
uint32_t chunk = std::min(size, dist);
|
uint32_t chunk = std::min(size, dist);
|
||||||
|
|
||||||
if (offset_from_start == 0 || force_check) {
|
if (offset_from_start == 0 || force_check) {
|
||||||
ret = erase_erase_unit(area, offset - offset_from_start);
|
if (!erase) {
|
||||||
if (ret != MBED_SUCCESS) {
|
erase = true;
|
||||||
return MBED_ERROR_WRITE_FAILED;
|
start_offset = offset - offset_from_start;
|
||||||
}
|
}
|
||||||
|
end_offset = offset + dist;
|
||||||
}
|
}
|
||||||
offset += chunk;
|
offset += chunk;
|
||||||
size -= chunk;
|
size -= chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (erase) {
|
||||||
|
int ret = erase_area(area, start_offset, end_offset - start_offset);
|
||||||
|
if (ret != MBED_SUCCESS) {
|
||||||
|
return MBED_ERROR_WRITE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return MBED_SUCCESS;
|
return MBED_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue