From 744cdfd93b9e4d4e8ccc445cdc7371164b1a3bed Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Thu, 15 Apr 2021 13:34:35 -0700 Subject: [PATCH] Add SIGSTOP and SIGTSTP handling to issig This change adds SIGSTOP and SIGTSTP handling to the issig function; this mirrors its behavior on Solaris. This way, long running kernel tasks can be stopped with the appropriate signals. Note that doing so with ctrl-z on the command line doesn't return control of the tty to the shell, because tty handling is done separately from stopping the process. That can be future work, if people feel that it is a necessary addition. Reviewed-by: Brian Behlendorf Reviewed-by: Matthew Ahrens Signed-off-by: Paul Dagnelie Issue #810 Issue #10843 Closes #11801 --- config/kernel-siginfo.m4 | 21 +++++++++++++ config/kernel-signal-stop.m4 | 21 +++++++++++++ config/kernel-special-state.m4 | 21 +++++++++++++ config/kernel.m4 | 6 ++++ include/os/linux/spl/sys/signal.h | 18 +---------- include/os/linux/spl/sys/thread.h | 13 ++++++++ module/os/linux/spl/spl-thread.c | 51 +++++++++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 config/kernel-siginfo.m4 create mode 100644 config/kernel-signal-stop.m4 create mode 100644 config/kernel-special-state.m4 diff --git a/config/kernel-siginfo.m4 b/config/kernel-siginfo.m4 new file mode 100644 index 000000000000..6ddb0dcc37d2 --- /dev/null +++ b/config/kernel-siginfo.m4 @@ -0,0 +1,21 @@ +dnl # +dnl # 4.20 API change +dnl # Added kernel_siginfo_t +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_SIGINFO], [ + ZFS_LINUX_TEST_SRC([siginfo], [ + #include + ],[ + kernel_siginfo_t info __attribute__ ((unused)); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SIGINFO], [ + AC_MSG_CHECKING([whether kernel_siginfo_t tyepedef exists]) + ZFS_LINUX_TEST_RESULT([siginfo], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIGINFO, 1, [kernel_siginfo_t exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel-signal-stop.m4 b/config/kernel-signal-stop.m4 new file mode 100644 index 000000000000..6cb86e7c4cde --- /dev/null +++ b/config/kernel-signal-stop.m4 @@ -0,0 +1,21 @@ +dnl # +dnl # 4.4 API change +dnl # Added kernel_signal_stop +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_SIGNAL_STOP], [ + ZFS_LINUX_TEST_SRC([signal_stop], [ + #include + ],[ + kernel_signal_stop(); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SIGNAL_STOP], [ + AC_MSG_CHECKING([whether signal_stop() exists]) + ZFS_LINUX_TEST_RESULT([signal_stop], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIGNAL_STOP, 1, [signal_stop() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel-special-state.m4 b/config/kernel-special-state.m4 new file mode 100644 index 000000000000..aa60aabebc43 --- /dev/null +++ b/config/kernel-special-state.m4 @@ -0,0 +1,21 @@ +dnl # +dnl # 4.17 API change +dnl # Added set_special_state() function +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_SET_SPECIAL_STATE], [ + ZFS_LINUX_TEST_SRC([set_special_state], [ + #include + ],[ + set_special_state(TASK_STOPPED); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_SET_SPECIAL_STATE], [ + AC_MSG_CHECKING([whether set_special_state() exists]) + ZFS_LINUX_TEST_RESULT([set_special_state], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SET_SPECIAL_STATE, 1, [set_special_state() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index b8d53490a4a5..c66c3a4057fe 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -128,6 +128,9 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_MKNOD ZFS_AC_KERNEL_SRC_SYMLINK ZFS_AC_KERNEL_SRC_BIO_MAX_SEGS + ZFS_AC_KERNEL_SRC_SIGNAL_STOP + ZFS_AC_KERNEL_SRC_SIGINFO + ZFS_AC_KERNEL_SRC_SET_SPECIAL_STATE AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -229,6 +232,9 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_MKNOD ZFS_AC_KERNEL_SYMLINK ZFS_AC_KERNEL_BIO_MAX_SEGS + ZFS_AC_KERNEL_SIGNAL_STOP + ZFS_AC_KERNEL_SIGINFO + ZFS_AC_KERNEL_SET_SPECIAL_STATE ]) dnl # diff --git a/include/os/linux/spl/sys/signal.h b/include/os/linux/spl/sys/signal.h index fd32f08b3489..6b538c8966f2 100644 --- a/include/os/linux/spl/sys/signal.h +++ b/include/os/linux/spl/sys/signal.h @@ -33,22 +33,6 @@ #define FORREAL 0 /* Usual side-effects */ #define JUSTLOOKING 1 /* Don't stop the process */ -/* - * The "why" argument indicates the allowable side-effects of the call: - * - * FORREAL: Extract the next pending signal from p_sig into p_cursig; - * stop the process if a stop has been requested or if a traced signal - * is pending. - * - * JUSTLOOKING: Don't stop the process, just indicate whether or not - * a signal might be pending (FORREAL is needed to tell for sure). - */ -static __inline__ int -issig(int why) -{ - ASSERT(why == FORREAL || why == JUSTLOOKING); - - return (signal_pending(current)); -} +extern int issig(int why); #endif /* SPL_SIGNAL_H */ diff --git a/include/os/linux/spl/sys/thread.h b/include/os/linux/spl/sys/thread.h index 99d9c9bf3821..220742387b62 100644 --- a/include/os/linux/spl/sys/thread.h +++ b/include/os/linux/spl/sys/thread.h @@ -70,4 +70,17 @@ extern struct task_struct *spl_kthread_create(int (*func)(void *), extern proc_t p0; +#ifdef HAVE_SIGINFO +typedef kernel_siginfo_t spl_kernel_siginfo_t; +#else +typedef siginfo_t spl_kernel_siginfo_t; +#endif + +#ifdef HAVE_SET_SPECIAL_STATE +#define spl_set_special_state(x) set_special_state((x)) +#else +#define spl_set_special_state(x) __set_current_state((x)) +#endif + + #endif /* _SPL_THREAD_H */ diff --git a/module/os/linux/spl/spl-thread.c b/module/os/linux/spl/spl-thread.c index db23fb64a298..834c527117a3 100644 --- a/module/os/linux/spl/spl-thread.c +++ b/module/os/linux/spl/spl-thread.c @@ -158,3 +158,54 @@ spl_kthread_create(int (*func)(void *), void *data, const char namefmt[], ...) } while (1); } EXPORT_SYMBOL(spl_kthread_create); + +/* + * The "why" argument indicates the allowable side-effects of the call: + * + * FORREAL: Extract the next pending signal from p_sig into p_cursig; + * stop the process if a stop has been requested or if a traced signal + * is pending. + * + * JUSTLOOKING: Don't stop the process, just indicate whether or not + * a signal might be pending (FORREAL is needed to tell for sure). + */ +int +issig(int why) +{ + ASSERT(why == FORREAL || why == JUSTLOOKING); + + if (!signal_pending(current)) + return (0); + + if (why != FORREAL) + return (1); + + struct task_struct *task = current; + spl_kernel_siginfo_t __info; + sigset_t set; + siginitsetinv(&set, 1ULL << (SIGSTOP - 1) | 1ULL << (SIGTSTP - 1)); + sigorsets(&set, &task->blocked, &set); + + spin_lock_irq(&task->sighand->siglock); + int ret; + if ((ret = dequeue_signal(task, &set, &__info)) != 0) { +#ifdef HAVE_SIGNAL_STOP + spin_unlock_irq(&task->sighand->siglock); + kernel_signal_stop(); +#else + if (current->jobctl & JOBCTL_STOP_DEQUEUED) + spl_set_special_state(TASK_STOPPED); + + spin_unlock_irq(¤t->sighand->siglock); + + schedule(); +#endif + return (0); + } + + spin_unlock_irq(&task->sighand->siglock); + + return (1); +} + +EXPORT_SYMBOL(issig);