-
Notifications
You must be signed in to change notification settings - Fork 651
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
RFC: Flash Storage with Isolation #3905
Comments
I agree with your assessment. In brief: don't use In nonvolatile_storage_driver, I did write: tock/capsules/extra/src/nonvolatile_storage_driver.rs Lines 9 to 10 in 01918fa
At the time we didn't have a mechanism for restricting regions to specific apps. Now we do with tock/capsules/extra/src/screen_shared.rs Lines 78 to 81 in 01918fa
|
I should add, I don't think changing nonvolatile_storage_driver.rs should require changing the syscall interface. So it should be possible to work on open thread with full flash before any isolation is supported, or in parallel with updating the capsule. |
Sounds good! We'll move forward with making the appropriate changes to nonvolatile_storage_driver. |
I agree. We already have OpenThread ported to use nonvolatile_storage_driver, so nothing should have to change on the user-space side. |
Not planning on using app_state for openthread anymore. See tock/tock#3905 This reverts commit 8b2dc67.
@bradjc , we were looking into implementing isolation between apps for the non volatile storage driver and came up with two potential approaches. We wanted to get some feedback from you on which approach makes more sense to have in Tock. Option 1: hardcode each app's flash regions in board's
|
let apps_regions = static_init!( | |
[capsules_extra::screen_shared::AppScreenRegion; 3], | |
[ | |
capsules_extra::screen_shared::AppScreenRegion::new( | |
kernel::process::ShortId::Fixed(core::num::NonZeroU32::new(crc("circle")).unwrap()), | |
0, // x | |
0, // y | |
8 * 8, // width | |
8 * 8 // height | |
), | |
capsules_extra::screen_shared::AppScreenRegion::new( | |
kernel::process::ShortId::Fixed(core::num::NonZeroU32::new(crc("count")).unwrap()), | |
8 * 8, // x | |
0, // y | |
8 * 8, // width | |
4 * 8 // height | |
), | |
capsules_extra::screen_shared::AppScreenRegion::new( | |
kernel::process::ShortId::Fixed( | |
core::num::NonZeroU32::new(crc("tock-scroll")).unwrap() | |
), | |
8 * 8, // x | |
4 * 8, // y | |
8 * 8, // width | |
4 * 8 // height | |
) | |
] | |
); |
We could mimic this design for nonvolatile storage. Before the user flashes the kernel, they have to manually specify which range of flash storage correspond to each app. Instead of specifying address ranges, the user could specify how many bytes of flash they want for each app and the kernel will figure out where to put them (like app_state?).
Pros
- Simple to implement
- We know exactly which regions of flash belong to each process when the board boots up
Cons
- Requires modifying main.rs before flashing kernel
- Can't dynamically use more flash than given at compile time
Option 2: dynamic flash allocation
The other approach is to dynamically allocate Pages of non-volatile storage to apps. The flash would be split up into many fixed size pages. Then processes can ask for more flash blocks when they need space.
Implementing this will require a master table inside the capsule to keep track of which AppId owns which page.
Freeing pages is complicated. Processes would have to explicitly free pages because processes may still want to read the data on next power cycle. The OS should not delete user data without permission. Also, the virtual addressing makes it complicated to delete any page that is not at the end of the allocation.
This table gets lost if the board loses power, so allocating new pages has to synchronously commit the table to flash.
Having a dynamic amount of space available also makes the abstraction confusing to the application developer. This is a worse version of a filesystem.
Pros:
- Do not need to specify regions at compile time, which can be error prone
- User doesn't have to recompile the kernel
- Can grow storage usage to the whole flash
Cons:
- Complex logic for address translation
- Need to store tables in memory
- Need tables to be persistent in flash so that we know which pages belong to which process on board reboot
Option 3: There is a third option which gets a bit of the best parts from both.
/// Reserve a region of flash for application usage.
/// This flash must not be used by the kernel
///
/// start_address is the flash address to start the reservation and size
/// is the number of bytes to reserve.
/// app_size is the maximum size a single application can reserve. It must
/// be smaller or equal to `size`
fn reserve_app_flash(start_address: usize, size: usize, app_size: usize) -> Result<> Which reserves a region of flash for applications and sets a maximum application size. Then when an application starts it run a syscall to get the // Return the maximum allocation size for a single application
size_t app_flash_get_max_alloc_size(); An application can then allocate up to that size // Allocate a region of flash of size `size` bytes in flash
// The access permissions are determined by read and write IDs in
// the `perms` struct
int app_flash_allocate_size(size_t size, access_permissions perms); The Tock capsule just needs to append a header at the start of the region with the reserved size and read/write permission information. That way on power up the capsule can iterate through the reserved region to find all of the existing regions. An application can also free a region, in which case the capsule will just erase the entire region (if it can, otherwise it will just write 0's to clear the data). There is a possibility of fragmentation, but it hopefully won't be too bad. The other issue is alignment as you are adding a header to the region the app requested. That could end up wasting an entire flash region to just store a few bytes. But if the size isn't page aligned that shouldn't matter. Pros
Cons
|
I think all of these are good designs, and the benefit of capsules is we don't have to choose, we could support all of them, and let board authors decide what meets their needs. I'm not too worried about freeing allocated storage as that doesn't seem like a common use case for nonvolatile storage. I think for options 2/3, the main question is: use a linked list-esque structure (like TBFs), or a single table for tracking allocations and metadata. The table approach seems more straightforward. |
A table approach is difficult in flash. You basically need to store the table in memory and erase the section at some point, which is risky in terms of data corruption. linked list-esque is probably the way to go |
Hello!
@tyler-potyondy, @Samir-Rashid and I are porting OpenThread to Tock.
Goals
We are looking to utilize Tock's flash features so that OpenThread can save some state persistently.
We have the following two constraints:
Issues
We have looked into Tock's flash offerings and have come across two potential options:
According to our current understanding of these features, they are insufficient for our needs. Can anyone confirm if the following is an accurate understanding and that there is no way to do this flash isolation without keeping the flash region in memory?
app_flash_driver
Here is how we understand the app_flash_driver to work:
Pros:
tock/capsules/extra/src/app_flash_driver.rs
Lines 101 to 111 in 01918fa
nonvolatile_storage_driver
tock/capsules/extra/src/nonvolatile_storage_driver.rs
Lines 218 to 229 in 01918fa
Question
With these two existing offerings, it seems that it is not possible for us to have flash isolation while avoiding mapping our entire persistent state to memory. One potential solution is to add application-level isolation to the existing non-volatile driver. This can re-use the mechanisms that app_state uses for reserving space at app-compile time. Otherwise, we are open to suggestions on how to proceed.
The text was updated successfully, but these errors were encountered: