diff --git a/apps/starry/mysql/README.md b/apps/starry/mysql/README.md index 1754f65eda..96a1cfac5e 100644 --- a/apps/starry/mysql/README.md +++ b/apps/starry/mysql/README.md @@ -2,11 +2,25 @@ This app prepares an x86_64 Debian rootfs with Oracle MySQL 8.4.6 generic glibc binaries, then runs a StarryOS guest-side SQL workload. +Only x86_64 Debian/glibc rootfs images are supported. The Oracle generic package is not suitable for aarch64 or Alpine/musl rootfs images. + +## Host Privileges + +MySQL rootfs preparation must run as `root`, or as a user with passwordless `sudo`. +The prebuild script attaches the generated ext4 image with `losetup` and mounts it +to install MySQL, unpack Debian runtime libraries, and write `/root/mysql-env.sh`. +Without those privileges, the app flow stops before QEMU starts. + +Run the app from a root shell, a root-capable container, or a host account that +can run the required mount flow with passwordless `sudo`: + ```bash cargo xtask starry app qemu -t mysql --arch x86_64 ``` -Only x86_64 Debian/glibc rootfs images are supported. The Oracle generic package is not suitable for aarch64 or Alpine/musl rootfs images. +The generated image is cached at `tmp/axbuild/rootfs/rootfs-x86_64-mysql.img`, +and downloads are cached under `target/mysql`, but the current prebuild still +needs root privileges when it verifies, resizes, mounts, and refreshes the image. ## Rootfs Preparation diff --git a/apps/starry/mysql/README_CN.md b/apps/starry/mysql/README_CN.md index b297538694..2e90a6509e 100644 --- a/apps/starry/mysql/README_CN.md +++ b/apps/starry/mysql/README_CN.md @@ -2,11 +2,25 @@ 这个应用会先准备一个带 Oracle MySQL 8.4.6 generic glibc 二进制包的 x86_64 Debian rootfs,然后在 StarryOS guest 中运行 SQL 测试。 +当前只支持 x86_64 Debian/glibc rootfs。Oracle generic 包不适合 aarch64,也不适合 Alpine/musl rootfs。 + +## 宿主机权限要求 + +MySQL rootfs 准备流程必须以 `root` 身份运行,或者由具备 passwordless +`sudo` 的用户运行。`prebuild.sh` 会使用 `losetup` 挂载生成的 ext4 +镜像,然后在镜像内安装 MySQL、解包 Debian 运行时依赖,并写入 +`/root/mysql-env.sh`。如果没有这些权限,app 流程会在 QEMU 启动前停止。 + +请在 root shell、具备 root 权限的容器,或者能以 passwordless `sudo` +执行挂载流程的宿主机账号下运行: + ```bash cargo xtask starry app qemu -t mysql --arch x86_64 ``` -当前只支持 x86_64 Debian/glibc rootfs。Oracle generic 包不适合 aarch64,也不适合 Alpine/musl rootfs。 +生成后的镜像会缓存在 `tmp/axbuild/rootfs/rootfs-x86_64-mysql.img`,下载内容会缓存在 +`target/mysql`,但当前 `prebuild.sh` 在检查、扩容、挂载和刷新镜像时仍然需要 +root 权限。 ## Rootfs 准备 diff --git a/os/StarryOS/kernel/src/syscall/fs/aio.rs b/os/StarryOS/kernel/src/syscall/fs/aio.rs index d6a2c66946..09ed0f8829 100644 --- a/os/StarryOS/kernel/src/syscall/fs/aio.rs +++ b/os/StarryOS/kernel/src/syscall/fs/aio.rs @@ -33,7 +33,7 @@ use starry_signal::SignalSet; use starry_vm::{VmMutPtr, VmPtr}; use crate::{ - file::{Directory, File, FileLike, get_file_like, memfd::Memfd}, + file::{Directory, File, FileLike, event::EventFd, get_file_like, memfd::Memfd}, mm::{AddrSpace, Backend, IoVec}, syscall::signal::check_sigset_size, task::{AsThread, with_blocked_signals}, @@ -159,7 +159,7 @@ struct AioRequest { cb_ptr: usize, data: u64, op: AioOperation, - resfd: Option>, + resfd: Option>, } struct PendingRequest { @@ -607,11 +607,14 @@ fn sync_target_from_fd(fd: c_int) -> AxResult { } // Resolve the optional eventfd notification target from an iocb. -fn resolve_resfd(cb: &Iocb) -> AxResult>> { +fn resolve_resfd(cb: &Iocb) -> AxResult>> { if (cb.flags & IOCB_FLAG_RESFD) == 0 { Ok(None) } else { - get_file_like(cb.resfd as c_int).map(Some) + let file = get_file_like(cb.resfd as c_int)?; + file.downcast_arc::() + .map(Some) + .map_err(|_| AxError::InvalidInput) } } @@ -733,7 +736,7 @@ fn prepare_request( } // Signal an eventfd completion counter when IOCB_FLAG_RESFD is set. -fn notify_resfd(resfd: &Arc) -> AxResult<()> { +fn notify_resfd(resfd: &EventFd) -> AxResult<()> { let data = 1u64.to_ne_bytes(); resfd.write(&mut data.as_slice())?; Ok(()) @@ -1254,10 +1257,7 @@ pub fn sys_io_setup(nr_events: u32, ctxp: *mut AioContextId) -> AxResult } // Destroy an AIO context after cancelling queued work and draining workers. -pub fn sys_io_destroy(ctx: AioContextId) -> AxResult { - debug!("sys_io_destroy called: ctx={:#x}", ctx); - let context = lookup_context(ctx)?; - AIO_CONTEXTS.write().remove(&context.id); +fn destroy_context(context: Arc) { context.destroying.store(true, Ordering::Release); { @@ -1287,6 +1287,35 @@ pub fn sys_io_destroy(ctx: AioContextId) -> AxResult { warn!("sys_io_destroy: failed to unmap ring buffer: {:?}", err); } debug!("sys_io_destroy: success"); +} + +// Destroy all AIO contexts owned by a process during last-thread exit. +pub fn cleanup_aio_contexts_for_pid(pid: Pid) { + let contexts = { + let mut table = AIO_CONTEXTS.write(); + let ids: Vec<_> = table + .iter() + .filter_map(|(&id, context)| (context.owner == pid).then_some(id)) + .collect(); + ids.into_iter() + .filter_map(|id| table.remove(&id)) + .collect::>() + }; + + for context in contexts { + destroy_context(context); + } +} + +// Destroy an AIO context after cancelling queued work and draining workers. +pub fn sys_io_destroy(ctx: AioContextId) -> AxResult { + debug!("sys_io_destroy called: ctx={:#x}", ctx); + let context = lookup_context(ctx)?; + let context = AIO_CONTEXTS + .write() + .remove(&context.id) + .ok_or_else(invalid_context)?; + destroy_context(context); Ok(0) } diff --git a/os/StarryOS/kernel/src/task/ops.rs b/os/StarryOS/kernel/src/task/ops.rs index b221ad6ae9..f2aa8f0e0c 100644 --- a/os/StarryOS/kernel/src/task/ops.rs +++ b/os/StarryOS/kernel/src/task/ops.rs @@ -524,6 +524,11 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { // a non-leader `execve`'s de_thread the two differ, and the thread // group is keyed by the user-visible TID. if process.exit_thread(thr.tid(), exit_code) { + // AIO contexts pin the process address space and may have worker tasks + // waiting on outstanding requests. Tear them down before releasing the + // process address-space slot. + crate::syscall::cleanup_aio_contexts_for_pid(process.pid()); + // Close all file descriptors before marking the process as exited. // This ensures pipe write ends and other resources are properly released, // so parent processes blocking on pipe reads will receive EOF. diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-io-destroy/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-io-destroy/c/src/main.c index dfdef4ddeb..1ea4daa04c 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-io-destroy/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-io-destroy/c/src/main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #define IOCB_CMD_POLL 5 @@ -24,6 +25,42 @@ struct iocb { uint32_t aio_resfd; }; +static void child_exit_without_io_destroy(void) +{ + pid_t child = fork(); + CHECK(child >= 0, "fork child for implicit AIO cleanup"); + if (child == 0) { + aio_context_t child_ctx = 0; + if (syscall(SYS_io_setup, 4, &child_ctx) != 0 || child_ctx == 0) { + _exit(10); + } + + int child_pipe[2] = {-1, -1}; + if (pipe(child_pipe) != 0) { + _exit(11); + } + + struct iocb cb; + memset(&cb, 0, sizeof(cb)); + cb.aio_data = 0x6262; + cb.aio_lio_opcode = IOCB_CMD_POLL; + cb.aio_fildes = (uint32_t)child_pipe[0]; + cb.aio_buf = POLLIN; + struct iocb *list[1] = {&cb}; + if (syscall(SYS_io_submit, child_ctx, 1, list) != 1) { + _exit(12); + } + + _exit(0); + } + + int status = 0; + CHECK_RET(waitpid(child, &status, 0), child, + "wait child that exits without io_destroy"); + CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "child exits cleanly after implicit AIO cleanup"); +} + int main(void) { TEST_START("io_destroy syscall semantics"); @@ -67,6 +104,8 @@ int main(void) CHECK_RET(close(pipefd[1]), 0, "close poll pipe write end"); } + child_exit_without_io_destroy(); + CHECK_ERR(syscall(SYS_io_destroy, 0), EINVAL, "io_destroy rejects context 0"); CHECK_ERR(syscall(SYS_io_destroy, 0x7fffffffUL), EINVAL, diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-io-submit/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-io-submit/c/src/main.c index bfe61f699b..1253cbfb84 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-io-submit/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-io-submit/c/src/main.c @@ -225,6 +225,14 @@ int main(void) CHECK_RET(close(efd), 0, "close aio eventfd"); } + memset(&cb, 0, sizeof(cb)); + cb.aio_lio_opcode = IOCB_CMD_NOOP; + cb.aio_flags = IOCB_FLAG_RESFD; + cb.aio_resfd = (uint32_t)fd; + struct iocb *bad_resfd[1] = {&cb}; + CHECK_ERR(syscall(SYS_io_submit, ctx, 1, bad_resfd), EINVAL, + "io_submit rejects IOCB_FLAG_RESFD with non-eventfd fd"); + struct iocb valid; struct iocb invalid; memset(&valid, 0, sizeof(valid));