]> granicus.if.org Git - zfs/commitdiff
kernel timer API rework
authorRafael Kitover <rkitover@gmail.com>
Thu, 23 May 2019 21:40:28 +0000 (14:40 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 23 May 2019 21:40:28 +0000 (14:40 -0700)
In `config/kernel-timer.m4` refactor slightly to check more generally
for the new `timer_setup()` APIs, but also check the callback signature
because some kernels (notably 4.14) have the new `timer_setup()` API but
use the old callback signature. Also add a check for a `flags` member in
`struct timer_list`, which was added in 4.1-rc8.

Add compatibility shims to `include/spl/sys/timer.h` to allow using the
new timer APIs with the only two caveats being that the callback
argument type must be declared as `spl_timer_list_t` and an explicit
assignment is required to get the timer variable for the `timer_of()`
macro. So the callback would look like this:

```c
__cv_wakeup(spl_timer_list_t t)
{
        struct timer_list *tmr = (struct timer_list *)t;
struct thing *parent = from_timer(parent, tmr,
parent_timer_field);
... /* do stuff with parent */
```

Make some minor changes to `spl-condvar.c` and `spl-taskq.c` to use the
new timer APIs instead of conditional code.

Reviewed-by: Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
Closes #8647

config/kernel-timer.m4
config/kernel.m4
include/spl/sys/timer.h
module/spl/spl-condvar.c
module/spl/spl-taskq.c

index 4dc3f84ed47e1a016e567e9831e7f4bdbdb1e435..b0e1afa153ab2e7c6e826abd2ef9c9aebf3ef4a3 100644 (file)
@@ -1,26 +1,51 @@
+dnl # 4.14-rc3 API change
+dnl # https://lwn.net/Articles/735887/
 dnl #
-dnl # 4.15 API change
-dnl # https://lkml.org/lkml/2017/11/25/90
 dnl # Check if timer_list.func get passed a timer_list or an unsigned long
 dnl # (older kernels).  Also sanity check the from_timer() and timer_setup()
 dnl # macros are available as well, since they will be used in the same newer
 dnl # kernels that support the new timer_list.func signature.
 dnl #
-AC_DEFUN([ZFS_AC_KERNEL_TIMER_FUNCTION_TIMER_LIST], [
-       AC_MSG_CHECKING([whether timer_list.function gets a timer_list])
+dnl # Also check for the existance of flags in struct timer_list, they were
+dnl # added in 4.1-rc8 via 0eeda71bc30d.
+
+AC_DEFUN([ZFS_AC_KERNEL_TIMER_SETUP], [
+       AC_MSG_CHECKING([whether timer_setup() is available])
        tmp_flags="$EXTRA_KCFLAGS"
        EXTRA_KCFLAGS="-Werror"
+
        ZFS_LINUX_TRY_COMPILE([
                #include <linux/timer.h>
-               void task_expire(struct timer_list *tl) {}
+
+               struct my_task_timer {
+                       struct timer_list timer;
+                       int data;
+               };
+
+               void task_expire(struct timer_list *tl)
+               {
+                       struct my_task_timer *task_timer = from_timer(task_timer, tl, timer);
+                       task_timer->data = 42;
+               }
+       ],[
+               struct my_task_timer task_timer;
+               timer_setup(&task_timer.timer, task_expire, 0);
        ],[
-               #ifndef from_timer
-               #error "No from_timer() macro"
-               #endif
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_KERNEL_TIMER_SETUP, 1,
+                   [timer_setup() is available])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
 
-               struct timer_list timer;
-               timer.function = task_expire;
-               timer_setup(&timer, NULL, 0);
+       AC_MSG_CHECKING([whether timer function expects timer_list])
+
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/timer.h>
+               void task_expire(struct timer_list *tl) {}
+       ],[
+               struct timer_list tl;
+               tl.function = task_expire;
        ],[
                AC_MSG_RESULT(yes)
                AC_DEFINE(HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST, 1,
@@ -28,5 +53,21 @@ AC_DEFUN([ZFS_AC_KERNEL_TIMER_FUNCTION_TIMER_LIST], [
        ],[
                AC_MSG_RESULT(no)
        ])
+
+       AC_MSG_CHECKING([whether struct timer_list has flags])
+
+       ZFS_LINUX_TRY_COMPILE([
+               #include <linux/timer.h>
+       ],[
+               struct timer_list tl;
+               tl.flags = 2;
+       ],[
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_KERNEL_TIMER_LIST_FLAGS, 1,
+                   [struct timer_list has a flags member])
+       ],[
+               AC_MSG_RESULT(no)
+       ])
+
        EXTRA_KCFLAGS="$tmp_flags"
 ])
index 026a5258f9f03f89fd88832d892167ae7517e121..54f39164bb5f2c39e91e06cb22ec224afc7790dc 100644 (file)
@@ -37,7 +37,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
        ZFS_AC_KERNEL_GROUP_INFO_GID
        ZFS_AC_KERNEL_WRITE
        ZFS_AC_KERNEL_READ
-       ZFS_AC_KERNEL_TIMER_FUNCTION_TIMER_LIST
+       ZFS_AC_KERNEL_TIMER_SETUP
        ZFS_AC_KERNEL_DECLARE_EVENT_CLASS
        ZFS_AC_KERNEL_CURRENT_BIO_TAIL
        ZFS_AC_KERNEL_SUPER_USER_NS
index a6b134570cd85dbabc6ef87900f4c61d108d7b6b..31d89d3b97d6e23016b8c2da377d817261f26667 100644 (file)
@@ -72,4 +72,29 @@ usleep_range(unsigned long min, unsigned long max)
 #define        USEC_TO_TICK(us)                usecs_to_jiffies(us)
 #define        NSEC_TO_TICK(ns)                usecs_to_jiffies(ns / NSEC_PER_USEC)
 
+#ifndef from_timer
+#define        from_timer(var, timer, timer_field) \
+       container_of(timer, typeof(*var), timer_field)
+#endif
+
+#ifdef HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST
+typedef struct timer_list *spl_timer_list_t;
+#else
+typedef unsigned long spl_timer_list_t;
+#endif
+
+#ifndef HAVE_KERNEL_TIMER_SETUP
+
+static inline void
+timer_setup(struct timer_list *timer, void (*func)(spl_timer_list_t), u32 fl)
+{
+#ifdef HAVE_KERNEL_TIMER_LIST_FLAGS
+       (timer)->flags = fl;
+#endif
+       init_timer(timer);
+       setup_timer(timer, func, (spl_timer_list_t)(timer));
+}
+
+#endif /* HAVE_KERNEL_TIMER_SETUP */
+
 #endif  /* _SPL_TIMER_H */
index 1e6e38b7874b1dd2683dc5dfaf56a4fcef02b7f9..a7a9d1db9a9856b7a17e3e0185cc7186d5d5c7ac 100644 (file)
@@ -154,26 +154,39 @@ EXPORT_SYMBOL(__cv_wait_sig);
 #if defined(HAVE_IO_SCHEDULE_TIMEOUT)
 #define        spl_io_schedule_timeout(t)      io_schedule_timeout(t)
 #else
+
+struct spl_task_timer {
+       struct timer_list timer;
+       struct task_struct *task;
+};
+
 static void
-__cv_wakeup(unsigned long data)
+__cv_wakeup(spl_timer_list_t t)
 {
-       wake_up_process((struct task_struct *)data);
+       struct timer_list *tmr = (struct timer_list *)t;
+       struct spl_task_timer *task_timer = from_timer(task_timer, tmr, timer);
+
+       wake_up_process(task_timer->task);
 }
 
 static long
 spl_io_schedule_timeout(long time_left)
 {
        long expire_time = jiffies + time_left;
-       struct timer_list timer;
+       struct spl_task_timer task_timer;
+       struct timer_list *timer = &task_timer.timer;
+
+       task_timer.task = current;
 
-       init_timer(&timer);
-       setup_timer(&timer, __cv_wakeup, (unsigned long)current);
-       timer.expires = expire_time;
-       add_timer(&timer);
+       timer_setup(timer, __cv_wakeup, 0);
+
+       timer->expires = expire_time;
+       add_timer(timer);
 
        io_schedule();
 
-       del_timer_sync(&timer);
+       del_timer_sync(timer);
+
        time_left = expire_time - jiffies;
 
        return (time_left < 0 ? 0 : time_left);
index 7684257be7adebce9d8e3a0c34e525e18b2c54d8..a39f94e4cc20611a1537c64879b8f33d8d223c6c 100644 (file)
@@ -24,6 +24,7 @@
  *  Solaris Porting Layer (SPL) Task Queue Implementation.
  */
 
+#include <sys/timer.h>
 #include <sys/taskq.h>
 #include <sys/kmem.h>
 #include <sys/tsd.h>
@@ -242,20 +243,13 @@ task_expire_impl(taskq_ent_t *t)
        wake_up(&tq->tq_work_waitq);
 }
 
-#ifdef HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST
 static void
-task_expire(struct timer_list *tl)
+task_expire(spl_timer_list_t tl)
 {
-       taskq_ent_t *t = from_timer(t, tl, tqent_timer);
+       struct timer_list *tmr = (struct timer_list *)tl;
+       taskq_ent_t *t = from_timer(t, tmr, tqent_timer);
        task_expire_impl(t);
 }
-#else
-static void
-task_expire(unsigned long data)
-{
-       task_expire_impl((taskq_ent_t *)data);
-}
-#endif
 
 /*
  * Returns the lowest incomplete taskqid_t.  The taskqid_t may
@@ -597,9 +591,6 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
        t->tqent_func = func;
        t->tqent_arg = arg;
        t->tqent_taskq = tq;
-#ifndef HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST
-       t->tqent_timer.data = 0;
-#endif
        t->tqent_timer.function = NULL;
        t->tqent_timer.expires = 0;
        t->tqent_birth = jiffies;
@@ -649,9 +640,6 @@ taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg,
        t->tqent_func = func;
        t->tqent_arg = arg;
        t->tqent_taskq = tq;
-#ifndef HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST
-       t->tqent_timer.data = (unsigned long)t;
-#endif
        t->tqent_timer.function = task_expire;
        t->tqent_timer.expires = (unsigned long)expire_time;
        add_timer(&t->tqent_timer);
@@ -744,11 +732,7 @@ taskq_init_ent(taskq_ent_t *t)
 {
        spin_lock_init(&t->tqent_lock);
        init_waitqueue_head(&t->tqent_waitq);
-#ifdef HAVE_KERNEL_TIMER_FUNCTION_TIMER_LIST
        timer_setup(&t->tqent_timer, NULL, 0);
-#else
-       init_timer(&t->tqent_timer);
-#endif
        INIT_LIST_HEAD(&t->tqent_list);
        t->tqent_id = 0;
        t->tqent_func = NULL;