Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kernel timer API rework #8647

Merged
merged 1 commit into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 52 additions & 11 deletions config/kernel-timer.m4
Original file line number Diff line number Diff line change
@@ -1,32 +1,73 @@
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,
[timer_list.function gets a 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)
])

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EXTRA_KCFLAGS="$tmp_flags"
])
2 changes: 1 addition & 1 deletion config/kernel.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions include/spl/sys/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
behlendorf marked this conversation as resolved.
Show resolved Hide resolved

#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 */
29 changes: 21 additions & 8 deletions module/spl/spl-condvar.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 4 additions & 20 deletions module/spl/spl-taskq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down