diff --git a/config/kernel-siginfo.m4 b/config/kernel-siginfo.m4 new file mode 100644 index 0000000000..6ddb0dcc37 --- /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 0000000000..6cb86e7c4c --- /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 0000000000..aa60aabebc --- /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 dfb6165d87..7196e66ca2 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -129,6 +129,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]) @@ -231,6 +234,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 fd32f08b34..6b538c8966 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 99d9c9bf38..220742387b 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 db23fb64a2..834c527117 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);