Skip to content

Commit 2141526

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 7d91ff0 commit 2141526

File tree

3 files changed

+103
-108
lines changed

3 files changed

+103
-108
lines changed

UNITTESTS/moduletests/storage/kvstore/TDBStore/moduletest.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,21 @@ TEST_F(TDBStoreModuleTest, set_deinit_init_get)
9696
EXPECT_EQ(tdb.remove("key"), MBED_SUCCESS);
9797
}
9898
}
99+
100+
TEST_F(TDBStoreModuleTest, reserved_data)
101+
{
102+
char data[20];
103+
char buf[20];
104+
for (int i = 0; i < sizeof(buf); ++i) {
105+
data[i] = rand();
106+
}
107+
EXPECT_EQ(tdb.reserved_data_set(data, sizeof(data)), MBED_SUCCESS);
108+
EXPECT_EQ(tdb.reserved_data_get(buf, sizeof(buf)), MBED_SUCCESS);
109+
EXPECT_EQ(0, memcmp(data, buf, sizeof(buf)));
110+
EXPECT_EQ(tdb.reserved_data_set(data, 1024), MBED_ERROR_INVALID_SIZE);
111+
EXPECT_EQ(tdb.reserved_data_set(data, sizeof(data)), MBED_ERROR_WRITE_FAILED);
112+
EXPECT_EQ(tdb.deinit(), MBED_SUCCESS);
113+
EXPECT_EQ(tdb.init(), MBED_SUCCESS);
114+
EXPECT_EQ(tdb.reserved_data_get(buf, sizeof(buf)), MBED_SUCCESS);
115+
EXPECT_EQ(0, memcmp(data, buf, sizeof(buf)));
116+
}

features/storage/kvstore/tdbstore/TDBStore.cpp

Lines changed: 72 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -831,33 +831,11 @@ int TDBStore::garbage_collection()
831831
int ret;
832832
size_t ind;
833833

834-
ret = check_erase_before_write(1 - _active_area, 0, _master_record_offset + _master_record_size);
834+
ret = reset_area(1 - _active_area);
835835
if (ret) {
836836
return ret;
837837
}
838838

839-
ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
840-
841-
if (!ret) {
842-
// Copy reserved data
843-
to_offset = 0;
844-
reserved_size = _master_record_offset;
845-
846-
while (reserved_size) {
847-
chunk_size = std::min(work_buf_size, reserved_size);
848-
ret = read_area(_active_area, to_offset, chunk_size, _work_buf);
849-
if (ret) {
850-
return ret;
851-
}
852-
ret = write_area(1 - _active_area, to_offset, chunk_size, _work_buf);
853-
if (ret) {
854-
return ret;
855-
}
856-
to_offset += chunk_size;
857-
reserved_size -= chunk_size;
858-
}
859-
}
860-
861839
to_offset = _master_record_offset + _master_record_size;
862840

863841
// Initialize in case table is empty
@@ -1059,7 +1037,7 @@ int TDBStore::init()
10591037
// Master record may be either corrupt or erased - either way erase it
10601038
// (this will do nothing if already erased)
10611039
if (ret == MBED_ERROR_INVALID_DATA_DETECTED) {
1062-
if (check_erase_before_write(area, _master_record_offset, _master_record_size, true)) {
1040+
if (reset_area(area)) {
10631041
MBED_ERROR(MBED_ERROR_READ_FAILED, "TDBSTORE: Unable to reset area at init");
10641042
}
10651043
area_state[area] = TDBSTORE_AREA_STATE_EMPTY;
@@ -1124,11 +1102,9 @@ int TDBStore::init()
11241102
}
11251103
}
11261104

