Skip to content

Commit 3652328

Browse files
author
Seppo Takalo
committed
TDBStore: Keep copy of reserved data on both areas.
Change the "reserved data" logic so that every time we erase and area, the content of reserved data is then immediately copied to newly erased area. This keeps two copies of the data. When data is requested, return only if checksum is matching. When data is written, only allow if BOTH checksums are incorrect, meaning that areas are either corrupted or erased. Only exception is TDBStore::reset() which erases all keys and reserved data. Removed all logic that tried to detect, if reserved are was erased or corrupted. Rely entirely on checksum. Add moduletest for reserved data.
1 parent 2ddd406 commit 3652328

File tree

2 files changed

+85
-108
lines changed

2 files changed

+85
-108
lines changed

features/storage/kvstore/tdbstore/TDBStore.cpp

Lines changed: 72 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -848,33 +848,11 @@ int TDBStore::garbage_collection()
848848
int ret;
849849
size_t ind;
850850

851-
ret = check_erase_before_write(1 - _active_area, 0, _master_record_offset + _master_record_size);
851+
ret = reset_area(1 - _active_area);
852852
if (ret) {
853853
return ret;
854854
}
855855

856-
ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
857-
858-
if (!ret) {
859-
// Copy reserved data
860-
to_offset = 0;
861-
reserved_size = _master_record_offset;
862-
863-
while (reserved_size) {
864-
chunk_size = std::min(work_buf_size, reserved_size);
865-
ret = read_area(_active_area, to_offset, chunk_size, _work_buf);
866-
if (ret) {
867-
return ret;
868-
}
869-
ret = write_area(1 - _active_area, to_offset, chunk_size, _work_buf);
870-
if (ret) {
871-
return ret;
872-
}
873-
to_offset += chunk_size;
874-
reserved_size -= chunk_size;
875-
}
876-
}
877-
878856
to_offset = _master_record_offset + _master_record_size;
879857

880858
// Initialize in case table is empty
@@ -1078,7 +1056,7 @@ int TDBStore::init()
10781056
// Master record may be either corrupt or erased - either way erase it
10791057
// (this will do nothing if already erased)
10801058
if (ret == MBED_ERROR_INVALID_DATA_DETECTED) {
1081-
if (check_erase_before_write(area, _master_record_offset, _master_record_size, true)) {
1059+
if (reset_area(area)) {
10821060
MBED_ERROR(MBED_ERROR_READ_FAILED, "TDBSTORE: Unable to reset area at init");
10831061
}
10841062
area_state[area] = TDBSTORE_AREA_STATE_EMPTY;
@@ -1143,11 +1121,9 @@ int TDBStore::init()
11431121
}
11441122
}
11451123

