Kaynağa Gözat

feat: add system reboot and process teardown support

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>
Shell 5 ay önce
ebeveyn
işleme
944f3d05b5

+ 3 - 0
components/dfs/dfs_v2/include/dfs_mnt.h

@@ -64,6 +64,9 @@ rt_bool_t dfs_mnt_has_child_mnt(struct dfs_mnt *mnt, const char* fullpath);
 
 int dfs_mnt_foreach(struct dfs_mnt* (*func)(struct dfs_mnt *mnt, void *parameter), void *parameter);
 
+typedef void (*dfs_mnt_umnt_cb_t)(struct dfs_mnt *mnt);
+RT_OBJECT_HOOKLIST_DECLARE(dfs_mnt_umnt_cb_t, dfs_mnt_umnt);
+
 #ifdef __cplusplus
 }
 #endif

+ 4 - 0
components/dfs/dfs_v2/src/dfs_mnt.c

@@ -21,6 +21,8 @@
 
 static struct dfs_mnt *_root_mnt = RT_NULL;
 
+RT_OBJECT_HOOKLIST_DEFINE(dfs_mnt_umnt);
+
 /*
  * mnt tree structure
  *
@@ -257,6 +259,8 @@ int dfs_mnt_unref(struct dfs_mnt* mnt)
             if (mnt->flags & MNT_IS_UMOUNT)
             {
                 mnt->fs_ops->umount(mnt);
+
+                RT_OBJECT_HOOKLIST_CALL(dfs_mnt_umnt, (mnt));
             }
 
             /* free full path */

+ 9 - 0
components/lwp/libc_musl.h

@@ -11,6 +11,15 @@
 #ifndef __LIBC_MUSL_H__
 #define __LIBC_MUSL_H__
 
+/* from reboot.h */
+#define RB_AUTOBOOT     0x01234567
+#define RB_HALT_SYSTEM  0xcdef0123
+#define RB_ENABLE_CAD   0x89abcdef
+#define RB_DISABLE_CAD  0
+#define RB_POWER_OFF    0x4321fedc
+#define RB_SW_SUSPEND   0xd000fce2
+#define RB_KEXEC        0x45584543
+
 /* from internal/futex.h */
 
 #define FUTEX_WAIT        0

+ 2 - 0
components/lwp/lwp.h

@@ -340,6 +340,8 @@ int lwp_session_set_foreground(rt_session_t session, pid_t pgid);
 /* complete the job control related bussiness on process exit */
 void lwp_jobctrl_on_exit(struct rt_lwp *lwp);
 
+sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void));
+
 #ifdef __cplusplus
 }
 #endif

+ 33 - 2
components/lwp/lwp_pid.c

@@ -69,13 +69,30 @@ static int lwp_pid_ary_alloced = 0;
 static struct lwp_avl_struct *lwp_pid_root = RT_NULL;
 static pid_t current_pid = 0;
 static struct rt_mutex pid_mtx;
+static struct rt_wqueue _pid_emptyq;
 
 int lwp_pid_init(void)
 {
+    rt_wqueue_init(&_pid_emptyq);
     rt_mutex_init(&pid_mtx, "pidmtx", RT_IPC_FLAG_PRIO);
     return 0;
 }
 
+int lwp_pid_wait_for_empty(int wait_flags, rt_tick_t to)
+{
+    int error;
+
+    if (wait_flags == RT_INTERRUPTIBLE)
+    {
+        error = rt_wqueue_wait_interruptible(&_pid_emptyq, 0, to);
+    }
+    else
+    {
+        error = rt_wqueue_wait_killable(&_pid_emptyq, 0, to);
+    }
+    return error;
+}
+
 void lwp_pid_lock_take(void)
 {
     LWP_DEF_RETURN_CODE(rc);
@@ -211,7 +228,15 @@ void lwp_pid_put(struct rt_lwp *lwp)
 
     lwp_pid_lock_take();
     lwp_pid_put_locked(lwp->pid);
-    lwp_pid_lock_release();
+    if (lwp_pid_root == AVL_EMPTY)
+    {
+        rt_wqueue_wakeup_all(&_pid_emptyq, RT_NULL);
+        /* refuse any new pid allocation now */
+    }
+    else
+    {
+        lwp_pid_lock_release();
+    }
 
     /* reset pid field */
     lwp->pid = 0;
@@ -1539,6 +1564,7 @@ static void _notify_parent(rt_lwp_t lwp)
 
 static void _resr_cleanup(struct rt_lwp *lwp)
 {
+    int need_cleanup_pid = RT_FALSE;
     lwp_jobctrl_on_exit(lwp);
 
     LWP_LOCK(lwp);
@@ -1608,7 +1634,7 @@ static void _resr_cleanup(struct rt_lwp *lwp)
          * if process is orphan, it doesn't have parent to do the recycling.
          * Otherwise, its parent had setup a flag to mask out recycling event
          */
-        lwp_pid_put(lwp);
+        need_cleanup_pid = RT_TRUE;
     }
 
     LWP_LOCK(lwp);
@@ -1628,6 +1654,11 @@ static void _resr_cleanup(struct rt_lwp *lwp)
     {
         LWP_UNLOCK(lwp);
     }
+
+    if (need_cleanup_pid)
+    {
+        lwp_pid_put(lwp);
+    }
 }
 
 static int _lwp_setaffinity(int tid, int cpu)

+ 1 - 1
components/lwp/lwp_pid.h

@@ -26,7 +26,7 @@ struct rt_lwp;
 
 struct lwp_avl_struct *lwp_get_pid_ary(void);
 int lwp_pid_init(void);
-
+int lwp_pid_wait_for_empty(int wait_flags, rt_tick_t to);
 int lwp_pid_for_each(int (*cb)(pid_t pid, void *data), void *data);
 void lwp_pid_put(struct rt_lwp *lwp);
 void lwp_pid_lock_take(void);

+ 50 - 0
components/lwp/lwp_runtime.c

@@ -109,3 +109,53 @@ static int lwp_startup(void)
     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;
+}

+ 15 - 4
components/lwp/lwp_syscall.c

@@ -6171,16 +6171,27 @@ sysret_t sys_chown(const char *pathname, uid_t owner, gid_t group)
     return (ret < 0 ? GET_ERRNO() : ret);
 }
 
-#include <sys/reboot.h>
-sysret_t sys_reboot(int magic, int magic2, int type)
+#ifndef LWP_USING_RUNTIME
+sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void))
+{
+    /* if no LWP_USING_RUNTIME configured */
+    return -ENOSYS;
+}
+#endif
+
+sysret_t sys_reboot(int magic, int magic2, int type, void *arg)
 {
     sysret_t rc;
     switch (type)
     {
-        /* TODO add software poweroff protocols */
+        /* Hardware reset */
         case RB_AUTOBOOT:
+            rc = lwp_teardown(lwp_self(), rt_hw_cpu_reset);
+            break;
+
+        /* Stop system and switch power off */
         case RB_POWER_OFF:
-            rt_hw_cpu_reset();
+            rc = lwp_teardown(lwp_self(), rt_hw_cpu_shutdown);
             break;
         default:
             rc = -ENOSYS;