-
Notifications
You must be signed in to change notification settings - Fork 105
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
SD/MMC/FAT partition mouting with esp:mount/4
and umount
#1289
Conversation
src/platforms/esp32/partitions.csv
Outdated
main.avm, data, phy, 0x210000, 0x100000 | ||
boot.avm, data, phy, 0x1D0000, 0x80000, | ||
main.avm, data, phy, 0x250000, 0x80000 | ||
fatpart, data, phy, 0x2D0000, 0x100000 |
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.
@petermm: honestly it is still totally WIP, I just pushed stuff for having a few CI runs ;)
d52cf89
to
6784189
Compare
confirmed working taking image and saving to sd card on esp32cam:
|
esp:mount/4
and umount
esp:mount/4
and umount
esp:mount/4
and umount
@@ -114,6 +124,8 @@ struct ESP32PlatformData | |||
#endif | |||
mbedtls_ctr_drbg_context random_ctx; | |||
bool random_is_initialized; | |||
|
|||
struct SyncList misc_entries; |
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 not the most elegant solution maybe, but for sure avoids changing multiple times ESP32PlatformData.
I suggest keeping this for attaching structs that are not so frequently used, such as mount points, for which a linear search is good enough.
@pguyot on a second thought misc_entries
might be not the best name, what about attachments_list
?
target, &host_config, &slot_config, &mount_config, &mount->handle.card); | ||
#endif | ||
|
||
} else if (!strcmp(source, "sdspi")) { |
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.
Help for manual testing this might be appreciated.
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 tried with several devices but it seems none of the ones I own use SDMMC_HOST_DEFAULT() or SDSPI_HOST_DEFAULT() pins, so for wide spread compatibility these will need to be configurable.
Can you re-run this test after my recent changes? |
mount->misc_entry.entry_type = (uintptr_t) nif_esp_mount; | ||
mount->base_path = strdup(target); | ||
mount->mount_type = FATSPIFlash; | ||
synclist_append(&platform->misc_entries, &mount->misc_entry.list_head); |
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.
Instead of altering platform data, why don't you use a resource? It would unmount if garbage collected, and would make iterating on the list of mount points unnecessary.
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 was one of my thoughts, but I wanted to mimic the same semantic that exists on regular unix where disks are unmounted using the path as handle, so in case we generalize our API we are already support the same semantics.
But, after your feedback, I was thinking that one day we can store paths inside of refs on systems that uses paths as handles for unmounting.
Does it makes sense to you?
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.
Definitely. If the system requires paths to unmount, it is unlikely very short on memory so we can maintain a copy around of the path in the resource. While on ESP32, a resource will consume a little less than the linked list with the copy of the path.
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.
@pguyot on a 3rd thought there are other problems with this approach:
- Basically open files are resources, so we need to make sure that they are closed and garbage collected before running umount.
- I dind't check if umounting can cause errors when files are open, but I don't think there is an option to postpone destructor and running it again when all files have been closed, in order to try umount a second time
- I'm not sure if performing I/O (that might also fail) from a resource destructor is a good idea, but here I can change my mind
In short explicit umount I think gives a better control over operation order and error handling
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 never thought umount should only be implicit. It should be explicit and resource garbage collection should serve as a fallback. The same thing is true with files. Close should be explicitely called.
If necessary, we can increase the ref count of resources that need to be destroyed later. But in this case, it shouldn't be the case. It's a programming error to not explicitely call umount or close, but an error that doesn't leak with a resource (and does without a resource).
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 I fixed this comment.
|
||
struct MountedFS *mount = malloc(sizeof(struct MountedFS)); | ||
mount->misc_entry.entry_type = (uintptr_t) nif_esp_mount; | ||
mount->base_path = strdup(target); |
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'm not sure this is freed, but if this is moved to a resource we probably no longer need it.
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 is a memory leak for sure, indeed.
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.
Also, we could avoid the allocation of strdup
here by simply moving ownership of the pointer (e.g. base_path = target; target = NULL;).
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.
Good point, fixed.
81410bb
to
333b769
Compare
return make_esp_error_tuple(ret, ctx); | ||
} | ||
|
||
mounted_fs->mount_type = Unmounted; |
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.
@pguyot I'm looking for a suggestion here, should I protect the MountedFS resource with some kind of lock?
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 analysis, I believe we need some kind of protection because esp idf function esp_vfs_fat_spiflash_unmount_rw_wl
doesn't look thread safe to me.
If we call it twice with the same value close enough, first core would free the pointer and second would try to free it before it's nullified.
The lock could be implemented around mount_type with a special umounting type, or just setting umounted before it's actually umounted, since we don't have the umounted => mounted transition.
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.
At the end of the day I realized that using a SpinLock was guaranteed to be safe and headache free.
libs/eavmlib/src/esp.erl
Outdated
Source :: unicode:chardata(), | ||
Target :: unicode:chardata(), | ||
FS :: fat, | ||
Opts :: list(proplists:property()) | #{atom() => term()} |
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.
proplists:proplist()
is directly a list.
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.
Fixed.
|
||
char *target = interop_term_to_string(target_term, &str_ok); | ||
if (!str_ok || strlen(target) > 8) { | ||
RAISE_ERROR(BADARG_ATOM); |
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 we free source
here before returning an invalid term?
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, and sorry for the pedantic repeated mistake, I think I fixed all of them.
|
||
struct MountedFS *mount = malloc(sizeof(struct MountedFS)); | ||
mount->misc_entry.entry_type = (uintptr_t) nif_esp_mount; | ||
mount->base_path = strdup(target); |
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.
Also, we could avoid the allocation of strdup
here by simply moving ownership of the pointer (e.g. base_path = target; target = NULL;).
return make_esp_error_tuple(ret, ctx); | ||
} | ||
|
||
mounted_fs->mount_type = Unmounted; |
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 analysis, I believe we need some kind of protection because esp idf function esp_vfs_fat_spiflash_unmount_rw_wl
doesn't look thread safe to me.
If we call it twice with the same value close enough, first core would free the pointer and second would try to free it before it's nullified.
The lock could be implemented around mount_type with a special umounting type, or just setting umounted before it's actually umounted, since we don't have the umounted => mounted transition.
378bfd9
to
2990608
Compare
hmm |
100b5d4
to
20ee7d6
Compare
union | ||
{ | ||
sdmmc_card_t *card; | ||
wl_handle_t wl; |
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'm curious why not use wear leveling for all mounts? It is supported on SD and flash, for both SPIFFS and FatFS.
It's not in the scope of this PR obviously, but I think it would be very beneficial to explore using a filesystem and wear leveling for the application partition, so we do not prematurely wear out the first few sectors of the app partition from heavy rewrites of the same few blocks at the beginning of the partition.
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 depends on the kind of usage you are planning.
However I would rather iterate.
Tested with spiflash on both esp-idf v5 and v4. |
Not working here
gives:
Don't understand the return value of :esp.mount being {:ok, ""}, the two |
libs/eavmlib/src/esp.erl
Outdated
%% @doc Unmounts filesystem located at given path | ||
%% @end | ||
%%----------------------------------------------------------------------------- | ||
-spec umount(Target :: unicode:chardata()) -> 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.
Spec needs to be updated, as it's a resource now.
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.
Fixed
libs/eavmlib/src/esp.erl
Outdated
erlang:nif_error(undefined). | ||
|
||
%%----------------------------------------------------------------------------- | ||
%% @param Target the path of the mounted filesystem that should be unmounted |
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.
Documentation needs to be updated as it's a resource now.
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.
Fixed.
libs/eavmlib/src/esp.erl
Outdated
Target :: unicode:chardata(), | ||
FS :: fat, | ||
Opts :: proplists:proplist() | #{atom() => term()} | ||
) -> 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.
Spec of return type needs to be updated
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.
Fixed.
libs/eavmlib/src/esp.erl
Outdated
%% @param Target the path where the filesystem will be mounted | ||
%% @param FS the filesystem, only fat is supported now | ||
%% @param Opts | ||
%% @returns ok in case of success, otherwise an error |
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.
Documentation of return type needs to be updated
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.
Fixed.
Add missing `export_type([...])` and missing `proplists` type Signed-off-by: Davide Bettio <davide@uninstall.it>
Increase factory app partition size. Signed-off-by: Davide Bettio <davide@uninstall.it>
libs/eavmlib/src/esp.erl
Outdated
Target :: unicode:chardata(), | ||
FS :: fat, | ||
Opts :: proplists:proplist() | #{atom() => term()} | ||
) -> {ok, mounted_fs()} | {error, Reason}. |
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 sure if this is related to the CI build error, but you might want to specify Reason :: term()
here, and again below.
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's the quick typical lunch break change mistake !
libs/eavmlib/src/esp.erl
Outdated
%% @doc Unmounts filesystem located at given path | ||
%% @end | ||
%%----------------------------------------------------------------------------- | ||
-spec umount(Target :: mounted_fs()) -> ok | {error, Reason}. |
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.
See comment above.
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.
Fixed
term_put_tuple_element(return_term, 0, OK_ATOM); | ||
term_put_tuple_element(return_term, 1, mount_term); | ||
} | ||
enif_release_resource(mount); |
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 working sdmmc nor sdspi.
moving enif_release_resource(mount);
down into the umount function makes it all work.
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 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.
oh my, it appears to have been build gremlins somehow, despite everything being cleaned out:/
(changed down to otp26 and it started working, going back to otp27 kept it working :/)
Allow to mount and umount external storage such as SD/MMC or internal flash using `esp:mount/4` and `esp:umount/1`. Right now only `fat` filesystem is supported. Their semantic and parameters resembles unix mount and umount syscalls. Signed-off-by: Davide Bettio <davide@uninstall.it>
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.
Tested successfully sdmmc and sdspi.
Adds a Storage section to the Programmers guide, with the recently added esp:mount/umount atomvm#1289 Signed-off-by: Peter M <petermm@gmail.com>
Adds a Storage section to the Programmers guide, with the recently added esp:mount/umount atomvm#1289 Signed-off-by: Peter M <petermm@gmail.com>
Adds a Storage section to the Programmers guide, with the recently added esp:mount/umount atomvm#1289 Signed-off-by: Peter M <petermm@gmail.com>
Adds a Storage section to the Programmers guide, with the recently added esp:mount/umount atomvm#1289 Signed-off-by: Peter M <petermm@gmail.com>
Add esp:mount/umount to programmers-guide.md Adds a Storage section to the Programmers guide Esp section https://www.atomvm.net/doc/main/programmers-guide.html#esp32-specific-apis , with the recently added esp:mount/umount #1289 Bit unsure of the Internal flash part, but should be good. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later