zhaoxingyu12 opened a new pull request, #21769:
URL: https://github.com/apache/nuttx/pull/21769
The scratch buffers under fs/ are freed before the call returns. Take them
from the global temp buffer pool instead of fs_heap: allocation becomes a
bitmap lookup, and the transient traffic no longer fragments fs_heap.
## Summary
The scratch buffers under fs/ are allocated, used within a single call, and
released before that call returns. They were taken from the filesystem private
heap (fs_heap_malloc() / fs_heap_free()), which means every such call pays a
heap allocation and a free, and this purely transient traffic interleaves with
the long-lived allocations in fs_heap and fragments it.
This change takes those buffers from the global temporary buffer pool
(lib_get_tempbuffer() / lib_put_tempbuffer()) instead. When the request fits in
the pool, the allocation is an ffs() on a free bitmap plus one
atomic_cmpxchg(), with no heap involvement at all; requests that do not fit
fall back to an ordinary allocation. The facility is already used for the same
purpose in libs/libc and libs/libbuiltin, so this makes fs/ consistent with
existing practice rather than introducing a new mechanism.
Two API differences are absorbed at the call sites: lib_get_tempbuffer()
does not zero its buffer, so the sites that previously used fs_heap_zalloc()
clear it explicitly; and the facility has no strdup() equivalent, so the single
site that duplicated a string now measures it with strlen() and copies it with
memcpy().
## Impact
Users / API: no change. No public interface, Kconfig option, on-disk format
or error code is added, removed or renamed.
Build: no change to the build system or to any defconfig.
Hardware: none.
Documentation: none.
Security: no change. No new external input is parsed and no buffer size is
derived from untrusted data.
Compatibility: behaviour is preserved, but the memory a request is served
from now depends on configuration. Requests up to TEMP_MAX_SIZE (the larger of
CONFIG_PATH_MAX and CONFIG_LINE_MAX) come from the static pool of
CONFIG_LIBC_MAX_TEMPBUFFER buffers. Larger requests fall back to lib_malloc()
when CONFIG_LIBC_TEMPBUFFER_MALLOC=y. With CONFIG_LIBC_TEMPBUFFER=n the
facility degrades to alloca(), so these buffers come from the caller stack;
configurations that disable the option and run with tight stacks should be
sized accordingly.
## Testing
Host: Ubuntu x86_64, riscv-none-elf-gcc 13.4.0, QEMU 10.2.1
Target: rv-virt:nsh64 (QEMU riscv64, single core)
$ ./tools/configure.sh rv-virt:nsh64
$ make
$ qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -smp 1 \
-bios none -kernel nuttx -nographic
Relevant configuration of the test image:
CONFIG_LIBC_TEMPBUFFER=y
CONFIG_LIBC_MAX_TEMPBUFFER=2
CONFIG_LIBC_TEMPBUFFER_MALLOC=y
CONFIG_PATH_MAX=256 /* TEMP_MAX_SIZE = 256 */
CONFIG_LINE_MAX=80
CONFIG_SENDFILE_BUFSIZE=512
A small test application exercises the two converted call sites that are
reachable from user space, covering the normal path, the empty/NULL path and
the path where the request exceeds the pool and falls back to a heap allocation:
nsh> tbuftest
tbuftest: TEMP_MAX_SIZE=256 MAX_TEMPBUFFER=2 SENDFILE_BUFSIZE=512
--- fs/vfs/fs_select.c ---
[ OK ] select: timeout ret=0 want 0
[ OK ] select: readable ret=1 isset=1 want 1/1
[ OK ] select: nfds=0 x20 ret=0, no crash
[ OK ] select: 40fd pool-overflow ret=40 want 40 (320B)
--- fs/vfs/fs_sendfile.c ---
[ OK ] sendfile: 2000B sent=2000 verify=0 want 2000/0
[ OK ] sendfile: offset=1000 sent=1000 verify=0 want 1000/0
[ OK ] sendfile: 200x no leak/assert
What each case checks:
select: timeout / select: readable — a pipe with and without pending data.
The readable case also confirms that the explicit memset() correctly replaces
the previous fs_heap_zalloc(), since a stale events field would produce wrong
revents.
select: nfds=0 x20 — with nfds == 0 no pollfd array is allocated and pollset
stays NULL; repeated 20 times to confirm the release path handles it.
select: 40fd pool-overflow — 40 pollfd entries are 320 bytes, above
TEMP_MAX_SIZE, so this exercises the fallback allocation.
sendfile: 2000B / offset=1000 — transfers larger than
CONFIG_SENDFILE_BUFSIZE, so the buffer is reused across several iterations; the
destination file is verified byte by byte, with and without the offset argument.
sendfile: 200x — 200 consecutive get/put cycles, to catch a leaked pool slot
or a double release tripping the DEBUGASSERT() in lib_put_tempbuffer().
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]