-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Settings Driver #775
Settings Driver #775
Conversation
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 need some changes fo memory allocations.
|
||
void reorderSettingsBlock(void) | ||
{ | ||
uint8_t *ramBuffer = (uint8_t *)malloc(OPENTHREAD_CONFIG_SETTINGS_PAGE_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.
You can't call malloc/free directly. When this runs on Windows in kernel mode, you have to call different functions to allocate memory.
Current coverage is 73.07% (diff: 86.76%)@@ master #775 diff @@
==========================================
Files 105 108 +3
Lines 15899 16247 +348
Methods 2164 2208 +44
Messages 0 0
Branches 1888 1950 +62
==========================================
+ Hits 11452 11873 +421
+ Misses 3833 3736 -97
- Partials 614 638 +24
|
|
||
void reorderSettingsBlock(void) | ||
{ | ||
uint8_t ramBuffer[OPENTHREAD_CONFIG_SETTINGS_PAGE_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.
Buffering settings in RAM to rewrite the settings block does allow this to operate on a single flash page. However, there are a couple issues with this:
- If the device resets (or loses the memory) for any reason during this operation, all the settings will be lost.
- This requires buffering a significant amount of state in memory before writing it back.
I'd prefer a solution that swaps between two different flash pages. Using two flash pages would avoid needing to buffer all the settings in RAM and can be made resilient to device reboots.
Thoughts?
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.
Agree. It is better.
I will use one flash page as the swap page (similar to the RAM space).
The possible problem is that this page may be read/write many more times than others.
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.
Using two flash pages should not result in more flash writes. Writes happen on a single flash page until there is no more space. When space runs out on page A, the implementation can pull out the useful data, write it to page B, then use page B until that runs out of space. Then switch back to page A.
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. But may it need more than one flash page to store the settings?
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.
Another way is to always use 2* N flash pages. N 's default is 1, but can be larger like 2, 3....
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, the simplest is to support at least 2x the size needed for storing settings. The number of pages should be computed based on an OpenThread compile-time configuration and a platform-specific page size. When I mentioned "page" above, I really meant "flash space used for settings".
I've got a more basic question of why OpenThread is dealing with something so platform specific as flash pages to begin with. On Windows this makes no sense. If we need to persist a setting, we'd likely just write it to a registry key. Shouldn't the otPlat* API be something basic like read/write setting? We can move this code for converting settings to flash pages into an intermediate library a platform can optionally use. |
The |
@jwhui , so would you prefer put settings driver in example/platform/posix and example/platform/cc2538? |
@luwannest, we can leave it where it is now. However, we should provide compile-time options to allow building with an alternative platform-specific implementation of |
757819c
to
9528816
Compare
9528816
to
144476f
Compare
@lluwang, thanks for updating this PR. I'm a little concerned about staging the changes in RAM. I think we could simplify this by removing support for:
The above functions are noted as optional in the docs. Also, we need to update the API to support multiple instances as we have done with the rest of the OpenThread APIs. At the same time, we need to avoid using globals within the implementation. |
{ | ||
kMaxStageAddNum = 3, | ||
kMaxStageDeleteNum = 3, | ||
kMaxStageDataLen = 32, |
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.
Not large enough to support Operational Datasets.
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 removed otPlatSettingsChangesBegin/otPlatSettingsCommitChanges/otPlatSettingsAbandonChanges.
static uint8_t sStageAddSettingsNum; | ||
static uint16_t sStageAddSettingsBufLength; | ||
static struct stageDeleteSettingsBlock sStageDeleteSettingsBlock[kMaxStageDeleteNum]; | ||
static uint8_t sStageDeleteSettingsNum; |
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.
Avoid using globals.
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 still keep using sBase and sUsedSize as global variables,and failed to find a way not using them as global...
@@ -229,7 +229,7 @@ ThreadError otPlatSettingsAdd(uint16_t aKey, const uint8_t *aValue, int aValueLe | |||
* @retval kThreadError_NotImplemented | |||
* This function is not implemented on this platform. | |||
*/ | |||
ThreadError otPlatSettingsDelete(uint16_t aKey, int index); | |||
ThreadError otPlatSettingsDelete(uint16_t aKey, int aIndex); |
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.
Need to update the otPlatSettings*
APIs to take an otInstance
.
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.
otInstance has been added for otPlatSettings_.
I will submit a separate PR for otPlatFlash_.
error = kThreadError_NoBufs); | ||
} | ||
|
||
otPlatFlashWrite(sBase + sUsedSize, reinterpret_cast<uint8_t *>(stageBlock), |
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.
We need to make sure the write is atomic. For example, what if the node reboots in the middle of this flash write? What probably needs to happen here is first write out the block header and data. Then when that's complete, modify a bit in the header to indicate that the write has completed.
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 split kSettingsAdd flag into kBlockAddBeginFlag and kBlockAddCompleteFlag for atomic write.
|
||
if ((!(block.flag & kBlockAddFlag) && block.flag & kBlockDeleteFlag) && (block.key == aKey)) | ||
{ | ||
SuccessOrExit(error = otPlatSettingsDelete(aKey, -1)); |
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.
Should not delete the old setting until the new setting is written. Maybe a simpler approach here is simply not to delete. A later block with the same key and index 0 indicates that all prior blocks are invalidated.
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 changed the logic here.
otPlatSettingsSet is otPlatSettingsAdd with index 0.
The block entry is valid only when
- kSettingsAddComplete flag is not set
- kSettingsDelete flag is set
- there is not block entry containing the same key and <= index after this block entry
- remove support for otPlatSettingsChangesBegin/otPlatSettingsCommitChanges/otPlatSettingsAbandonChanges for embeded system - one block entry is valid, when addComplete not set and Delete is set and not smaller index with the same key found after this block entry - add otInstance support
} OT_TOOL_PACKED_END; | ||
|
||
static uint32_t sBase; | ||
static uint32_t sUsedSize; |
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 can put these in the otInstance
structure and reference these based on the otInstance
pointer passed in.
otPlatFlashRead(address, reinterpret_cast<uint8_t *>(&block), sizeof(block)); | ||
|
||
if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag) && | ||
(block.key == aKey) && (block.index == aIndex || aIndex == -1)) |
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.
Consider we have (A, B, C) and delete B (i.e. index 1), the resulting set will be (A, C) where index of A is 0 and C is 1. The current implementation here does not properly update the index.
I don't actually think you need to store the index. You only need to store the first item (i.e. index 0). That way, if I call delete on index 1 twice, both B and C will be deleted appropriately. If we avoid storing the actual index, I think that will simplify the implementation.
enum | ||
{ | ||
kSettingsFlagSize = 4, | ||
kSettingsBlockDataSize = 32, |
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 will need to buffer up to 255 bytes to support the Operational Datasets.
- Move global variable to otInstance
test_settings_LDADD = $(COMMON_LDADD) \ | ||
$(top_builddir)/examples/platforms/posix/libopenthread-posix.a \ | ||
$(NULL) | ||
test_settings_SOURCES = test_settings.cpp |
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 please follow the model of the tests not depending on a specific platform? Can you please include the test_platform.cpp
as the other tests above do?
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 this case, the implementation in settings.cpp
depends on a working implementation of otPlatFlash*
. We could include examples/platforms/posix/flash.c
directly, but that would still mean this test depends on a specific platform. Thoughts?
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.
The issue here is that to include it in the Windows unit test project it needs to not depend on the platform library. What if we created an in memory implementation of the functions implemented in flash.c
, and put those in test_platform.cpp
? Is the goal here to test the platform code or the core code?
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 a "mock" in-memory implementation of otPlatFlash*
is reasonable. @lluwang, thoughts?
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.
But till my understanding, it is almost the same as what posix does except that using in-memory space but not posix "flash"?
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.
The idea is to remove any platform-specific dependencies (i.e. file operations).
Or is it a bad way to allocate this big (256Kbytes) buffer? |
@lluwang, I just tried running |
Thanks @jwhui ! |
|
||
void RunSettingsTests(void) | ||
{ | ||
TestSettingsInit(); |
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 you add these tests to test_windows.cpp
?
To get appveyor building, you need to fix the warning in |
Actually, I think you can just not include |
Could I know how test_windows.cpp run the unit test? |
It looks like it is running your unit tests, and one is failing. Assertion failed at:
|
It looks like it doesn't necessarily run the tests in sequence. If the tests have dependencies among each other, they aren't really separate tests then. Either you need to make them independent, or make Windows just run |
From Seth Rickard s-rickard@ti.com:
|
If we do like this, is it specific to some platforms? Though settings driver may always only be used in embedded platform. For the potential flash error, do we need to have a component to manage the flash layout? |
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.
Looks good.
@@ -23,6 +23,7 @@ | |||
<ClCompile Include="..\..\src\core\coap\coap_server.cpp" /> | |||
<ClCompile Include="..\..\src\core\common\logging.cpp" /> | |||
<ClCompile Include="..\..\src\core\common\message.cpp" /> | |||
<ClCompile Include="..\..\src\core\common\settings.cpp" /> |
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.
Don't add this file to the kernel mode version.
LGTM 👍 , we can address Seth's concerns in a separate PR. |
…ub_aaf5a57 to master * commit '4da26717b402603ae58dd17c6cad8c33c2a34753': Cli: update 'keysequence' to make keyswithguardtime configurable (openthread#815) Settings Driver (openthread#775) ncp: Added sniffer host tool. (openthread#773)
…a` (openthread#775) Bumps [third_party/openthread/repo](https://github.com/openthread/openthread) from `5bc74b7` to `fa09e8a`. - [Commits](openthread/openthread@5bc74b7...fa09e8a) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This is one of PRs for 9.2.8.