41 lines
3.4 KiB
Markdown
41 lines
3.4 KiB
Markdown
# B6J2 — rmtfs v1.3 source audit: RAM shadow lifetime with `rmtfs -r -P -v` (no -s)
|
|
|
|
Source: `linux/initramfs-modem1/dl/rmtfs-src` = git tag v1.3 (b30a3eb). Board binary: Alpine `rmtfs 1.3-r0` (aports commit 1628fd4c),
|
|
sha256 1bf6b195… (same in initramfs-mm1); v1.3-specific strings present ("write to %zd bytes exceededs max size", "[RMTFS] bye from %d").
|
|
|
|
## Data structures (storage.c)
|
|
- `static struct rmtfd rmtfds[MAX_CALLERS=10]` — process-global static array; each slot: `id`, `node`, `fd`, `partition`, `shadow_buf`, `shadow_len`.
|
|
- `storage_read_only` (= -r) is a process-global flag set once in `storage_init()`.
|
|
- `storage_init()` is called exactly once in `main()` (rmtfs.c:575) and only initialises slots to fd=-1 / shadow_buf=NULL.
|
|
|
|
## open/read path
|
|
- QMI OPEN (rmtfs.c:55 `rmtfs_open`) → `storage_open(pkt->node, path)` (storage.c:112).
|
|
- storage.c:131-137: **if a slot with `(fd != -1 || shadow_buf) && node == node && partition == part` exists, that slot is returned as-is**
|
|
— no `open()`, no `read()` of the partition.
|
|
- Only for a free slot: `fd_open()` → with -r `storage_populate_shadow_buf()` (storage.c:280): open(O_RDONLY) the by-partlabel device,
|
|
`calloc(len)`, one `read()` of the whole partition into `shadow_buf`, close the fd. This is the only place the backing partition is read.
|
|
|
|
## request handling
|
|
- RW_IOVEC read → `storage_pread()`: with -r, memcpy from `shadow_buf` (storage.c:228-231).
|
|
- RW_IOVEC write → `storage_pwrite()`: with -r, memcpy into `shadow_buf` (realloc if it grows), never touches the fd (storage.c:247-267);
|
|
`storage_sync()` is a no-op with -r (storage.c:274).
|
|
- QMI CLOSE → `storage_close()` → `free(shadow_buf)` (storage.c:176-188). This and `storage_exit()` (process exit) are the ONLY frees.
|
|
|
|
## reconnect behaviour (MSS stop/start while rmtfs lives)
|
|
- QRTR BYE (`rmtfs_bye`) and DEL_CLIENT (`rmtfs_del_client`) handlers only dbgprintf and return 0 (rmtfs.c:346-358): nothing is closed or freed.
|
|
- ENETRESET on the QRTR socket: `main()` loops `do { run_rmtfs() } while (ret == -ENETRESET)` (rmtfs.c:581-583); `run_rmtfs` only
|
|
re-opens/re-publishes the socket; `storage_init/exit` are outside the loop → shadows survive.
|
|
- Without -s: `rprocfd = -1` → rmtfs never writes remoteproc state; SIGTERM → loop `break` → `storage_exit()` → process exits (rmtfs.c:453-455).
|
|
- The modem on this board never sends QMI CLOSE: session-28 rmtfs.log (11 MSS boots) has 0 "close" lines; each MSS stop shows only
|
|
`del_client` + `bye from 0`. The next MSS boot re-opens the same 4 paths from the same node 0 and gets the same caller ids 0..3.
|
|
|
|
## Answer
|
|
With a single long-lived `rmtfs -r -P -v` process, a modem_fs1/fs2/fsg/fsc write received from MSS stays in that slot's `shadow_buf`
|
|
for the lifetime of the process, and a later MSS boot (same node, no CLOSE) is served from that modified RAM copy — **the backing
|
|
partitions are NOT re-read on MSS reconnect**. They are re-read only by a new rmtfs process (or after an explicit QMI CLOSE, which this
|
|
modem does not send). The old B6 flow started a new rmtfs per cycle, so every MSS boot saw the on-disk EFS image.
|
|
→ **GO for B6J3.**
|
|
|
|
Caveats: (1) a hypothetical CLOSE from the modem would free the shadow (debug printf in `rmtfs_close` also passes an int for %s —
|
|
would be UB with -v); not observed. (2) The shadow is only the EFS part of the modem's persistent state; USIM files (EF_EPSLOCI etc.)
|
|
persist across MSS restarts in both flows.
|