1127-
reserved_ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
1128-
1129-
// If we either have a corrupt record somewhere, or the reserved area is corrupt,
1130-
// perform garbage collection to salvage all preceding records and/or clean reserved area.
1131-
if ((ret == MBED_ERROR_INVALID_DATA_DETECTED) || (reserved_ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
1105+
// If we either have a corrupt record somewhere
1106+
// perform garbage collection to salvage all preceding records.
1107+
if ((ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
11321108
ret = garbage_collection();
11331109
if (ret) {
11341110
MBED_ERROR(ret, "TDBSTORE: Unable to perform GC at init");
@@ -1179,8 +1155,19 @@ int TDBStore::deinit()
11791155

11801156
int TDBStore::reset_area(uint8_t area)
11811157
{
1158+
uint8_t buf[RESERVED_AREA_SIZE + sizeof(reserved_trailer_t)];
1159+
int ret;
1160+
bool copy_reserved_data = do_reserved_data_get(buf, sizeof(buf), 0, buf + RESERVED_AREA_SIZE) == MBED_SUCCESS;
1161+
11821162
// Erase reserved area and master record
1183-
return check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
1163+
ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
1164+
if (ret) {
1165+
return ret;
1166+
}
1167+
if (copy_reserved_data) {
1168+
ret = write_area(area, 0, sizeof(buf), buf);
1169+
}
1170+
return ret;
11841171
}
11851172

11861173
int TDBStore::reset()
@@ -1196,7 +1183,7 @@ int TDBStore::reset()
11961183

11971184
// Reset both areas
11981185
for (area = 0; area < _num_areas; area++) {
1199-
ret = reset_area(area);
1186+
ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size, true);
12001187
if (ret) {
12011188
goto end;
12021189
}
@@ -1343,116 +1330,97 @@ void TDBStore::update_all_iterators(bool added, uint32_t ram_table_ind)
13431330
int TDBStore::reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size)
13441331
{
13451332
reserved_trailer_t trailer;
1346-
int os_ret, ret = MBED_SUCCESS;
1333+
int ret;
13471334

13481335
if (reserved_data_buf_size > RESERVED_AREA_SIZE) {
13491336
return MBED_ERROR_INVALID_SIZE;
13501337
}
13511338

13521339
_mutex.lock();
13531340

1354-
ret = do_reserved_data_get(0, RESERVED_AREA_SIZE);
1355-
if ((ret == MBED_SUCCESS) || (ret == MBED_ERROR_INVALID_DATA_DETECTED)) {
1341+
ret = do_reserved_data_get(0, 0);
1342+
if (ret == MBED_SUCCESS) {
13561343
ret = MBED_ERROR_WRITE_FAILED;
13571344
goto end;
1358-
} else if (ret != MBED_ERROR_ITEM_NOT_FOUND) {
1359-
goto end;
1360-
}
1361-
1362-
ret = write_area(_active_area, 0, reserved_data_buf_size, reserved_data);
1363-
if (ret) {
1364-
goto end;
13651345
}
13661346

13671347
trailer.trailer_size = sizeof(trailer);
13681348
trailer.data_size = reserved_data_buf_size;
13691349
trailer.crc = calc_crc(initial_crc, reserved_data_buf_size, reserved_data);
13701350

1371-
ret = write_area(_active_area, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1372-
if (ret) {
1373-
goto end;
1374-
}
1375-
1376-
os_ret = _buff_bd->sync();
1377-
if (os_ret) {
1378-
ret = MBED_ERROR_WRITE_FAILED;
1379-
goto end;
1351+
/*
1352+
* Write to both areas
1353+
* Both must success, as they are required to be erased when TDBStore initializes
1354+
* its area
1355+
*/
1356+
for (int i = 0; i < _num_areas; ++i) {
1357+
ret = write_area(i, 0, reserved_data_buf_size, reserved_data);
1358+
if (ret) {
1359+
goto end;
1360+
}
1361+
ret = write_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1362+
if (ret) {
1363+
goto end;
1364+
}
1365+
ret = _buff_bd->sync();
1366+
if (ret) {
1367+
goto end;
1368+
}
13801369
}
1381-
1370+
ret = MBED_SUCCESS;
13821371
end:
13831372
_mutex.unlock();
13841373
return ret;
13851374
}
13861375

1387-
int TDBStore::do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size)
1376+
int TDBStore::do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size, void *copy_trailer)
13881377
{
13891378
reserved_trailer_t trailer;
1390-
uint8_t *buf;
1379+
uint8_t buf[RESERVED_AREA_SIZE];
13911380
int ret;
1392-
bool erased = true;
1393-
size_t actual_size;
1394-
uint32_t crc = initial_crc;
1395-
uint32_t offset;
1396-
uint8_t blank = _buff_bd->get_erase_value();
1397-
1398-
ret = read_area(_active_area, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1399-
if (ret) {
1400-
return ret;
1401-
}
1381+
uint32_t crc;
14021382

1403-
buf = reinterpret_cast <uint8_t *>(&trailer);
1404-
for (uint32_t i = 0; i < sizeof(trailer); i++) {
1405-
if (buf[i] != blank) {
1406-
erased = false;
1407-
break;
1383+
/*
1384+
* Try to keep reserved data identical on both areas, therefore
1385+
* we can return any of these data, if the checmsum is correct.
1386+
*/
1387+
for (int i = 0; i < _num_areas; ++i) {
1388+
ret = read_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
1389+
if (ret) {
1390+
return ret;
14081391
}
1409-
}
14101392

1411-
if (!erased) {
1412-
actual_size = trailer.data_size;
1413-
if (actual_data_size) {
1414-
*actual_data_size = actual_size;
1393+
// First validy check: is the trailer header size correct
1394+
if (trailer.trailer_size != sizeof(trailer)) {
1395+
continue;
14151396
}
1416-
if (reserved_data_buf_size < actual_size) {
1417-
return MBED_ERROR_INVALID_SIZE;
1397+
// Second validy check: Is the data too big (corrupt header)
1398+
if (trailer.data_size > RESERVED_AREA_SIZE) {
1399+
continue;
14181400
}
1419-
} else {
1420-
actual_size = std::min((size_t) RESERVED_AREA_SIZE, reserved_data_buf_size);
1421-
}
1422-
1423-
if (reserved_data) {
1424-
buf = reinterpret_cast <uint8_t *>(reserved_data);
1425-
} else {
1426-
buf = _work_buf;
1427-
}
1428-
1429-
offset = 0;
14301401

1431-
while (actual_size) {
1432-
uint32_t chunk = std::min(work_buf_size, (uint32_t) actual_size);
1433-
ret = read_area(_active_area, offset, chunk, buf + offset);
1402+
// Next, verify the checksum
1403+
ret = read_area(i, 0, trailer.data_size, buf);
14341404
if (ret) {
14351405
return ret;
14361406
}
1437-
for (uint32_t i = 0; i < chunk; i++) {
1438-
if (buf[i] != blank) {
1439-
erased = false;
1440-
break;
1407+
crc = calc_crc(initial_crc, trailer.data_size, buf);
1408+
if (crc == trailer.crc) {
1409+
// Correct data, copy it and return to caller
1410+
if (reserved_data) {
1411+
memcpy(reserved_data, buf, trailer.data_size);
14411412
}
1413+
if (actual_data_size) {
1414+
*actual_data_size = trailer.data_size;
1415+
}
1416+
if (copy_trailer) {
1417+
memcpy(copy_trailer, &trailer, sizeof(trailer));
1418+
}
1419+
return MBED_SUCCESS;
14421420
}
1443-
1444-
crc = calc_crc(crc, chunk, buf + offset);
1445-
offset += chunk;
1446-
actual_size -= chunk;
14471421
}
14481422

1449-
if (erased) {
1450-
return MBED_ERROR_ITEM_NOT_FOUND;
1451-
} else if (crc != trailer.crc) {
1452-
return MBED_ERROR_INVALID_DATA_DETECTED;
1453-
}
1454-
1455-
return MBED_SUCCESS;
1423+
return MBED_ERROR_ITEM_NOT_FOUND;
14561424
}
14571425

14581426
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)