public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [patch 0/3] [RFC] tick_program_event/clockevents_program_event tweaking
@ 2011-08-23 13:29 Martin Schwidefsky
  2011-08-23 13:29 ` [patch 1/3] clockevent event minimum delay adjustments Martin Schwidefsky
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Martin Schwidefsky @ 2011-08-23 13:29 UTC (permalink / raw)
  To: linux-arch, linux-kernel, linux-s390, Thomas Gleixner,
	Ingo Molnar, john stultz

Greetings,
I rediscovered a couple of clockevents patches which have patiently
been sitting on my hard drive for a rather long time. After some
polishing I guess they are now ready for a review. The aim is to
improve the handling of the clockevents device for s390.

The first patch addresses an issue with the automatic adjustment of
the minimum delay of the clockevents device. We have seen situations
where this adjustment errornously increased the minimum delay on a
virtual system running under z/VM. The only way to get the delay back
to a sane value is a reboot.
To solve this problem a new config option GENERIC_CLOCKEVENTS_MIN_ADJUST
is introduced that allows to select if the automatic increase of the
minimum delay of a clockevents device should be done or not.
The patch enables GENERIC_CLOCKEVENTS_MIN_ADJUST for x86, for s390
we never want to do an adjustment.
Question to the architecture maintainers: are there any other platforms
which will need the adjustment as well?


The second issue that patches #2 and #3 are trying to solve is the fact
that the current code only supports clockevents devices which use a time
delta. The clock comparator found on s390 uses a wall-time value that is
constantly compared to the current TOD clock. If the clock comparator
value is smaller than the TOD clock value an interrupt is made pending. 

The current clockevents code is needlessly complex for this clockevents
device, the function trace of a tick_program_event call looks like this:

  0)               |  tick_program_event() {
  0)               |    tick_dev_program_event() {
  0)               |      ktime_get() {
  0)               |        read_tod_clock() {
  0)   0.336 us    |        } /* read_tod_clock */
  0)   0.692 us    |      } /* ktime_get */
  0)               |      clockevents_program_event() {
  0)               |        s390_next_event() {
  0)   0.190 us    |        } /* s390_next_event */
  0)   0.701 us    |      } /* clockevents_program_event */
  0)   1.901 us    |    } /* tick_dev_program_event */
  0)   2.370 us    |  } /* tick_program_event */

The code does a ktime_get and substracts the result from the expires
value, then passes the delta to the s390_next_event function. This
function then uses get_clock and adds that value to the delta. So
basically the current ktime is first subtracted and then readded to
the expires value for no gain.

The new code implemented by the patches #2 and #3 gives this call trace:

  0)               |  tick_program_event() {
  0)               |    clockevents_program_event() {
  0)               |      s390_next_ktime() {
  0)               |        ktime_get_monotonic_offset() {
  0)   0.183 us    |        } /* ktime_get_monotonic_offset */
  0)   0.734 us    |      } /* s390_next_ktime */
  0)   1.120 us    |    } /* clockevents_program_event */
  0)   1.557 us    |  } /* tick_program_event */

The function tick_dev_program_event is gone, clockevents_program_event
passes the unmodified expires value to s390_next_ktime. This new
function only needs to subtract the wall_to_monotonic offset to get
the wall-clock value to program the clock comparator. And forcing the
minimum delay on a clockevent device with CLOCK_EVT_FEAT_KTIME is not
necessary anymore. Any ktime value can be programmed to the clock
comparator, even one that is in the past. As soon as the irqs are
open again we will simply get an interrupt.

The old code needs 151 instruction for a tick_program_event call,
the new one only 85. A nice improvement.

To: linux-arch@vger.kernel.org
To: linux-kernel@vger.kernel.org
To: linux-s390@vger.kernel.org
To: Ingo Molnar <mingo@elte.hu>
To: Thomas Gleixner <tglx@linutronix.de>
To: john stultz <johnstul@us.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

-- 
blue skies,
   Martin.

"Reality continues to ruin my life." - Calvin.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [patch 1/3] clockevent event minimum delay adjustments
  2011-08-23 13:29 [patch 0/3] [RFC] tick_program_event/clockevents_program_event tweaking Martin Schwidefsky
@ 2011-08-23 13:29 ` Martin Schwidefsky
  2011-08-23 13:29 ` [patch 2/3] direct ktime path for clockevent devices Martin Schwidefsky
  2011-08-23 13:29 ` [patch 3/3] use direct ktime path for s390 clockevent device Martin Schwidefsky
  2 siblings, 0 replies; 4+ messages in thread