1146-
reserved_ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
1147-
1148-
// If we either have a corrupt record somewhere, or the reserved area is corrupt,
1149-
// perform garbage collection to salvage all preceding records and/or clean reserved area.
1150-
if ((ret == MBED_ERROR_INVALID_DATA_DETECTED) || (reserved_ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
1124+
// If we either have a corrupt record somewhere
1125+
// perform garbage collection to salvage all preceding records.
1126+
if ((ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
11511127
ret = garbage_collection();
11521128
if (ret) {
11531129
MBED_ERROR(ret, "TDBSTORE: Unable to perform GC at init");
@@ -1198,8 +1174,19 @@ int TDBStore::deinit()
11981174

11991175
int TDBStore::reset_area(uint8_t area)
12001176
{
1177+
uint8_t buf[RESERVED_AREA_SIZE + sizeof(reserved_trailer_t)];
1178+
int ret;
1179+
bool copy_reserved_data = do_reserved_data_get(buf, sizeof(buf), 0, buf + RESERVED_AREA_SIZE) == MBED_SUCCESS;
1180+
12011181
// Erase reserved area and master record
1202-
return check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
1182+
ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
1183+
if (ret) {
1184+
return ret;
1185+
}
1186+
if (copy_reserved_data) {
1187+
ret = write_area(area, 0, sizeof(buf), buf);
1188+
}
1189+
return ret;
12031190
}
12041191

12051192
int TDBStore::reset()
@@ -1215,7 +1202,7 @@ int TDBStore::reset()
12151202

12161203
// Reset both areas
12171204
for (area = 0; area < _num_areas; area++) {
1218-
ret = reset_area(area);
1205+
ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
12191206
if (ret) {
12201207
goto end;
12211208
}
@@ -1362,116 +1349,97 @@ void TDBStore::update_all_iterators(bool added, uint32_t ram_table_ind)
13621349
int TDBStore::reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size)
13631350
{
13641351
reserved_trailer_t trailer;
1365-
int os_ret, ret = MBED_SUCCESS;
1352+
int ret;
13661353

13671354
if (reserved_data_buf_size > RESERVED_AREA_SIZE) {
13681355
return MBED_ERROR_INVALID_SIZE;
13691356
}
13701357

13711358
_mutex.lock();
13721359

1373-
ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
1374-
if ((ret == MBED_SUCCESS) || (ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
1360+
ret = do_reserved_data_get(0, 0);
1361+
if (ret == MBED_SUCCESS) {
13751362
ret = MBED_ERROR_WRITE_FAILED;
13761363
goto end;
1377-
} else if (ret != MBED_ERROR_ITEM_NOT_FOUND) {
1378-
goto end;
1379-
}
1380-
1381-
ret = write_area(_active_area, 0, reserved_data_buf_size, reserved_data);
1382-
if (ret) {
1383-
goto end;
13841364
}
13851365

13861366
trailer.trailer_size = sizeof(trailer);
13871367
trailer.data_size = reserved_data_buf_size;
13881368
trailer.crc = calc_crc(initial_crc, reserved_data_buf_size, reserved_data);
13891369

1390-
ret = write_area(_active_area, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1391-
if (ret) {
1392-
goto end;
1393-
}
1394-
1395-
os_ret = _buff_bd->sync();
1396-
if (os_ret) {
1397-
ret = MBED_ERROR_WRITE_FAILED;
1398-
goto end;
1370+
/*
1371+
* Write to both areas
1372+
* Both must success, as they are required to be erased when TDBStore initializes
1373+
* its area
1374+
*/
1375+
for (int i = 0; i < _num_areas; ++i) {
1376+
ret = write_area(i, 0, reserved_data_buf_size, reserved_data);
1377+
if (ret) {
1378+
goto end;
1379+
}
1380+
ret = write_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1381+
if (ret) {
1382+
goto end;
1383+
}
1384+
ret = _buff_bd->sync();
1385+
if (ret) {
1386+
goto end;
1387+
}
13991388
}
1400-
1389+
ret = MBED_SUCCESS;
14011390
end:
14021391
_mutex.unlock();
14031392
return ret;
14041393
}
14051394

1406-
int TDBStore::do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size)
1395+
int TDBStore::do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size, void *copy_trailer)
14071396
{
14081397
reserved_trailer_t trailer;
1409-
uint8_t *buf;
1398+
uint8_t buf[RESERVED_AREA_SIZE];
14101399
int ret;
1411-
bool erased = true;
1412-
size_t actual_size;
1413-
uint32_t crc = initial_crc;
1414-
uint32_t offset;
1415-
uint8_t blank = _buff_bd->get_erase_value();
1416-
1417-
ret = read_area(_active_area, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1418-
if (ret) {
1419-
return ret;
1420-
}
1400+
uint32_t crc;
14211401

1422-
buf = reinterpret_cast <uint8_t *>(&trailer);
1423-
for (uint32_t i = 0; i < sizeof(trailer); i++) {
1424-
if (buf[i] != blank) {
1425-
erased = false;
1426-
break;
1402+
/*
1403+
* Try to keep reserved data identical on both areas, therefore
1404+
* we can return any of these data, if the checmsum is correct.
1405+
*/
1406+
for (int i = 0; i < _num_areas; ++i) {
1407+
ret = read_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1408+
if (ret) {
1409+
return ret;
14271410
}
1428-
}
14291411

1430-
if (!erased) {
1431-
actual_size = trailer.data_size;
1432-
if (actual_data_size) {
1433-
*actual_data_size = actual_size;
1412+
// First validy check: is the trailer header size correct
1413+
if (trailer.trailer_size != sizeof(trailer)) {
1414+
continue;
14341415
}
1435-
if (reserved_data_buf_size < actual_size) {
1436-
return MBED_ERROR_INVALID_SIZE;
1416+
// Second validy check: Is the data too big (corrupt header)
1417+
if (trailer.data_size > RESERVED_AREA_SIZE) {
1418+
continue;
14371419
}
1438-
} else {
1439-
actual_size = std::min((size_t) RESERVED_AREA_SIZE, reserved_data_buf_size);
1440-
}
1441-
1442-
if (reserved_data) {
1443-
buf = reinterpret_cast <uint8_t *>(reserved_data);
1444-
} else {
1445-
buf = _work_buf;
1446-
}
1447-
1448-
offset = 0;
14491420

1450-
while (actual_size) {
1451-
uint32_t chunk = std::min(work_buf_size, (uint32_t) actual_size);
1452-
ret = read_area(_active_area, offset, chunk, buf + offset);
1421+
// Next, verify the checksum
1422+
ret = read_area(i, 0, trailer.data_size, buf);
14531423
if (ret) {
14541424
return ret;
14551425
}
1456-
for (uint32_t i = 0; i < chunk; i++) {
1457-
if (buf[i] != blank) {
1458-
erased = false;
1459-
break;
1426+
crc = calc_crc(initial_crc, trailer.data_size, buf);
1427+
if (crc == trailer.crc) {
1428+
// Correct data, copy it and return to caller
1429+
if (reserved_data) {
1430+
memcpy(reserved_data, buf, trailer.data_size);
14601431
}
1432+
if (actual_data_size) {
1433+
*actual_data_size = trailer.data_size;
1434+
}
1435+
if (copy_trailer) {
1436+
memcpy(copy_trailer, &trailer, sizeof(trailer));
1437+
}
1438+
return MBED_SUCCESS;
14611439
}
1462-
1463-
crc = calc_crc(crc, chunk, buf + offset);
1464-
offset += chunk;
1465-
actual_size -= chunk;
14661440
}
14671441

1468-
if (erased) {
1469-
return MBED_ERROR_ITEM_NOT_FOUND;
1470-
} else if (crc != trailer.crc) {
1471-
return MBED_ERROR_INVALID_DATA_DETECTED;
1472-
}
1473-
1474-
return MBED_SUCCESS;
1442+
return MBED_ERROR_ITEM_NOT_FOUND;
14751443
}
14761444

14771445
int TDBStore::reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size)

features/storage/kvstore/tdbstore/TDBStore.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "features/storage/blockdevice/BlockDevice.h"
2424
#include "features/storage/blockdevice/BufferedBlockDevice.h"
2525
#include "PlatformMutex.h"
26+
#include "mbed_error.h"
2627

2728
namespace mbed {
2829

@@ -74,7 +75,7 @@ class TDBStore : public KVStore {
7475

7576

7677
/**
77-
* @brief Reset TDBStore contents (clear all keys)
78+
* @brief Reset TDBStore contents (clear all keys) and reserved data
7879
*
7980
* @returns MBED_SUCCESS Success.
8081
* MBED_ERROR_NOT_READY Not initialized.
@@ -338,6 +339,8 @@ class TDBStore : public KVStore {
338339

339340
/**
340341
* @brief Reset an area (erase its start).
342+
* This erases master record, but preserves the
343+
* reserved area data.
341344
*
342345
* @param[in] area Area.
343346
*
@@ -524,16 +527,22 @@ class TDBStore : public KVStore {
524527

525528
/**
526529
* @brief Get data from reserved area - worker function.
530+
* This verifies that reserved data on both areas have
531+
* correct checksums. If given pointer is not NULL, also
532+
* write the reserved data to buffer. If checksums are not
533+
* valid, return error code, and don't write anything to any
534+
* pointers.
527535
*
528-
* @param[in] reserved_data Reserved data buffer (0 to return nothing).
536+
* @param[out] reserved_data Reserved data buffer (NULL to return nothing).
529537
* @param[in] reserved_data_buf_size
530538
* Reserved data buffer size.
531-
* @param[in] actual_data_size Return data size.
539+
* @param[out] actual_data_size If not NULL, return actual data size.
540+
* @param[out] copy_trailer If not NULL, copy the trailer content to given buffer.
532541
*
533542
* @returns 0 on success or a negative error code on failure
534543
*/
535544
int do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size,
536-
size_t *actual_data_size = 0);
545+
size_t *actual_data_size = 0, void *copy_trailer = 0);
537546

538547
/**
539548
* @brief Update all iterators after adding or deleting of keys.

0 commit comments

Comments
 (0)