diff --git a/config/spl-build.m4 b/config/spl-build.m4 index 926abd5c8..afc8de65f 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -52,6 +52,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_KMEM_CACHE_CREATE_USERCOPY SPL_AC_WAIT_QUEUE_ENTRY_T SPL_AC_WAIT_QUEUE_HEAD_ENTRY + SPL_AC_IO_SCHEDULE_TIMEOUT SPL_AC_KERNEL_WRITE SPL_AC_KERNEL_READ SPL_AC_KERNEL_TIMER_FUNCTION_TIMER_LIST @@ -1598,6 +1599,26 @@ AC_DEFUN([SPL_AC_WAIT_QUEUE_HEAD_ENTRY], [ ]) ]) +dnl # +dnl # 3.19 API change +dnl # The io_schedule_timeout() function is present in all 2.6.32 kernels +dnl # but it was not exported until Linux 3.19. The RHEL 7.x kernels which +dnl # are based on a 3.10 kernel do export this symbol. +dnl # +AC_DEFUN([SPL_AC_IO_SCHEDULE_TIMEOUT], [ + AC_MSG_CHECKING([whether io_schedule_timeout() is available]) + SPL_LINUX_TRY_COMPILE_SYMBOL([ + #include + ], [ + (void) io_schedule_timeout(1); + ], [io_schedule_timeout], [], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IO_SCHEDULE_TIMEOUT, 1, [yes]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + dnl # dnl # 4.14 API change dnl # kernel_write() which was introduced in 3.9 was updated to take diff --git a/include/sys/condvar.h b/include/sys/condvar.h index 5479e75ad..5fcc9068a 100644 --- a/include/sys/condvar.h +++ b/include/sys/condvar.h @@ -56,6 +56,7 @@ extern void __cv_wait(kcondvar_t *, kmutex_t *); extern void __cv_wait_io(kcondvar_t *, kmutex_t *); extern void __cv_wait_sig(kcondvar_t *, kmutex_t *); extern clock_t __cv_timedwait(kcondvar_t *, kmutex_t *, clock_t); +extern clock_t __cv_timedwait_io(kcondvar_t *, kmutex_t *, clock_t); extern clock_t __cv_timedwait_sig(kcondvar_t *, kmutex_t *, clock_t); extern clock_t cv_timedwait_hires(kcondvar_t *, kmutex_t *, hrtime_t, hrtime_t res, int flag); @@ -71,6 +72,7 @@ extern void __cv_broadcast(kcondvar_t *c); #define cv_wait_sig(cvp, mp) __cv_wait_sig(cvp, mp) #define cv_wait_interruptible(cvp, mp) cv_wait_sig(cvp, mp) #define cv_timedwait(cvp, mp, t) __cv_timedwait(cvp, mp, t) +#define cv_timedwait_io(cvp, mp, t) __cv_timedwait_io(cvp, mp, t) #define cv_timedwait_sig(cvp, mp, t) __cv_timedwait_sig(cvp, mp, t) #define cv_timedwait_interruptible(cvp, mp, t) cv_timedwait_sig(cvp, mp, t) #define cv_signal(cvp) __cv_signal(cvp) diff --git a/module/spl/spl-condvar.c b/module/spl/spl-condvar.c index 80c2ef090..4778fb256 100644 --- a/module/spl/spl-condvar.c +++ b/module/spl/spl-condvar.c @@ -136,6 +136,13 @@ __cv_wait(kcondvar_t *cvp, kmutex_t *mp) } EXPORT_SYMBOL(__cv_wait); +void +__cv_wait_io(kcondvar_t *cvp, kmutex_t *mp) +{ + cv_wait_common(cvp, mp, TASK_UNINTERRUPTIBLE, 1); +} +EXPORT_SYMBOL(__cv_wait_io); + void __cv_wait_sig(kcondvar_t *cvp, kmutex_t *mp) { @@ -143,12 +150,34 @@ __cv_wait_sig(kcondvar_t *cvp, kmutex_t *mp) } EXPORT_SYMBOL(__cv_wait_sig); -void -__cv_wait_io(kcondvar_t *cvp, kmutex_t *mp) +#if defined(HAVE_IO_SCHEDULE_TIMEOUT) +#define spl_io_schedule_timeout(t) io_schedule_timeout(t) +#else +static void +__cv_wakeup(unsigned long data) { - cv_wait_common(cvp, mp, TASK_UNINTERRUPTIBLE, 1); + wake_up_process((struct task_struct *)data); } -EXPORT_SYMBOL(__cv_wait_io); + +static long +spl_io_schedule_timeout(long time_left) +{ + long expire_time = jiffies + time_left; + struct timer_list timer; + + init_timer(&timer); + setup_timer(&timer, __cv_wakeup, (unsigned long)current); + timer.expires = expire_time; + add_timer(&timer); + + io_schedule(); + + del_timer_sync(&timer); + time_left = expire_time - jiffies; + + return (time_left < 0 ? 0 : time_left); +} +#endif /* * 'expire_time' argument is an absolute wall clock time in jiffies. @@ -156,7 +185,7 @@ EXPORT_SYMBOL(__cv_wait_io); */ static clock_t __cv_timedwait_common(kcondvar_t *cvp, kmutex_t *mp, clock_t expire_time, - int state) + int state, int io) { DEFINE_WAIT(wait); kmutex_t *m; @@ -188,7 +217,10 @@ __cv_timedwait_common(kcondvar_t *cvp, kmutex_t *mp, clock_t expire_time, * race where 'cvp->cv_waiters > 0' but the list is empty. */ mutex_exit(mp); - time_left = schedule_timeout(time_left); + if (io) + time_left = spl_io_schedule_timeout(time_left); + else + time_left = schedule_timeout(time_left); /* No more waiters a different mutex could be used */ if (atomic_dec_and_test(&cvp->cv_waiters)) { @@ -214,14 +246,24 @@ __cv_timedwait_common(kcondvar_t *cvp, kmutex_t *mp, clock_t expire_time, clock_t __cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t exp_time) { - return (__cv_timedwait_common(cvp, mp, exp_time, TASK_UNINTERRUPTIBLE)); + return (__cv_timedwait_common(cvp, mp, exp_time, + TASK_UNINTERRUPTIBLE, 0)); } EXPORT_SYMBOL(__cv_timedwait); +clock_t +__cv_timedwait_io(kcondvar_t *cvp, kmutex_t *mp, clock_t exp_time) +{ + return (__cv_timedwait_common(cvp, mp, exp_time, + TASK_UNINTERRUPTIBLE, 1)); +} +EXPORT_SYMBOL(__cv_timedwait_io); + clock_t __cv_timedwait_sig(kcondvar_t *cvp, kmutex_t *mp, clock_t exp_time) { - return (__cv_timedwait_common(cvp, mp, exp_time, TASK_INTERRUPTIBLE)); + return (__cv_timedwait_common(cvp, mp, exp_time, + TASK_INTERRUPTIBLE, 0)); } EXPORT_SYMBOL(__cv_timedwait_sig);