Skip to content

Commit bb155ad

Browse files
committed
Add filesystem recovery tests
Add tests for filesystem resilience and wear leveling. These tests take shared filesystem code and simulate different scenarios while this code is running. Information on the new tests can be found below. mbed-littlefs-tests-filesystem_recovery-resilience: Tests that after every block device operation the filesystem is in a well defined state. mbed-littlefs-tests-filesystem_recovery-wear_leveling: Tests that the littlefs correctly handles when flash is exhausted by using a simulated block device until there are no free good blocks. Note - This patch also adds several new block devices for testing. These will eventually be moved into mbed-os.
1 parent 7eaf61c commit bb155ad

File tree

10 files changed

+1585
-0
lines changed

10 files changed

+1585
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017-2017 ARM Limited
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#include "mbed.h"
24+
#include "unity.h"
25+
#include "utest.h"
26+
#include "test_env.h"
27+
28+
#include "atomic_usage.h"
29+
#include "ObservingBlockDevice.h"
30+
#include "LittleFileSystem.h"
31+
32+
33+
using namespace utest::v1;
34+
35+
#define TEST_CYCLES 10
36+
#define TEST_BD_SIZE (16 * 1024)
37+
38+
/**
39+
* Check that the filesystem is valid after every change
40+
*
41+
* This test is to ensure that littlefs contains a valid filesystem at
42+
* all times. This property is required for handling unexpected power
43+
* loss.
44+
*/
45+
void test_resilience()
46+
{
47+
HeapBlockDevice bd(TEST_BD_SIZE);
48+
49+
// Setup the test
50+
setup_atomic_operations(&bd, true);
51+
52+
// Run check on every write operation
53+
ObservingBlockDevice observer(&bd);
54+
observer.attach(check_atomic_operations);
55+
56+
// Perform operations
57+
printf("Performing %i operations on flash\n", TEST_CYCLES);
58+
for (int i = 1; i <= TEST_CYCLES; i++) {
59+
int64_t ret = perform_atomic_operations(&observer);
60+
TEST_ASSERT_EQUAL(i, ret);
61+
}
62+
printf("No errors detected\n");
63+
}
64+
65+
Case cases[] = {
66+
Case("test resilience", test_resilience),
67+
};
68+
69+
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
70+
{
71+
GREENTEA_SETUP(20, "default_auto");
72+
return greentea_test_setup_handler(number_of_cases);
73+
}
74+
75+
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
76+
77+
int main()
78+
{
79+
Harness::run(specification);
80+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017-2017 ARM Limited
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#include "mbed.h"
24+
#include "unity.h"
25+
#include "utest.h"
26+
#include "test_env.h"
27+
28+
#include "atomic_usage.h"
29+
#include "ExhaustibleBlockDevice.h"
30+
#include "LittleFileSystem.h"
31+
32+
using namespace utest::v1;
33+
34+
#define ERASE_CYCLES 20
35+
#define TEST_BD_SIZE (8 * 1024)
36+
37+
static uint32_t test_wear_leveling_size(uint32_t bd_size)
38+
{
39+
HeapBlockDevice hbd(bd_size, 1, 1, 512);
40+
ExhaustibleBlockDevice ebd(&hbd, ERASE_CYCLES);
41+
42+
printf("Testing size %lu\n", bd_size);
43+
setup_atomic_operations(&ebd, true);
44+
45+
int64_t cycles = 0;
46+
while (true) {
47+
int64_t ret = perform_atomic_operations(&ebd);
48+
check_atomic_operations(&ebd);
49+
if (-1 == ret) {
50+
break;
51+
}
52+
cycles++;
53+
TEST_ASSERT_EQUAL(cycles, ret);
54+
55+
}
56+
57+
printf(" Simulated flash lasted %lli cylces\n", cycles);
58+
return cycles;
59+
}
60+
61+
/**
62+
* Check that storage life is proportional to storage size
63+
*
64+
* This test is to ensure that littlefs is properly handling wear
65+
* leveling. It does this by creating a simulated flash block device
66+
* which can be worn out and then using it until it is exhausted.
67+
* It then doubles the size of the block device and runs the same
68+
* test. If the block device with twice the size lasts at least
69+
* twice as long then the test passes.
70+
*/
71+
void test_wear_leveling()
72+
{
73+
uint32_t cycles_1 = test_wear_leveling_size(TEST_BD_SIZE * 1);
74+
uint32_t cycles_2 = test_wear_leveling_size(TEST_BD_SIZE * 2);
75+
TEST_ASSERT(cycles_2 > cycles_1 * 2);
76+
}
77+
78+
Case cases[] = {
79+
Case("test wear leveling", test_wear_leveling),
80+
};
81+
82+
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
83+
{
84+
GREENTEA_SETUP(60, "default_auto");
85+
return greentea_test_setup_handler(number_of_cases);
86+
}
87+
88+
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
89+
90+
int main()
91+
{
92+
Harness::run(specification);
93+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ExhaustibleBlockDevice.h"
18+
19+
20+
ExhaustibleBlockDevice::ExhaustibleBlockDevice(BlockDevice *bd, uint32_t erase_cycles)
21+
: _bd(bd), _erase_array(NULL), _erase_cycles(erase_cycles)
22+
{
23+
}
24+
25+
ExhaustibleBlockDevice::~ExhaustibleBlockDevice()
26+
{
27+
delete[] _erase_array;
28+
}
29+
30+
int ExhaustibleBlockDevice::init()
31+
{
32+
int err = _bd->init();
33+
if (err) {
34+
return err;
35+
}
36+
37+
if (!_erase_array) {
38+
// can only be allocated after initialization
39+
_erase_array = new uint32_t[_bd->size() / _bd->get_erase_size()];
40+
for (size_t i = 0; i < _bd->size() / _bd->get_erase_size(); i++) {
41+
_erase_array[i] = _erase_cycles;
42+
}
43+
}
44+
45+
return 0;
46+
}
47+
48+
int ExhaustibleBlockDevice::deinit()
49+
{
50+
// _erase_array is lazily cleaned up in destructor to allow
51+
// data to live across de/reinitialization
52+
return _bd->deinit();
53+
}
54+
55+
int ExhaustibleBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
56+
{
57+
return _bd->read(buffer, addr, size);
58+
}
59+
60+
int ExhaustibleBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
61+
{
62+
MBED_ASSERT(is_valid_program(addr, size));
63+
64+
if (_erase_array[addr / get_erase_size()] == 0) {
65+
// TODO possibly something more destructive here
66+
return 0;
67+
}
68+
69+
return _bd->program(buffer, addr, size);
70+
}
71+
72+
int ExhaustibleBlockDevice::erase(bd_addr_t addr, bd_size_t size)
73+
{
74+
MBED_ASSERT(is_valid_erase(addr, size));
75+
76+
// use an erase cycle
77+
if (_erase_array[addr / get_erase_size()] > 0) {
78+
_erase_array[addr / get_erase_size()] -= 1;
79+
}
80+
81+
if (_erase_array[addr / get_erase_size()] == 0) {
82+
// TODO possibly something more destructive here
83+
return 0;
84+
}
85+
86+
return _bd->erase(addr, size);
87+
}
88+
89+
bd_size_t ExhaustibleBlockDevice::get_read_size() const
90+
{
91+
return _bd->get_read_size();
92+
}
93+
94+
bd_size_t ExhaustibleBlockDevice::get_program_size() const
95+
{
96+
return _bd->get_program_size();
97+
}
98+
99+
bd_size_t ExhaustibleBlockDevice::get_erase_size() const
100+
{
101+
return _bd->get_erase_size();
102+
}
103+
104+
bd_size_t ExhaustibleBlockDevice::size() const
105+
{
106+
return _bd->size();
107+
}

0 commit comments

Comments
 (0)