From: Martin Schwidefsky @ 2011-08-23 13:29 UTC (permalink / raw)
  To: linux-arch, linux-kernel, linux-s390, Thomas Gleixner,
	Ingo Molnar, john stultz
  Cc: Martin Schwidefsky

[-- Attachment #1: clockevent-move-force.diff --]
[-- Type: text/plain, Size: 12734 bytes --]

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

The automatic increase of the min_delta_ns of a clockevents devices
should be done in the clockevents code as the minimum delay is an
attribute of the clockevents device.
In addition not all architectures want the automatic adjustment, on a
massively virtualized system it can happen that the programming of a
clock event fails several times in a row because the virtual cpu has
been rescheduled quickly enough. In that case the minimum delay will
erroneously be increased with no way back. The new config symbol
GENERIC_CLOCKEVENTS_MIN_ADJUST is used to enable the automatic
adjustment. The config option is selected only for x86.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
 arch/x86/Kconfig             |    1 
 include/linux/clockchips.h   |    2 
 kernel/time/Kconfig          |    2 
 kernel/time/clockevents.c    |  125 ++++++++++++++++++++++++++++++++++++++-----
 kernel/time/tick-broadcast.c |    4 -
 kernel/time/tick-common.c    |    4 -
 kernel/time/tick-internal.h  |    2 
 kernel/time/tick-oneshot.c   |   77 +-------------------------
 8 files changed, 123 insertions(+), 94 deletions(-)

Index: linux-clockevents/arch/x86/Kconfig
===================================================================
--- linux-clockevents.orig/arch/x86/Kconfig	2011-08-09 17:40:09.252011057 +0200
+++ linux-clockevents/arch/x86/Kconfig	2011-08-23 15:15:59.155588652 +0200
@@ -68,6 +68,7 @@
 	select GENERIC_IRQ_PROBE
 	select GENERIC_PENDING_IRQ if SMP
 	select GENERIC_IRQ_SHOW
+	select GENERIC_CLOCKEVENTS_MIN_ADJUST
 	select IRQ_FORCED_THREADING
 	select USE_GENERIC_SMP_HELPERS if SMP
 	select HAVE_BPF_JIT if (X86_64 && NET)
Index: linux-clockevents/include/linux/clockchips.h
===================================================================
--- linux-clockevents.orig/include/linux/clockchips.h	2011-05-20 13:13:38.365605338 +0200
+++ linux-clockevents/include/linux/clockchips.h	2011-08-23 15:15:59.159588700 +0200
@@ -140,7 +140,7 @@
 				 enum clock_event_mode mode);
 extern int clockevents_register_notifier(struct notifier_block *nb);
 extern int clockevents_program_event(struct clock_event_device *dev,
-				     ktime_t expires, ktime_t now);
+				     ktime_t expires, int force);
 
 extern void clockevents_handle_noop(struct clock_event_device *dev);
 
Index: linux-clockevents/kernel/time/Kconfig
===================================================================
--- linux-clockevents.orig/kernel/time/Kconfig	2010-08-09 17:53:26.000000000 +0200
+++ linux-clockevents/kernel/time/Kconfig	2011-08-23 15:15:59.159588700 +0200
@@ -27,3 +27,5 @@
 	default y
 	depends on GENERIC_CLOCKEVENTS || GENERIC_CLOCKEVENTS_MIGR
 
+config GENERIC_CLOCKEVENTS_MIN_ADJUST
+	bool
Index: linux-clockevents/kernel/time/clockevents.c
===================================================================
--- linux-clockevents.orig/kernel/time/clockevents.c	2011-06-08 19:17:59.275636952 +0200
+++ linux-clockevents/kernel/time/clockevents.c	2011-08-23 15:15:59.159588700 +0200
@@ -94,42 +94,139 @@
 	dev->next_event.tv64 = KTIME_MAX;
 }
 
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST
+
+/* Limit min_delta to a jiffie */
+#define MIN_DELTA_LIMIT		(NSEC_PER_SEC / HZ)
+
+/**
+ * clockevents_increase_min_delta - raise minimum delta of a clock event device
+ * @dev:       device to increase the minimum delta
+ *
+ * Returns 0 on success, -ETIME when the minimum delta reached the limit.
+ */
+static int clockevents_increase_min_delta(struct clock_event_device *dev)
+{
+	/* Nothing to do if we already reached the limit */
+	if (dev->min_delta_ns >= MIN_DELTA_LIMIT) {
+		printk(KERN_WARNING "CE: Reprogramming failure. Giving up\n");
+		dev->next_event.tv64 = KTIME_MAX;
+		return -ETIME;
+	}
+
+	if (dev->min_delta_ns < 5000)
+		dev->min_delta_ns = 5000;
+	else
+		dev->min_delta_ns += dev->min_delta_ns >> 1;
+
+	if (dev->min_delta_ns > MIN_DELTA_LIMIT)
+		dev->min_delta_ns = MIN_DELTA_LIMIT;
+
+	printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n",
+	       dev->name ? dev->name : "?",
+	       (unsigned long long) dev->min_delta_ns);
+	return 0;
+}
+
+/**
+ * clockevents_program_min_delta - Set clock event device to the minimum delay.
+ * @dev:	device to program
+ *
+ * Returns 0 on success, -ETIME when the retry loop failed.
+ */
+static int clockevents_program_min_delta(struct clock_event_device *dev)
+{
+	unsigned long long clc;
+	int64_t delta;
+	int i;
+
+	for (i = 0;;) {
+		delta = dev->min_delta_ns;
+		dev->next_event = ktime_add_ns(ktime_get(), delta);
+
+		if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN)
+			return 0;
+
+		dev->retries++;
+		clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+		if (dev->set_next_event((unsigned long) clc, dev) == 0)
+			return 0;
+
+		if (++i > 2) {
+			/*
+			 * We tried 3 times to program the device with the
+			 * given min_delta_ns. Try to increase the minimum
+			 * delta, if that fails as well get out of here.
+			 */
+			if (clockevents_increase_min_delta(dev))
+				return -ETIME;
+			i = 0;
+		}
+	}
+}
+
+#else  /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
+
+/**
+ * clockevents_program_min_delta - Set clock event device to the minimum delay.
+ * @dev:	device to program
+ *
+ * Returns 0 on success, -ETIME when the retry loop failed.
+ */
+static int clockevents_program_min_delta(struct clock_event_device *dev)
+{
+	unsigned long long clc;
+	int64_t delta;
+
+	delta = dev->min_delta_ns;
+	dev->next_event = ktime_add_ns(ktime_get(), delta);
+
+	if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN)
+		return 0;
+
+	dev->retries++;
+	clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+	return dev->set_next_event((unsigned long) clc, dev);
+}
+
+#endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
+
 /**
  * clockevents_program_event - Reprogram the clock event device.
+ * @dev:	device to program
  * @expires:	absolute expiry time (monotonic clock)
+ * @force:	program minimum delay if expires can not be set
  *
  * Returns 0 on success, -ETIME when the event is in the past.
  */
 int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
-			      ktime_t now)
+			      int force)
 {
 	unsigned long long clc;
 	int64_t delta;
+	int rc;
 
 	if (unlikely(expires.tv64 < 0)) {
 		WARN_ON_ONCE(1);
 		return -ETIME;
 	}
 
-	delta = ktime_to_ns(ktime_sub(expires, now));
-
-	if (delta <= 0)
-		return -ETIME;
-
 	dev->next_event = expires;
 
 	if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN)
 		return 0;
 
-	if (delta > dev->max_delta_ns)
-		delta = dev->max_delta_ns;
-	if (delta < dev->min_delta_ns)
-		delta = dev->min_delta_ns;
+	delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
+	if (delta <= 0)
+		return force ? clockevents_program_min_delta(dev) : -ETIME;
 
-	clc = delta * dev->mult;
-	clc >>= dev->shift;
+	delta = min(delta, (int64_t) dev->max_delta_ns);
+	delta = max(delta, (int64_t) dev->min_delta_ns);
 
-	return dev->set_next_event((unsigned long) clc, dev);
+	clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+	rc = dev->set_next_event((unsigned long) clc, dev);
+
+	return (rc && force) ? clockevents_program_min_delta(dev) : rc;
 }
 
 /**
@@ -258,7 +355,7 @@
 	if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
 		return 0;
 
-	return clockevents_program_event(dev, dev->next_event, ktime_get());
+	return clockevents_program_event(dev, dev->next_event, 0);
 }
 
 /*
Index: linux-clockevents/kernel/time/tick-broadcast.c
===================================================================
--- linux-clockevents.orig/kernel/time/tick-broadcast.c	2011-05-24 13:02:20.066640590 +0200
+++ linux-clockevents/kernel/time/tick-broadcast.c	2011-08-23 15:15:59.159588700 +0200
@@ -194,7 +194,7 @@
 	for (next = dev->next_event; ;) {
 		next = ktime_add(next, tick_period);
 
-		if (!clockevents_program_event(dev, next, ktime_get()))
+		if (!clockevents_program_event(dev, next, 0))
 			return;
 		tick_do_periodic_broadcast();
 	}
@@ -373,7 +373,7 @@
 {
 	struct clock_event_device *bc = tick_broadcast_device.evtdev;
 
-	return tick_dev_program_event(bc, expires, force);
+	return clockevents_program_event(bc, expires, force);
 }
 
 int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
Index: linux-clockevents/kernel/time/tick-common.c
===================================================================
--- linux-clockevents.orig/kernel/time/tick-common.c	2011-03-16 09:26:42.994913981 +0100
+++ linux-clockevents/kernel/time/tick-common.c	2011-08-23 15:15:59.159588700 +0200
@@ -94,7 +94,7 @@
 	 */
 	next = ktime_add(dev->next_event, tick_period);
 	for (;;) {
-		if (!clockevents_program_event(dev, next, ktime_get()))
+		if (!clockevents_program_event(dev, next, 0))
 			return;
 		/*
 		 * Have to be careful here. If we're in oneshot mode,
@@ -137,7 +137,7 @@
 		clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
 
 		for (;;) {
-			if (!clockevents_program_event(dev, next, ktime_get()))
+			if (!clockevents_program_event(dev, next, 0))
 				return;
 			next = ktime_add(next, tick_period);
 		}
Index: linux-clockevents/kernel/time/tick-internal.h
===================================================================
--- linux-clockevents.orig/kernel/time/tick-internal.h	2011-03-16 09:26:42.994913981 +0100
+++ linux-clockevents/kernel/time/tick-internal.h	2011-08-23 15:15:59.159588700 +0200
@@ -26,8 +26,6 @@
 extern void tick_setup_oneshot(struct clock_event_device *newdev,
 			       void (*handler)(struct clock_event_device *),
 			       ktime_t nextevt);
-extern int tick_dev_program_event(struct clock_event_device *dev,
-				  ktime_t expires, int force);
 extern int tick_program_event(ktime_t expires, int force);
 extern void tick_oneshot_notify(void);
 extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));
Index: linux-clockevents/kernel/time/tick-oneshot.c
===================================================================
--- linux-clockevents.orig/kernel/time/tick-oneshot.c	2011-03-16 09:26:42.994913981 +0100
+++ linux-clockevents/kernel/time/tick-oneshot.c	2011-08-23 15:15:59.159588700 +0200
@@ -21,74 +21,6 @@
 
 #include "tick-internal.h"
 
-/* Limit min_delta to a jiffie */
-#define MIN_DELTA_LIMIT		(NSEC_PER_SEC / HZ)
-
-static int tick_increase_min_delta(struct clock_event_device *dev)
-{
-	/* Nothing to do if we already reached the limit */
-	if (dev->min_delta_ns >= MIN_DELTA_LIMIT)
-		return -ETIME;
-
-	if (dev->min_delta_ns < 5000)
-		dev->min_delta_ns = 5000;
-	else
-		dev->min_delta_ns += dev->min_delta_ns >> 1;
-
-	if (dev->min_delta_ns > MIN_DELTA_LIMIT)
-		dev->min_delta_ns = MIN_DELTA_LIMIT;
-
-	printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n",
-	       dev->name ? dev->name : "?",
-	       (unsigned long long) dev->min_delta_ns);
-	return 0;
-}
-
-/**
- * tick_program_event internal worker function
- */
-int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
-			   int force)
-{
-	ktime_t now = ktime_get();
-	int i;
-
-	for (i = 0;;) {
-		int ret = clockevents_program_event(dev, expires, now);
-
-		if (!ret || !force)
-			return ret;
-
-		dev->retries++;
-		/*
-		 * We tried 3 times to program the device with the given
-		 * min_delta_ns. If that's not working then we increase it
-		 * and emit a warning.
-		 */
-		if (++i > 2) {
-			/* Increase the min. delta and try again */
-			if (tick_increase_min_delta(dev)) {
-				/*
-				 * Get out of the loop if min_delta_ns
-				 * hit the limit already. That's
-				 * better than staying here forever.
-				 *
-				 * We clear next_event so we have a
-				 * chance that the box survives.
-				 */
-				printk(KERN_WARNING
-				       "CE: Reprogramming failure. Giving up\n");
-				dev->next_event.tv64 = KTIME_MAX;
-				return -ETIME;
-			}
-			i = 0;
-		}
-
-		now = ktime_get();
-		expires = ktime_add_ns(now, dev->min_delta_ns);
-	}
-}
-
 /**
  * tick_program_event
  */
