Skip to content
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

Merged
merged 23 commits into from
Oct 17, 2016
Merged

Settings Driver #775

merged 23 commits into from
Oct 17, 2016

Conversation

lluwang
Copy link
Contributor

@lluwang lluwang commented Oct 8, 2016

This is one of PRs for 9.2.8.

Copy link
Contributor

@nibanks nibanks left a 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);
Copy link
Contributor

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.

@codecov-io
Copy link

codecov-io commented Oct 9, 2016

Current coverage is 73.07% (diff: 86.76%)

Merging #775 into master will increase coverage by 1.04%

@@             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   

Powered by Codecov. Last update 10f9204...f66018b


void reorderSettingsBlock(void)
{
uint8_t ramBuffer[OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE];
Copy link
Member

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:

  1. If the device resets (or loses the memory) for any reason during this operation, all the settings will be lost.
  2. 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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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....

Copy link
Member

@jwhui jwhui Oct 9, 2016

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".

@jwhui jwhui added this to the 0.01.00 (Initial Official Release) milestone Oct 9, 2016
@jwhui jwhui self-assigned this Oct 9, 2016
@nibanks
Copy link
Contributor

nibanks commented Oct 9, 2016

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.

@jwhui
Copy link
Member

jwhui commented Oct 9, 2016

The otPlatSettings* API is the only real requirement for OpenThread. At the same time, we expect a common target for OpenThread are MCUs with on-chip flash storage capability. For that reason, we've decided to also define a common implementation of otPlatSettings* that lives on top of otPlatFlash* APIs. The intent is that this implementation of otPlatSettings* is only provided for convenience and can be replaced on a given platform. I agree that this PR in its current form does not completely fulfill the intent.

@lluwang
Copy link
Contributor Author

lluwang commented Oct 9, 2016

@jwhui , so would you prefer put settings driver in example/platform/posix and example/platform/cc2538?

@jwhui
Copy link
Member

jwhui commented Oct 9, 2016

@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 otPlatSettings*. I wouldn't necessarily worry about those compile-time options in this PR, we can introduce those in a separate PR.

@jwhui
Copy link
Member

jwhui commented Oct 11, 2016

@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:

  • otPlatSettingsBeginChange()
  • otPlatSettingsCommitChange()
  • otPlatSettingsAbandonChange()

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,
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using globals.

Copy link
Contributor Author

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);
Copy link
Member

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.

Copy link
Contributor Author

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),
Copy link
Member

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.

Copy link
Contributor Author

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));
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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))
Copy link
Member

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,
Copy link
Member

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
Copy link
Contributor

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?

Copy link
Member

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?

Copy link
Contributor

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?

Copy link
Member

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?

Copy link
Contributor Author

@lluwang lluwang Oct 13, 2016

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"?

Copy link
Member

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

@lluwang
Copy link
Contributor Author

lluwang commented Oct 13, 2016

Or is it a bad way to allocate this big (256Kbytes) buffer?

@jwhui
Copy link
Member

jwhui commented Oct 13, 2016

@lluwang, I just tried running make -f examples/Makefile-posix distcheck on OSX and I see the same failure as on travis.

@lluwang
Copy link
Contributor Author

lluwang commented Oct 13, 2016

Thanks @jwhui !


void RunSettingsTests(void)
{
TestSettingsInit();
Copy link
Contributor

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?

@nibanks
Copy link
Contributor

nibanks commented Oct 14, 2016

To get appveyor building, you need to fix the warning in settings.cpp as shown here.

@nibanks
Copy link
Contributor

nibanks commented Oct 14, 2016

Actually, I think you can just not include settings.cpp in libopenthread_k for now, since the kernel mode version of the library is what I use in the driver on Windows, and I will implement my own versions of the otPlat* APIs. But it would be great still to fix those warnings.

@lluwang
Copy link
Contributor Author

lluwang commented Oct 14, 2016

Could I know how test_windows.cpp run the unit test?
Will it run TestSettingsInit, TestSettingsAdd, TestSettingsDelete, TestSettingsSet, TestSettingsSwap in sequence?

@nibanks
Copy link
Contributor

nibanks commented Oct 14, 2016

It looks like it is running your unit tests, and one is failing. Assertion failed at:

at TestSettingsSwap() in c:\projects\openthread\tests\unit\test_settings.cpp:line 139

@nibanks
Copy link
Contributor

nibanks commented Oct 14, 2016

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 RunSettingsTests and not each individual test.

@jwhui
Copy link
Member

jwhui commented Oct 14, 2016

From Seth Rickard s-rickard@ti.com:

The area your flash driver is writing begins at 0x00239000 and continues for a few pages. This puts the NV storage area about 228K into the chip’s flash. This is well above the code that is currently in OpenThread, but the area is not reserved in the linker script. It would be possible to write an application using OpenThread that has code in that area, without the linker complaining. The application would end up being accidentally overwritten by the settings.

The linker script might look like

SECTIONS
{
    /* place non-volatile storage in pages 254 and 255 */
    .nv_storage 0x7F800:
    {
        _nv_begin = .;
        . += 0x800;
        _nv_page_one = .;
        . += 0x800;
        _nv_end = .;
    } > FLASH
}

Then in the c file, finding the addresses would look like

extern char _nv_begin;
extern char _nv_page_one;
extern char _nv_end;

int someFunc()
{
    char *flash_base = &_nv_begin;
    uint32_t size_of_page = (uint32_t)(&_nv_page_one - &_nv_begin);
    uint8_t num_of_pages = (uint8_t)((&_nv_end - &_nv_begin) / size_of_page);
}

This way you don’t have to use #define s for the base address and page size

@lluwang
Copy link
Contributor Author

lluwang commented Oct 17, 2016

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?

Copy link
Contributor

@nibanks nibanks left a 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" />
Copy link
Contributor

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.

@jwhui
Copy link
Member

jwhui commented Oct 17, 2016

LGTM 👍 , we can address Seth's concerns in a separate PR.

@jwhui jwhui merged commit 8468ffd into openthread:master Oct 17, 2016
vaas-krish pushed a commit to vaas-krish/openthread that referenced this pull request May 23, 2017
…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)
xwang146 pushed a commit to xwang146/openthread that referenced this pull request Jun 3, 2021
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants