944f3d05b5
The patch introduces support for system reboot functionality and process teardown, allowing for a clean shutdown and unmounting of the root filesystem. This is necessary for ensuring a proper system shutdown process, especially when dealing with resource cleanup and ensuring that all processes have exited before system shutdown. Changes: - Added `lwp_teardown()` function to handle process cleanup and system teardown. - Introduced `lwp_pid_wait_for_empty()` to wait for process ID table emptiness before proceeding with shutdown. - Updated `dfs_mnt_unref()` to trigger callbacks when unmounting a filesystem. - Added new reboot types (`RB_AUTOBOOT`, `RB_POWER_OFF`) and implemented their corresponding actions, including cleanup of processes and unmounting root filesystem. - Extended `sys_reboot()` to handle reboot and power off types with appropriate callbacks for process and filesystem teardown. - Introduced callback mechanism for root filesystem unmount notifications. Signed-off-by: Shell <smokewood@qq.com>
162 lines
3.8 KiB
C
162 lines
3.8 KiB
C
/*
|
|
* Copyright (c) 2006-2024, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2024-11-11 Shell moved lwp_startup() from lwp.c;
|
|
* added lwp_teardown()
|
|
*/
|
|
|
|
#define DBG_TAG "lwp"
|
|
#define DBG_LVL DBG_INFO
|
|
#include <rtdbg.h>
|
|
|
|
#include "lwp_internal.h"
|
|
|
|
#include <rthw.h>
|
|
#include <rtthread.h>
|
|
|
|
#include <dfs_file.h>
|
|
#include <dfs_mnt.h>
|
|
#include <dfs_fs.h>
|
|
|
|
/**
|
|
* lwp_runtime:
|
|
* Runtime environment provide by init process including boot scripts,
|
|
* poweroff, shutdown, reboot, service management etc. In the kernel, lwp will
|
|
* provide the underlying software bootstrap and cleanup for the init proc.
|
|
*
|
|
*/
|
|
|
|
rt_weak int lwp_startup_debug_request(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define LATENCY_TIMES (3)
|
|
#define LATENCY_IN_MSEC (128)
|
|
#define LWP_CONSOLE_PATH "CONSOLE=/dev/console"
|
|
const char *init_search_path[] = {
|
|
"/sbin/init",
|
|
"/bin/init",
|
|
};
|
|
|
|
/**
|
|
* Startup process 1 and do the essential works
|
|
*/
|
|
static int lwp_startup(void)
|
|
{
|
|
int error;
|
|
|
|
const char *init_path;
|
|
char *argv[] = {0, "&"};
|
|
char *envp[] = {LWP_CONSOLE_PATH, 0};
|
|
|
|
#ifdef LWP_DEBUG_INIT
|
|
int command;
|
|
int countdown = LATENCY_TIMES;
|
|
while (countdown)
|
|
{
|
|
command = lwp_startup_debug_request();
|
|
if (command)
|
|
{
|
|
return 0;
|
|
}
|
|
rt_kprintf("Press any key to stop init process startup ... %d\n", countdown);
|
|
countdown -= 1;
|
|
rt_thread_mdelay(LATENCY_IN_MSEC);
|
|
}
|
|
rt_kprintf("Starting init ...\n");
|
|
#endif /* LWP_DEBUG_INIT */
|
|
|
|
for (size_t i = 0; i < sizeof(init_search_path)/sizeof(init_search_path[0]); i++)
|
|
{
|
|
struct stat s;
|
|
init_path = init_search_path[i];
|
|
error = stat(init_path, &s);
|
|
if (error == 0)
|
|
{
|
|
argv[0] = (void *)init_path;
|
|
error = lwp_execve((void *)init_path, 0, sizeof(argv)/sizeof(argv[0]), argv, envp);
|
|
if (error < 0)
|
|
{
|
|
LOG_W("%s: failed to setup runtime environment\b"
|
|
"\tlwp_execve() failed with code %d", __func__, error);
|
|
}
|
|
else if (error != 1)
|
|
{
|
|
LOG_W("%s: pid 1 is already allocated", __func__);
|
|
error = -EBUSY;
|
|
}
|
|
else
|
|
{
|
|
rt_lwp_t p = lwp_from_pid_locked(1);
|
|
p->sig_protected = 1;
|
|
|
|
error = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
LOG_D("%s: failed to setup runtime environment\b"
|
|
"\tinit program not found", __func__);
|
|
}
|
|
return error;
|
|
}
|
|
INIT_APP_EXPORT(lwp_startup);
|
|
|
|
/* don't use heap for safety */
|
|
static struct rt_work _teardown_work;
|
|
|
|
#define INIT_PID 1
|
|
static void _teardown_entry(struct rt_work *work, void *work_data)
|
|
{
|
|
int error;
|
|
void (*cb_on_reboot)(void) = work_data;
|
|
|
|
/* cleanup of process */
|
|
do
|
|
{
|
|
error = lwp_pid_wait_for_empty(RT_KILLABLE, RT_WAITING_FOREVER);
|
|
}
|
|
while (error);
|
|
LOG_I("All processes exited");
|
|
|
|
cb_on_reboot();
|
|
return;
|
|
}
|
|
|
|
static int _get_parent_pid(struct rt_lwp *lwp)
|
|
{
|
|
return lwp->parent ? lwp->parent->pid : 0;
|
|
}
|
|
|
|
/* reverse operation of lwp_startup() */
|
|
sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void))
|
|
{
|
|
struct rt_work *work;
|
|
|
|
if (lwp->pid != INIT_PID && _get_parent_pid(lwp) != INIT_PID)
|
|
{
|
|
/* The calling process has insufficient privilege */
|
|
return -EPERM;
|
|
}
|
|
|
|
work = &_teardown_work;
|
|
rt_work_init(work, _teardown_entry, cb);
|
|
|
|
#define SOME_DELAY (RT_TICK_PER_SECOND / 10) /* allow idle to cleanup resource */
|
|
rt_work_submit(work, SOME_DELAY);
|
|
|
|
lwp_exit(lwp, LWP_CREATE_STAT_EXIT(EXIT_SUCCESS));
|
|
|
|
/* never return */
|
|
RT_ASSERT(0);
|
|
return 0;
|
|
}
|