@@ -96,7 +28,7 @@
 {
 	struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
 
-	return tick_dev_program_event(dev, expires, force);
+	return clockevents_program_event(dev, expires, force);
 }
 
 /**
@@ -104,11 +36,10 @@
  */
 void tick_resume_oneshot(void)
 {
-	struct tick_device *td = &__get_cpu_var(tick_cpu_device);
-	struct clock_event_device *dev = td->evtdev;
+	struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
 
 	clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
-	tick_program_event(ktime_get(), 1);
+	clockevents_program_event(dev, ktime_get(), 1);
 }
 
 /**
@@ -120,7 +51,7 @@
 {
 	newdev->event_handler = handler;
 	clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
-	tick_dev_program_event(newdev, next_event, 1);
+	clockevents_program_event(newdev, next_event, 1);
 }
 
 /**


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [patch 2/3] direct ktime path for clockevent devices
  2011-08-23 13:29 [patch 0/3] [RFC] tick_program_event/clockevents_program_event tweaking Martin Schwidefsky
  2011-08-23 13:29 ` [patch 1/3] clockevent event minimum delay adjustments Martin Schwidefsky
@ 2011-08-23 13:29 ` Martin Schwidefsky
  2011-08-23 13:29 ` [patch 3/3] use direct ktime path for s390 clockevent device Martin Schwidefsky
  2 siblings, 0 replies; 4+ messages in thread
From: Martin Schwidefsky @ 2011-08-23 13:29 UTC (permalink / raw)
  To: linux-arch, linux-kernel, linux-s390, Thomas Gleixner,
	Ingo Molnar, john stultz
  Cc: Martin Schwidefsky

[-- Attachment #1: clockevent-feat-ktime.diff --]
[-- Type: text/plain, Size: 2918 bytes --]

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

There is at least one architecture (s390) with a sane clockevent device
that can be programmed with the equivalent of a ktime. No need to create
a delta against the current time, the ktime can be used directly.

A new clock device function 'set_next_ktime' is introduced that is called
with the unmodified ktime for the timer if the clock event device has the 
CLOCK_EVT_FEAT_KTIME bit set.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
 include/linux/clockchips.h |   10 +++++++---
 kernel/time/clockevents.c  |    4 ++++
 2 files changed, 11 insertions(+), 3 deletions(-)

Index: linux-clockevents/include/linux/clockchips.h
===================================================================
--- linux-clockevents.orig/include/linux/clockchips.h	2011-08-23 15:15:59.159588700 +0200
+++ linux-clockevents/include/linux/clockchips.h	2011-08-23 15:16:02.327629024 +0200
@@ -45,20 +45,22 @@
  */
 #define CLOCK_EVT_FEAT_PERIODIC		0x000001
 #define CLOCK_EVT_FEAT_ONESHOT		0x000002
+#define CLOCK_EVT_FEAT_KTIME		0x000004
 /*
  * x86(64) specific misfeatures:
  *
  * - Clockevent source stops in C3 State and needs broadcast support.
  * - Local APIC timer is used as a dummy device.
  */
