-
Notifications
You must be signed in to change notification settings - Fork 8.2k
subsys: fs/nvs: Improvements #8141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hi @aurel32, I have updated NVS to provide better support on systems with write block size > 1. If you have some time could you please test/review. |
doc/subsystems/nvs/nvs.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the double back ticks here. The double colon at the end of the previous line says the following indented text is a code-block, so as written the back ticks will show up in the generated doc output.
samples/subsys/nvs/README.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clean up some capitalization issues, and combine the sentences a bit for clarity:
This is a simple application demonstrating use of the NVS
module for non-volatile (flash) storage. In this application,
a counter is incremented on every reboot and stored in flash,
the application reboots, and the reboot counter data is retrieved.
samples/subsys/nvs/README.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing the build and run commands (and sample output).
doc/subsystems/nvs/nvs.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the double back ticks here. The double colon at the end of the previous line says the following indented text is a code-block, so as written the back ticks will show up in the generated doc output.
doc/subsystems/nvs/nvs.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, the two "length" metadata fields are confusing?
doc/subsystems/nvs/nvs.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
best to add a space around arithmetic operators: 254 * 20,000` minutes
|
Hi @Laczen, thanks for this new version. I'll try to have a look at it and try it tonight, if not tomorrow night. So far I have only seen it is based on an old version of the master branch which is in conflict with the current one. Any chance you can rebase it? Anyway that should not prevent me to test it. |
|
thanks for the patch - will review it soon. |
|
Hi @dbkinder, I have updated the documentation as requested. |
Codecov Report
@@ Coverage Diff @@
## master #8141 +/- ##
==========================================
+ Coverage 52.47% 52.48% +<.01%
==========================================
Files 198 198
Lines 24977 24975 -2
Branches 5210 5210
==========================================
Hits 13107 13107
+ Misses 9761 9759 -2
Partials 2109 2109
Continue to review full report at Codecov.
|
doc/subsystems/nvs/nvs.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please fix those, looks like a conflict that was not resolved.
|
@nashif, corrected |
|
Hi @Laczen. Thanks for the rebase, it makes the testing a lot more easy, as my application depends on changes that have been done recently. I have tested your patch on my board, and it works as expected. I haven't been able to reproduce the issue with the garbage collector on power failure. I'll review the code itself tomorrow. As said in our previous discussion, mixing the metadata and the data doesn't allow to implement a secure delete function (at least when the write block size is > 1), that said i believe we can do the same by triggering the garbage collector on the sector after writing the new/empty value. |
nvlsianpu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will put much more commend on Monday
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f. name shoud describes what the function does, maybe: _nvs_flash_len_align or _nvs_flash_get/calc/_aligned_len
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change this to: _get_wbs_aligned_len()
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previous name was better, maybe _nvs_flash_get/calc/_entry_len
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change this to get_wbs_aligned_entry_len()
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: _nvs_decode_lenght
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: _nvs_encode_lenght
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thing a too big size error should be generated in case the length exceeds 0x3fff. For example this function can return 0xFFFF in this case, and this value can be recognized as the error signal once it was caled by the _nvs_flash_wrt_entry function.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about changing encoding - It would increase the range by one byte:
short length <0, 0x7f>:
---------------
| B0 |
|-------------|
|b7 | b0 - b6 |
|-------------|
| 0 | length |
---------------
long length <0, 0x7fff):
------------------------------------
| B0 | B1 |
|------------------|---------------|
|b7 | b0 - b6 | b0 - b7 |
|------------------|---------------|
| 1 | length >> 8 | length & 0xFF |
------------------------------------
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about changing encoding - It would increase the range by one bit:
short length <0, 0x7f>:
---------------
| B0 |
|-------------|
|b7 | b0 - b6 |
|-------------|
| 0 | length |
---------------
long length <0, 0x7fff):
------------------------------------
| B0 | B1 |
|------------------|---------------|
|b7 | b0 - b6 | b0 - b7 |
|------------------|---------------|
| 1 | length >> 8 | length & 0xFF |
------------------------------------
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should still be a possibility to detect 0xff and 0xff. The range of 16383 bytes should be more then sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After rethinking, it is easily changed and easier to understand than the present. I will change it to the proposal, except length is max 0x7ffe.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: _nvs_decode_lenght
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better: _nvs_encode_lenght
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
aurel32
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have finally reviewed your changes, sorry for the delay. Overall the new flash format and the architecture of the code looks fine to me. I think it still needs some improvements in a few places, but nothing really big.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should define block_size as size_t, as it's the type returned by sizeof and also the type of len, so that the comparison is done on the same type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct block_size should be size_t, will change
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code can be simplified by using a while (len > 0), and then computing bytes to compare as min(block_size, len). Actually it's the strategy chosen a few lines before in _nvs_flash_block_move.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, will change (and also change block_size to size_t)
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that ver means "verify", but "version" first came to my mind. I think it would make it more understandable to call it _nvs_flash_write_cmp, and would be consistent with _nvs_flash_block_cmp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, _cmp is more consistent, will change
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the cases in this function looks like error prone. For example if write_block_size is 2 bytes and the data length to be written below 128 bytes, byte_cnt will be 3. This mean the first call to _nvs_flash_write_ver will try to write 3 bytes, which is the wrong number for a 2-byte write block size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an error, I will review this function once again to make it less error prone.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks simpler to test for len_size != 0xffff and use the value of len_size directly instead of testing both cases.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There you probably want to use min(len, len_flash), to make sure the data from the next entry, or even from outside the NVS area is read. This could be a security issue when combined with missing input validation from the caller. I actually wonder if _nvs_flash_read should also limit the access to the NVS area only.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all those private functions that are not part of the API should just be marked as static in nvs.c, so that the compiler can do more aggressive optimizations. I just report that here, but that applies to many functions in this file.
include/nvs/nvs.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know zephyr enough to know about the API policy, but removing this function is breaking the API. I know there is also a file system format change, so that might be fine after all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will reintroduce nvs_clear, if the user would like to clear the flash area for some other purpose, nvs_clear should be there.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thing a too big size error should be generated in case the length exceeds 0x3fff. For example this function can return 0xFFFF in this case, and this value can be recognized as the error signal once it was caled by the _nvs_flash_wrt_entry function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I have introduced a define NVS_MAX_LEN (0x7ffd), 0xffff is returned if len> NVS_MAX_LEN.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add spaces around - operator.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spaces around '+'
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the second length is swapped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nvlsianpu, thanks for all comments, the second length needs to be swapped because you will always read 2 bytes, but this can be data as well, so to make sure you get the correct length you are only sure about the last byte (this is definitely part of the length), so the last byte should be byte0 of the length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank for elaboration.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not _nvs_addr_advance(fs, &addr, len_size + sizeof(u16_t)); instead of this 'if'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, changed
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be worth to serve the case when this is about the last entry which was not entirely written to the flash (caused for instance by power down).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nvlsianpu, I am changing this routine a bit, fs->write_location is now updated everytime the length comparison is ok. So in the case of an entry that was not completely written the initialization routine will give a fs that is read-only but does not include the latest (invalid) entry. It is then possible to retrieve all information that is valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you are changing sound fine to me. Otherwise what I was asking might be added in a future PR as well. I think this is very likely to have the last entry corrupted for some devices. I would be great If nvs would can fix this in place by performing customized sector rotation.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood that this fill procedure is introduced due the backward serach feature you had added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi andrzej, also the first version of NVS has filling at the end of a sector. The length was even set to jump over the next sector header. However only the length was written to flash. Now ff's are written. The procedure has changed because of the variable length format, making the length parameter variable in size makes it impossible to reach certain sizes (e.g. on a system with byte write size a length of 127 bytes generates a fill block of 133 bytes; a length of 128 bytes generates a length of 136 bytes, so it is impossible to generate a block of size 134 and 135). This is of course more complicated for different write sizes, so I decided to avoid this difficulty by cutting it down to smaller blocks.
samples/subsys/nvs/src/main.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that "larger dataset" means those which are using the long format of length fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
|
@Laczen I put all comments I had. |
|
Hi @aurel32 and @nvlsianpu, I have integrated your comments in my latest rework. |
|
Good job guys! I'd suggest to make write verification optional, since some drivers already implement that. |
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we still need an automatic mechanism for restore FS automatically after the power down while writing issue - it is something we really need for any device in the field. this comment is continuation of the #8141 (comment) thread
cc @lemrey You opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is definitely desirable, the scenario is not uncommon.
I will comment on #8367.
|
Hi @lemrey, thanks for the comment. I think it's a good idea to make the verification optional. I will add this. I don't quite get what you mean by making the flash program unit available at compile time. Could you explain this a bit more ? |
|
Sure @Laczen , |
Even if you know it, it doesn't mean it is fixed. You can have a flash internal to the SoC with a write block size of 4 bytes, and an SPI flash with a write block size of 1. |
|
You are right @aurel32 :/ |
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason why we a reading one byte at a time?
I would suggest to read a multiple, like a word, to reduce the number of calls.
subsys/fs/nvs/nvs_priv.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically the maximum length that will be passed to the driver API in a single call, right?
I can definitely see a use for this, and I would be great to let the user see it menuconfig.
|
@Laczen Better to keep this within this PR as we have discussion story here. |
|
@nvlsianpu, @lemrey, @aurel32, to improve robustness against incomplete writes I have rewritten NVS with a completely different layout on flash. It now stores the data at the beginning of a sector and the metadata (id, length and a offset to the data) at the end of a sector. This way NVS should always be readable in case of a incomplete write (either data or metadata). |
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spaces around + operator
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* flash ate read */
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ensured that read operations are aligned to block-size? It is not expected form a flash driver that it support byte alignment while reading.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we avoid magic values - It looks great now for code review as it helps understanding code while I'm analyzing it, but in few months it will become an dark secret for me and other developers.
This applies to address's mask as well.
Of course using 0xff and 0x00 for memory fills are OK everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nvlsianpu, I am not sure what you mean by this comment. The address definition that is used in nvs is: high 2 bytes are the sector number, low 2 bytes are the offset in the sector. The calculation is mapping the address to an offset in flash. All address masks used are either to find the sector (addr >> 16) or the offset (addr & 0xffff).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That I understood well. What I mens to use predefined mask for extracting these numbers/addresses, something like that:
#define _SECTOR_NUM_MASK 0xFFFF0000
#define _SECTOR_NUM_SHIFT 16
#define _OFF_MASK 0x0000FFFF
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exveption -> exception
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rc = _nvs_flash_cmp_const(...
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this renew assignment from line538
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addr has been changed by _nvs_prev_ate()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't nvs iterates more for seek ate_wra? this assume that ate_wra referencing the second ate in a sector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no ate_wra is found by _nvs_prev_ate(), more iterations are not needed. ate_wra is not referencing the second ate in a sector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed re-implementation complacently.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spaces around *
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think leaving spaces around * makes it confusing, I am using spaces for +/- but not for *.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert free space value before exit as write didn't happen - a smaller entry might fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct, wil change this
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In API documentation pleas rephrase Any other value, indicates an error. -> Any negative value, indicates an error.
This is applicable to the nvs_read() as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixed.
subsys/fs/nvs/nvs.c
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand ho we come to this case as fs->ate_wra refer to place before newest ate metadata.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This happens when we have completely gone through nvs in reverse direction.
|
Hi @nvlsianpu, I think all your remarks have been addressed in the latest commit. |
|
@Laczen I'm happy witch changes. Can you fix shippable, merge conflict and clean git history (squash some commit, especial these which align code to review request. Appart form that the code is in expected shape to be approved. |
The nvs module has some disadvantages for larger block size. The data header and slot are taking up to much space. A rewrite is proposed that reduces the used storage space for systems with write block size > 4. The data storage in flash is now one unit consisting of: data_length, data_id, data and data_length again in a multiple of the write block size. The data_length at the end is used to validate the correctness of the flash write and also allows to travel backwards in the filesystem. As a comparison, on a system with block size 8 byte, a 32 bit values now fits 1 block including the metadata (length and id). This used to be 3 blocks. The data_length will occupy 1 byte if the data length is less than 128 byte, it will occupy 2 byte if the data length is 128 byte or more. The data length is limited to 16383 byte. Each write to flash is verified by a read back of the data. The read performance is improved because reading is done backwards so the latest items are found first. When the filesystem is locked it can be unlocked by calling reinit(), this will clear flash and setup everything for storage. add sample documentation - README.rst Update dtsi to include erase_block_size, use erase_block_size in sample Update prj.conf to include CONFIG_MPU_ALLOW_FLASH_WRITE Signed-off-by: Laczen JMS <[email protected]>
On flash NVS was stored one entry after another including the metadata of each entry. This has the disadvantage that when an incomplete write is performed (e.g. due to power failure) the complete sector had to be rewritten to get a completely functional system. The present rewrite changed the storage in flash of the data. For each sector the data is now written as follows: the data itself at the beginning of the sector (one after the other), the metadata (id, length, data offset in the sector, and a crc of the metadata) is written from the end of the sector. The metadata is of fixed size (8 byte) and for a sector that is completely occupied a metadata entry of all zeros is used. Writing data to flash always is done by: 1. Writing the data, 2. Writing the metadata. If an incomplete write is done NVS will ignore this incomplete write. At the same time the following improvements were done: 1. NVS now support 65536 sectors of each 65536 byte. 2. The sector size no longer requires to be a power of 2 (but it still needs to be a multiple of the flash erase page size). 3. NVS now also keeps track of the free space available. Signed-off-by: Laczen JMS <[email protected]>
|
@nvlsianpu, fixed shippable and merge conflict (was result of removing no longer valid configuration option NVS_PROTECT_FLASH, I will remove this option in a later PR). |
|
@nashif Ready to be merged IMO. |
|
Hi @Laczen. Sorry to not come back earlier about this, I have been busy with real life. I gave a look to your new implementation, and I like the new design of not mixing the data and the metadata. Thanks also for keeping a CRC. Overall it looks good to me. I have however found a few issues and small things that can be improved. The main one is that it doesn't work when block size != 1. I will send a pull request to fix it. I will also prepare a pull request for the small improvements. I have still a few things that I don't fully understand, I hope you don't mind I ask them there:
When len is 0, the ate is still written, so the free space is reduced. I am also not sure it's a good idea to call
|
|
Hi @aurel32, thanks for the fix when block size !=1, I don't understand how
I missed this. I have also been busy doing other things.
Regarding the calculation of block_size, this is only to protect for silly
definitions of NVS_BLOCK_SIZE, e.g. setting it to 12 on a system with
write_block_size 8. You are correct that there is a problem when
NVS_BLOCK_SIZE is smaller than write_block_size, but I don't know how to
fix that. Since NVS can be used on external flash, internal flash, ...
there is no way of figuring out in advance what a good setting of
NVS_BLOCK_SIZE is. I set it to 8 because I expect that for the moment that
are no systems with write_block_size bigger than 8.
Regrading the free space calculation: the free space calculation should
return how much data can be written to the system (possibly after garbage
collection), it is not an indication of how much flash is used at the
moment. Deleted items will not be copied when doing gc, so their data and
also ate should not be counted. On your system I would expect it to report
0x7d8, as NVS will always need two extra ate's for pointing to the last
entry and a sector closing ate. I need to go through the code again to see
why there is space reserved for two extra ate's (possibly to always allow a
delete).
Regarding the call of _nvs_flash_data_wrt(), the catch of len=0 would be
good to handle in the aligned write, than the flash protection unset and
set can be avoided.
Regarding nvs_clear I need to check the code again.
Op do 23 aug. 2018 om 23:37 schreef aurel32 <[email protected]>:
… Hi @Laczen <https://github.com/Laczen>. Sorry to not come back earlier
about this, I have been busy with real life.
I gave a look to your new implementation, and I like the new design of not
mixing the data and the metadata. Thanks also for keeping a CRC. Overall it
looks good to me. I have however found a few issues and small things that
can be improved. The main one is that it doesn't work when block size != 1.
I will send a pull request to fix it. I will also prepare a pull request
for the small improvements.
I have still a few things that I don't fully understand, I hope you don't
mind I ask them there:
-
There are 3 places with this code:
block_size = NVS_BLOCK_SIZE & ~(fs->write_block_size - 1);
I am not fully sure what it is supposed to do. Moreover it will return
0 if write_block_size is bigger than NVS_BLOCK_SIZE and will end-up in an
endless loop.
-
I am not fully sure about the free space accounting. In my case with
2x2048-byte sectors, the free space is always 7c8 aka 1992 bytes at start,
independently of the data already present. In addition I think there is an
issue in the following part of _nvs_flash_wrt:
rc = _nvs_flash_data_wrt(fs, data, len);
if (rc) {
return rc;
}
rc = _nvs_flash_ate_wrt(fs, &entry);
if (rc) {
return rc;
}
if (len != 0) {
fs->free_space -= _nvs_al_size(fs, len);
fs->free_space -= ate_size;
}
When len is 0, the ate is still written, so the free space is reduced. I
am also not sure it's a good idea to call _nvs_flash_data_wrt. Even it if
doesn't write anything, it still unsets the flash protection and sets it
back.
- nvs_clear calls _nvs_flash_erase_sector on all sectors which errors
out if the sector is already erased. It means that all sectors might not
been erased.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#8141 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAjVvNmiB0x5tY17ZInmPDvH_p-McaMSks5uTyCbgaJpZM4UYs0O>
.
|
No problem, this kind of things happen.
Ok, now that I understand the use case, I'll try to think about a solution. As you said very few systems should have a write block size bigger than 8 bytes.
Thanks for the explanation, I'll look at that part again with that in mind.
Ok, I'll try to do that and include that in PR #9601. |
|
Hi @aurel32, I have had some time to look into the problem of the free space calculation as well as nvs_clear. I have not been able to reproduce your numbers for the free space calculation. When nvs is initialized (with a sector of 2048 byte) it reports a free space of 2040 bytes, and the alloc wra is 0x7f8. For nvs_clear() I also do not see it error out on already erased sectors, the compare with 0xff returns a zero and _nvs_flash_erase_sector() returns a 0. It just does not do a erase (because it is not needed). |
|
Hi @Laczen, thanks for checking my points on |
NVS needed improvements to provide better support for systems with write block size > 1 byte. Also the mechanism to protect NVS against incomplete writes was not good enough. This PR provides a rewrite of NVS to handle both problems.
The way data is stored on flash, has been modified: data is stored in length-id-data-length and this complete set is aligned. The length at the end is used as a verification at start (nvs_init). An advantage of this change is also that the metadata (length and id) takes up less space so more data can be stored.
A CRC is not added to the data, but (if required) this can be done by the user application easily.
After each write a read is performed to check if the data was written OK.
When an error is detected (either during write/verification/initialization) NVS goes into a locked state. This allows the recovery of data before the error. A call to reinit() when in locked state erases flash storage and enables write acces.