Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion apps/starry/mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 15 additions & 1 deletion apps/starry/mysql/README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 准备

Expand Down
47 changes: 38 additions & 9 deletions os/StarryOS/kernel/src/syscall/fs/aio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -159,7 +159,7 @@ struct AioRequest {
cb_ptr: usize,
data: u64,
op: AioOperation,
resfd: Option<Arc<dyn FileLike>>,
resfd: Option<Arc<EventFd>>,
}

struct PendingRequest {
Expand Down Expand Up @@ -607,11 +607,14 @@ fn sync_target_from_fd(fd: c_int) -> AxResult<AioSyncTarget> {
}

// Resolve the optional eventfd notification target from an iocb.
fn resolve_resfd(cb: &Iocb) -> AxResult<Option<Arc<dyn FileLike>>> {
fn resolve_resfd(cb: &Iocb) -> AxResult<Option<Arc<EventFd>>> {
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::<EventFd>()
.map(Some)
.map_err(|_| AxError::InvalidInput)
}
}

Expand Down Expand Up @@ -733,7 +736,7 @@ fn prepare_request(
}

// Signal an eventfd completion counter when IOCB_FLAG_RESFD is set.
fn notify_resfd(resfd: &Arc<dyn FileLike>) -> AxResult<()> {
fn notify_resfd(resfd: &EventFd) -> AxResult<()> {
let data = 1u64.to_ne_bytes();
resfd.write(&mut data.as_slice())?;
Ok(())
Expand Down Expand Up @@ -1254,10 +1257,7 @@ pub fn sys_io_setup(nr_events: u32, ctxp: *mut AioContextId) -> AxResult<isize>
}

// Destroy an AIO context after cancelling queued work and draining workers.
pub fn sys_io_destroy(ctx: AioContextId) -> AxResult<isize> {
debug!("sys_io_destroy called: ctx={:#x}", ctx);
let context = lookup_context(ctx)?;
AIO_CONTEXTS.write().remove(&context.id);
fn destroy_context(context: Arc<AioContext>) {
context.destroying.store(true, Ordering::Release);

{
Expand Down Expand Up @@ -1287,6 +1287,35 @@ pub fn sys_io_destroy(ctx: AioContextId) -> AxResult<isize> {
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::<Vec<_>>()
};

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<isize> {
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)
}

Expand Down
5 changes: 5 additions & 0 deletions os/StarryOS/kernel/src/task/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <poll.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

#define IOCB_CMD_POLL 5
Expand All @@ -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");
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading