[RFC][PATCH 3/4] hrtimer: add nanosleep cancellation

From: Alexander Shishkin
Date: Wed Apr 27 2011 - 06:45:03 EST


This patch implements conditional cancellation for clock_nanosleep
system call. Users who want to be notified of the time changes while
they are sleeping should use TIMER_CANCEL_ON_CLOCK_SET flag and
provide the wall_to_monotonic value they have obtained earlier with
clock_rtoffset call, in rmtp argument. This is only supported for
TIMER_ABSTIME sleepers.

If the provided monotonic offset is still effective, the caller will
sleep until either the requested time comes or somebody changes the
system time, in which case the system call will return ECANCELED. If
the offset has changed by the time of calling clock_nanosleep(), it
will return ECANCELLED straight away.

Signed-off-by: Alexander Shishkin <virtuoso@xxxxxxxxx>
CC: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
CC: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
CC: John Stultz <johnstul@xxxxxxxxxx>
CC: Chris Friesen <chris.friesen@xxxxxxxxxxx>
CC: Kay Sievers <kay.sievers@xxxxxxxx>
CC: Kirill A. Shutemov <kirill@xxxxxxxxxxxxx>
CC: linux-kernel@xxxxxxxxxxxxxxx
---
include/linux/hrtimer.h | 1 +
include/linux/time.h | 1 +
kernel/compat.c | 2 +-
kernel/hrtimer.c | 38 ++++++++++++++++++++++++++++++++++++--
kernel/posix-timers.c | 1 +
5 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index a34b8c6..bf0b8d3 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -444,6 +444,7 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer,
extern long hrtimer_nanosleep(struct timespec *rqtp,
struct timespec __user *rmtp,
const enum hrtimer_mode mode,
+ int cancel_on_clock_set,
const clockid_t clockid);
extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);

diff --git a/include/linux/time.h b/include/linux/time.h
index 8994853..33ff9f3 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -310,5 +310,6 @@ struct itimerval {
* The various flags for setting POSIX.1b interval timers:
*/
#define TIMER_ABSTIME 0x01
+#define TIMER_CANCEL_ON_CLOCK_SET 0x02

#endif
diff --git a/kernel/compat.c b/kernel/compat.c
index 38b1d2c..863b3e1 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -199,7 +199,7 @@ asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp,
set_fs(KERNEL_DS);
ret = hrtimer_nanosleep(&tu,
rmtp ? (struct timespec __user *)&rmt : NULL,
- HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+ HRTIMER_MODE_REL, 0, CLOCK_MONOTONIC);
set_fs(oldfs);

if (ret) {
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 1463164..0a495c9 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -1537,6 +1537,11 @@ static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
return HRTIMER_NORESTART;
}

+static void nanosleeper_cancel(struct hrtimer *timer)
+{
+ hrtimer_wakeup(timer);
+}
+
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
{
sl->timer.function = hrtimer_wakeup;
@@ -1583,6 +1588,20 @@ static int update_rmtp(struct hrtimer *timer, struct timespec __user *rmtp)
return 1;
}

+static int nanosleep_set_cancel_on_clock_set(struct hrtimer_sleeper *t,
+ struct timespec __user *rmtp)
+{
+ struct timespec offset;
+
+ if (!rmtp)
+ return -EINVAL;
+ if (copy_from_user(&offset, rmtp, sizeof(offset)))
+ return -EFAULT;
+
+ return hrtimer_set_cancel_on_clock_set(&t->timer, &offset,
+ nanosleeper_cancel);
+}
+
long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
{
struct hrtimer_sleeper t;
@@ -1611,19 +1630,30 @@ out:
}

long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
- const enum hrtimer_mode mode, const clockid_t clockid)
+ const enum hrtimer_mode mode, int cancel_on_clock_set,
+ const clockid_t clockid)
{
struct restart_block *restart;
struct hrtimer_sleeper t;
int ret = 0;
unsigned long slack;

+ if (cancel_on_clock_set && mode != HRTIMER_MODE_ABS)
+ return -EINVAL;
+
slack = current->timer_slack_ns;
if (rt_task(current))
slack = 0;

hrtimer_init_on_stack(&t.timer, clockid, mode);
hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);
+
+ if (cancel_on_clock_set) {
+ ret = nanosleep_set_cancel_on_clock_set(&t, rmtp);
+ if (ret)
+ goto out;
+ }
+
if (do_nanosleep(&t, mode))
goto out;

@@ -1647,6 +1677,9 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,

ret = -ERESTART_RESTARTBLOCK;
out:
+ if (t.timer.cancel.cancelled)
+ ret = -ECANCELED;
+
destroy_hrtimer_on_stack(&t.timer);
return ret;
}
@@ -1662,7 +1695,8 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
if (!timespec_valid(&tu))
return -EINVAL;

- return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+ return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, 0,
+ CLOCK_MONOTONIC);
}

/*
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 2512708..4791c04 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -1040,6 +1040,7 @@ static int common_nsleep(const clockid_t which_clock, int flags,
{
return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
+ !!(flags & TIMER_CANCEL_ON_CLOCK_SET),
which_clock);
}

--
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/