-#define CLOCK_EVT_FEAT_C3STOP		0x000004
-#define CLOCK_EVT_FEAT_DUMMY		0x000008
+#define CLOCK_EVT_FEAT_C3STOP		0x000008
+#define CLOCK_EVT_FEAT_DUMMY		0x000010
 
 /**
  * struct clock_event_device - clock event device descriptor
  * @event_handler:	Assigned by the framework to be called by the low
  *			level handler of the event source
- * @set_next_event:	set next event function
+ * @set_next_event:	set next event function using a clocksource delta
+ * @set_next_ktime:	set next event function using a direct ktime value
  * @next_event:		local storage for the next event in oneshot mode
  * @max_delta_ns:	maximum delta value in ns
  * @min_delta_ns:	minimum delta value in ns
@@ -81,6 +83,8 @@
 	void			(*event_handler)(struct clock_event_device *);
 	int			(*set_next_event)(unsigned long evt,
 						  struct clock_event_device *);
+	int			(*set_next_ktime)(ktime_t expires,
+						  struct clock_event_device *);
 	ktime_t			next_event;
 	u64			max_delta_ns;
 	u64			min_delta_ns;
Index: linux-clockevents/kernel/time/clockevents.c
===================================================================
--- linux-clockevents.orig/kernel/time/clockevents.c	2011-08-23 15:15:59.159588700 +0200
+++ linux-clockevents/kernel/time/clockevents.c	2011-08-23 15:16:02.327629024 +0200
@@ -216,6 +216,10 @@
 	if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN)
 		return 0;
 
+	/* Shortcut for clockevent devices that can deal with ktime. */
+	if (dev->features & CLOCK_EVT_FEAT_KTIME)
+		return dev->set_next_ktime(expires, dev);
+
 	delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
 	if (delta <= 0)
 		return force ? clockevents_program_min_delta(dev) : -ETIME;


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [patch 3/3] use direct ktime path for s390 clockevent device
  2011-08-23 13:29 [patch 0/3] [RFC] tick_program_event/clockevents_program_event tweaking Martin Schwidefsky
  2011-08-23 13:29 ` [patch 1/3] clockevent event minimum delay adjustments Martin Schwidefsky
  2011-08-23 13:29 ` [patch 2/3] direct ktime path for clockevent devices Martin Schwidefsky
@ 2011-08-23 13:29 ` Martin Schwidefsky
  2 siblings, 0 replies; 4+ messages in thread
From: Martin Schwidefsky @ 2011-08-23 13:29 UTC (permalink / raw)
  To: linux-arch, linux-kernel, linux-s390, Thomas Gleixner,
	Ingo Molnar, john stultz
  Cc: Martin Schwidefsky

[-- Attachment #1: clockevent-feat-ktime-s390.diff --]
[-- Type: text/plain, Size: 1885 bytes --]

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

The clock comparator on s390 uses the same format as the TOD clock.
If the value in the clock comparator is smaller than the current TOD
value an interrupt is pending. Use the CLOCK_EVT_FEAT_KTIME feature
to get the unmodified ktime of the next clockevent expiration and
use it to program the clock comparator without querying the TOD clock.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
 arch/s390/kernel/time.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

Index: linux-clockevents/arch/s390/kernel/time.c
===================================================================
--- linux-clockevents.orig/arch/s390/kernel/time.c	2011-05-31 12:30:48.082228487 +0200
+++ linux-clockevents/arch/s390/kernel/time.c	2011-08-23 15:16:03.219640374 +0200
@@ -109,10 +109,14 @@
 	set_clock_comparator(S390_lowcore.clock_comparator);
 }
 
-static int s390_next_event(unsigned long delta,
+static int s390_next_ktime(ktime_t expires,
 			   struct clock_event_device *evt)
 {
-	S390_lowcore.clock_comparator = get_clock() + delta;
+	s64 nsecs;
+
+	nsecs = ktime_to_ns(ktime_sub(expires, ktime_get_monotonic_offset()));
+	do_div(nsecs, 125);
+	S390_lowcore.clock_comparator = TOD_UNIX_EPOCH + (nsecs << 9);
 	set_clock_comparator(S390_lowcore.clock_comparator);
 	return 0;
 }
@@ -137,14 +141,15 @@
 	cpu = smp_processor_id();
 	cd = &per_cpu(comparators, cpu);
 	cd->name		= "comparator";
-	cd->features		= CLOCK_EVT_FEAT_ONESHOT;
+	cd->features		= CLOCK_EVT_FEAT_ONESHOT |
+				  CLOCK_EVT_FEAT_KTIME;
 	cd->mult		= 16777;
 	cd->shift		= 12;
 	cd->min_delta_ns	= 1;
 	cd->max_delta_ns	= LONG_MAX;
 	cd->rating		= 400;
 	cd->cpumask		= cpumask_of(cpu);
-	cd->set_next_event	= s390_next_event;
+	cd->set_next_ktime	= s390_next_ktime;
 	cd->set_mode		= s390_set_mode;
 
 	clockevents_register_device(cd);


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2011-08-23 13:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-08-23 13:29 [patch 0/3] [RFC] tick_program_event/clockevents_program_event tweaking Martin Schwidefsky
2011-08-23 13:29 ` [patch 1/3] clockevent event minimum delay adjustments Martin Schwidefsky
2011-08-23 13:29 ` [patch 2/3] direct ktime path for clockevent devices Martin Schwidefsky
2011-08-23 13:29 ` [patch 3/3] use direct ktime path for s390 clockevent device Martin Schwidefsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox