Commit ab86e974 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull core timer updates from Ingo Molnar:
 "The main changes in this cycle's merge are:

   - Implement shadow timekeeper to shorten in kernel reader side
     blocking, by Thomas Gleixner.

   - Posix timers enhancements by Pavel Emelyanov:

   - allocate timer ID per process, so that exact timer ID allocations
     can be re-created be checkpoint/restore code.

   - debuggability and tooling (/proc/PID/timers, etc.) improvements.

   - suspend/resume enhancements by Feng Tang: on certain new Intel Atom
     processors (Penwell and Cloverview), there is a feature that the
     TSC won't stop in S3 state, so the TSC value won't be reset to 0
     after resume.  This can be taken advantage of by the generic via
     the CLOCK_SOURCE_SUSPEND_NONSTOP flag: instead of using the RTC to
     recover/approximate sleep time, the main (and precise) clocksource
     can be used.

   - Fix /proc/timer_list for 4096 CPUs by Nathan Zimmer: on so many
     CPUs the file goes beyond 4MB of size and thus the current
     simplistic seqfile approach fails.  Convert /proc/timer_list to a
     proper seq_file with its own iterator.

   - Cleanups and refactorings of the core timekeeping code by John
     Stultz.

   - International Atomic Clock time is managed by the NTP code
     internally currently but not exposed externally.  Separate the TAI
     code out and add CLOCK_TAI support and TAI support to the hrtimer
     and posix-timer code, by John Stultz.

   - Add deep idle support enhacement to the broadcast clockevents core
     timer code, by Daniel Lezcano: add an opt-in CLOCK_EVT_FEAT_DYNIRQ
     clockevents feature (which will be utilized by future clockevents
     driver updates), which allows the use of IRQ affinities to avoid
     spurious wakeups of idle CPUs - the right CPU with an expiring
     timer will be woken.

   - Add new ARM bcm281xx clocksource driver, by Christian Daudt

   - ... various other fixes and cleanups"

* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (52 commits)
  clockevents: Set dummy handler on CPU_DEAD shutdown
  timekeeping: Update tk->cycle_last in resume
  posix-timers: Remove unused variable
  clockevents: Switch into oneshot mode even if broadcast registered late
  timer_list: Convert timer list to be a proper seq_file
  timer_list: Split timer_list_show_tickdevices
  posix-timers: Show sigevent info in proc file
  posix-timers: Introduce /proc/PID/timers file
  posix timers: Allocate timer id per process (v2)
  timekeeping: Make sure to notify hrtimers when TAI offset changes
  hrtimer: Fix ktime_add_ns() overflow on 32bit architectures
  hrtimer: Add expiry time overflow check in hrtimer_interrupt
  timekeeping: Shorten seq_count region
  timekeeping: Implement a shadow timekeeper
  timekeeping: Delay update of clock->cycle_last
  timekeeping: Store cycle_last value in timekeeper struct as well
  ntp: Remove ntp_lock, using the timekeeping locks to protect ntp state
  timekeeping: Simplify tai updating from do_adjtimex
  timekeeping: Hold timekeepering locks in do_adjtimex and hardpps
  timekeeping: Move ADJ_SETOFFSET to top level do_adjtimex()
  ...
parents 8700c95a 6f7a05d7
...@@ -6,6 +6,7 @@ config ARCH_BCM ...@@ -6,6 +6,7 @@ config ARCH_BCM
select ARM_ERRATA_764369 if SMP select ARM_ERRATA_764369 if SMP
select ARM_GIC select ARM_GIC
select CPU_V7 select CPU_V7
select CLKSRC_OF
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_TIME select GENERIC_TIME
select GPIO_BCM select GPIO_BCM
......
...@@ -16,14 +16,11 @@ ...@@ -16,14 +16,11 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/irqchip.h> #include <linux/irqchip.h>
#include <linux/clocksource.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
static void timer_init(void)
{
}
static void __init board_init(void) static void __init board_init(void)
{ {
...@@ -35,7 +32,7 @@ static const char * const bcm11351_dt_compat[] = { "bcm,bcm11351", NULL, }; ...@@ -35,7 +32,7 @@ static const char * const bcm11351_dt_compat[] = { "bcm,bcm11351", NULL, };
DT_MACHINE_START(BCM11351_DT, "Broadcom Application Processor") DT_MACHINE_START(BCM11351_DT, "Broadcom Application Processor")
.init_irq = irqchip_init, .init_irq = irqchip_init,
.init_time = timer_init, .init_time = clocksource_of_init,
.init_machine = board_init, .init_machine = board_init,
.dt_compat = bcm11351_dt_compat, .dt_compat = bcm11351_dt_compat,
MACHINE_END MACHINE_END
...@@ -120,6 +120,7 @@ config X86 ...@@ -120,6 +120,7 @@ config X86
select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION
select OLD_SIGACTION if X86_32 select OLD_SIGACTION if X86_32
select COMPAT_OLD_SIGACTION if IA32_EMULATION select COMPAT_OLD_SIGACTION if IA32_EMULATION
select RTC_LIB
config INSTRUCTION_DECODER config INSTRUCTION_DECODER
def_bool y def_bool y
......
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
#define X86_FEATURE_AMD_DCM (3*32+27) /* multi-node processor */ #define X86_FEATURE_AMD_DCM (3*32+27) /* multi-node processor */
#define X86_FEATURE_APERFMPERF (3*32+28) /* APERFMPERF */ #define X86_FEATURE_APERFMPERF (3*32+28) /* APERFMPERF */
#define X86_FEATURE_EAGER_FPU (3*32+29) /* "eagerfpu" Non lazy FPU restore */ #define X86_FEATURE_EAGER_FPU (3*32+29) /* "eagerfpu" Non lazy FPU restore */
#define X86_FEATURE_NONSTOP_TSC_S3 (3*32+30) /* TSC doesn't stop in S3 state */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */ #define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
......
...@@ -96,6 +96,18 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c) ...@@ -96,6 +96,18 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
sched_clock_stable = 1; sched_clock_stable = 1;
} }
/* Penwell and Cloverview have the TSC which doesn't sleep on S3 */
if (c->x86 == 6) {
switch (c->x86_model) {
case 0x27: /* Penwell */
case 0x35: /* Cloverview */
set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC_S3);
break;
default:
break;
}
}
/* /*
* There is a known erratum on Pentium III and Core Solo * There is a known erratum on Pentium III and Core Solo
* and Core Duo CPUs. * and Core Duo CPUs.
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <asm/x86_init.h> #include <asm/x86_init.h>
#include <asm/time.h> #include <asm/time.h>
#include <asm/mrst.h> #include <asm/mrst.h>
#include <asm/rtc.h>
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
/* /*
...@@ -36,70 +37,24 @@ EXPORT_SYMBOL(rtc_lock); ...@@ -36,70 +37,24 @@ EXPORT_SYMBOL(rtc_lock);
* nowtime is written into the registers of the CMOS clock, it will * nowtime is written into the registers of the CMOS clock, it will
* jump to the next second precisely 500 ms later. Check the Motorola * jump to the next second precisely 500 ms later. Check the Motorola
* MC146818A or Dallas DS12887 data sheet for details. * MC146818A or Dallas DS12887 data sheet for details.
*
* BUG: This routine does not handle hour overflow properly; it just
* sets the minutes. Usually you'll only notice that after reboot!
*/ */
int mach_set_rtc_mmss(unsigned long nowtime) int mach_set_rtc_mmss(unsigned long nowtime)
{ {
int real_seconds, real_minutes, cmos_minutes; struct rtc_time tm;
unsigned char save_control, save_freq_select;
unsigned long flags;
int retval = 0; int retval = 0;
spin_lock_irqsave(&rtc_lock, flags); rtc_time_to_tm(nowtime, &tm);
if (!rtc_valid_tm(&tm)) {
/* tell the clock it's being set */ retval = set_rtc_time(&tm);
save_control = CMOS_READ(RTC_CONTROL); if (retval)
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); printk(KERN_ERR "%s: RTC write failed with error %d\n",
__FUNCTION__, retval);
/* stop and reset prescaler */
save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
cmos_minutes = CMOS_READ(RTC_MINUTES);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
cmos_minutes = bcd2bin(cmos_minutes);
/*
* since we're only adjusting minutes and seconds,
* don't interfere with hour overflow. This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60;
real_minutes = nowtime / 60;
/* correct for half hour time zone */
if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
real_minutes += 30;
real_minutes %= 60;
if (abs(real_minutes - cmos_minutes) < 30) {
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
real_seconds = bin2bcd(real_seconds);
real_minutes = bin2bcd(real_minutes);
}
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else { } else {
printk_once(KERN_NOTICE printk(KERN_ERR
"set_rtc_mmss: can't update from %d to %d\n", "%s: Invalid RTC value: write of %lx to RTC failed\n",
cmos_minutes, real_minutes); __FUNCTION__, nowtime);
retval = -1; retval = -EINVAL;
} }
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 (popular MC146818A clone with integrated
* battery and quartz) will not reset the oscillator and will not
* update precisely 500 ms later. You won't find this mentioned in
* the Dallas Semiconductor data sheets, but who believes data
* sheets anyway ... -- Markus Kuhn
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);
return retval; return retval;
} }
......
...@@ -768,7 +768,8 @@ static cycle_t read_tsc(struct clocksource *cs) ...@@ -768,7 +768,8 @@ static cycle_t read_tsc(struct clocksource *cs)
static void resume_tsc(struct clocksource *cs) static void resume_tsc(struct clocksource *cs)
{ {
clocksource_tsc.cycle_last = 0; if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
clocksource_tsc.cycle_last = 0;
} }
static struct clocksource clocksource_tsc = { static struct clocksource clocksource_tsc = {
...@@ -939,6 +940,9 @@ static int __init init_tsc_clocksource(void) ...@@ -939,6 +940,9 @@ static int __init init_tsc_clocksource(void)
clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS;
} }
if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
clocksource_tsc.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
/* /*
* Trust the results of the earlier calibration on systems * Trust the results of the earlier calibration on systems
* exporting a reliable TSC. * exporting a reliable TSC.
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/x86_init.h> #include <asm/x86_init.h>
#include <asm/rtc.h>
#define EFI_DEBUG 1 #define EFI_DEBUG 1
...@@ -352,10 +353,10 @@ static efi_status_t __init phys_efi_get_time(efi_time_t *tm, ...@@ -352,10 +353,10 @@ static efi_status_t __init phys_efi_get_time(efi_time_t *tm,
int efi_set_rtc_mmss(unsigned long nowtime) int efi_set_rtc_mmss(unsigned long nowtime)
{ {
int real_seconds, real_minutes;
efi_status_t status; efi_status_t status;
efi_time_t eft; efi_time_t eft;
efi_time_cap_t cap; efi_time_cap_t cap;
struct rtc_time tm;
status = efi.get_time(&eft, &cap); status = efi.get_time(&eft, &cap);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
...@@ -363,13 +364,20 @@ int efi_set_rtc_mmss(unsigned long nowtime) ...@@ -363,13 +364,20 @@ int efi_set_rtc_mmss(unsigned long nowtime)
return -1; return -1;
} }
real_seconds = nowtime % 60; rtc_time_to_tm(nowtime, &tm);
real_minutes = nowtime / 60; if (!rtc_valid_tm(&tm)) {
if (((abs(real_minutes - eft.minute) + 15)/30) & 1) eft.year = tm.tm_year + 1900;
real_minutes += 30; eft.month = tm.tm_mon + 1;
real_minutes %= 60; eft.day = tm.tm_mday;
eft.minute = real_minutes; eft.minute = tm.tm_min;
eft.second = real_seconds; eft.second = tm.tm_sec;
eft.nanosecond = 0;
} else {
printk(KERN_ERR
"%s: Invalid EFI RTC value: write of %lx to EFI RTC failed\n",
__FUNCTION__, nowtime);
return -1;
}
status = efi.set_time(&eft); status = efi.set_time(&eft);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
......
...@@ -85,27 +85,35 @@ unsigned long vrtc_get_time(void) ...@@ -85,27 +85,35 @@ unsigned long vrtc_get_time(void)
return mktime(year, mon, mday, hour, min, sec); return mktime(year, mon, mday, hour, min, sec);
} }
/* Only care about the minutes and seconds */
int vrtc_set_mmss(unsigned long nowtime) int vrtc_set_mmss(unsigned long nowtime)
{ {
int real_sec, real_min;
unsigned long flags; unsigned long flags;
int vrtc_min; struct rtc_time tm;
int year;
spin_lock_irqsave(&rtc_lock, flags); int retval = 0;
vrtc_min = vrtc_cmos_read(RTC_MINUTES);
rtc_time_to_tm(nowtime, &tm);
real_sec = nowtime % 60; if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) {
real_min = nowtime / 60; /*
if (((abs(real_min - vrtc_min) + 15)/30) & 1) * tm.year is the number of years since 1900, and the
real_min += 30; * vrtc need the years since 1972.
real_min %= 60; */
year = tm.tm_year - 72;
vrtc_cmos_write(real_sec, RTC_SECONDS); spin_lock_irqsave(&rtc_lock, flags);
vrtc_cmos_write(real_min, RTC_MINUTES); vrtc_cmos_write(year, RTC_YEAR);
spin_unlock_irqrestore(&rtc_lock, flags); vrtc_cmos_write(tm.tm_mon, RTC_MONTH);
vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH);
return 0; vrtc_cmos_write(tm.tm_hour, RTC_HOURS);
vrtc_cmos_write(tm.tm_min, RTC_MINUTES);
vrtc_cmos_write(tm.tm_sec, RTC_SECONDS);
spin_unlock_irqrestore(&rtc_lock, flags);
} else {
printk(KERN_ERR
"%s: Invalid vRTC value: write of %lx to vRTC failed\n",
__FUNCTION__, nowtime);
retval = -EINVAL;
}
return retval;
} }
void __init mrst_rtc_init(void) void __init mrst_rtc_init(void)
......
...@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o ...@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
/*
* Copyright (C) 2012 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/types.h>
#include <linux/io.h>
#include <asm/mach/time.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define KONA_GPTIMER_STCS_OFFSET 0x00000000
#define KONA_GPTIMER_STCLO_OFFSET 0x00000004
#define KONA_GPTIMER_STCHI_OFFSET 0x00000008
#define KONA_GPTIMER_STCM0_OFFSET 0x0000000C
#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0
#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4
struct kona_bcm_timers {
int tmr_irq;
void __iomem *tmr_regs;
};
static struct kona_bcm_timers timers;
static u32 arch_timer_rate;
/*
* We use the peripheral timers for system tick, the cpu global timer for
* profile tick
*/
static void kona_timer_disable_and_clear(void __iomem *base)
{
uint32_t reg;
/*
* clear and disable interrupts
* We are using compare/match register 0 for our system interrupts
*/
reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
/* Clear compare (0) interrupt */
reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
/* disable compare */
reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
}
static void
kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw)
{
void __iomem *base = IOMEM(timer_base);
int loop_limit = 4;
/*
* Read 64-bit free running counter
* 1. Read hi-word
* 2. Read low-word
* 3. Read hi-word again
* 4.1
* if new hi-word is not equal to previously read hi-word, then
* start from #1
* 4.2
* if new hi-word is equal to previously read hi-word then stop.
*/
while (--loop_limit) {
*msw = readl(base + KONA_GPTIMER_STCHI_OFFSET);
*lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET);
if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET))
break;
}
if (!loop_limit) {
pr_err("bcm_kona_timer: getting counter failed.\n");
pr_err(" Timer will be impacted\n");
}
return;
}
static const struct of_device_id bcm_timer_ids[] __initconst = {
{.compatible = "bcm,kona-timer"},
{},
};
static void __init kona_timers_init(void)
{
struct device_node *node;
u32 freq;
node = of_find_matching_node(NULL, bcm_timer_ids);
if (!node)
panic("No timer");
if (!of_property_read_u32(node, "clock-frequency", &freq))
arch_timer_rate = freq;
else
panic("clock-frequency not set in the .dts file");
/* Setup IRQ numbers */
timers.tmr_irq = irq_of_parse_and_map(node, 0);
/* Setup IO addresses */
timers.tmr_regs = of_iomap(node, 0);
kona_timer_disable_and_clear(timers.tmr_regs);
}
static int kona_timer_set_next_event(unsigned long clc,
struct clock_event_device *unused)
{
/*
* timer (0) is disabled by the timer interrupt already
* so, here we reload the next event value and re-enable
* the timer.
*
* This way, we are potentially losing the time between
* timer-interrupt->set_next_event. CPU local timers, when
* they come in should get rid of skew.
*/
uint32_t lsw, msw;
uint32_t reg;
kona_timer_get_counter(timers.tmr_regs, &msw, &lsw);
/* Load the "next" event tick value */
writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
/* Enable compare */
reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
return 0;
}
static void kona_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *unused)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
/* by default mode is one shot don't do any thing */
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
default:
kona_timer_disable_and_clear(timers.tmr_regs);
}
}
static struct clock_event_device kona_clockevent_timer = {
.name = "timer 1",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = kona_timer_set_next_event,
.set_mode = kona_timer_set_mode
};
static void __init kona_timer_clockevents_init(void)
{
kona_clockevent_timer.cpumask = cpumask_of(0);
clockevents_config_and_register(&kona_clockevent_timer,
arch_timer_rate, 6, 0xffffffff);
}
static irqreturn_t kona_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &kona_clockevent_timer;
kona_timer_disable_and_clear(timers.tmr_regs);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction kona_timer_irq = {
.name = "Kona Timer Tick",
.flags = IRQF_TIMER,
.handler = kona_timer_interrupt,
};
static void __init kona_timer_init(void)
{
kona_timers_init();
kona_timer_clockevents_init();
setup_irq(timers.tmr_irq, &kona_timer_irq);
kona_timer_set_next_event((arch_timer_rate / HZ), NULL);
}
CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer",
kona_timer_init);
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
#include <linux/fs_struct.h> #include <linux/fs_struct.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/flex_array.h> #include <linux/flex_array.h>
#include <linux/posix-timers.h>
#ifdef CONFIG_HARDWALL #ifdef CONFIG_HARDWALL
#include <asm/hardwall.h> #include <asm/hardwall.h>
#endif #endif
...@@ -2013,6 +2014,102 @@ static const struct file_operations proc_map_files_operations = { ...@@ -2013,6 +2014,102 @@ static const struct file_operations proc_map_files_operations = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
struct timers_private {
struct pid *pid;
struct task_struct *task;
struct sighand_struct *sighand;
struct pid_namespace *ns;
unsigned long flags;
};
static void *timers_start(struct seq_file *m, loff_t *pos)
{
struct timers_private *tp = m->private;
tp->task = get_pid_task(tp->pid, PIDTYPE_PID);
if (!tp->task)
return ERR_PTR(-ESRCH);
tp->sighand = lock_task_sighand(tp->task, &tp->flags);
if (!tp->sighand)
return ERR_PTR(-ESRCH);
return seq_list_start(&tp->task->signal->posix_timers, *pos);
}
static void *timers_next(struct seq_file *m, void *v, loff_t *pos)
{
struct timers_private *tp = m->private;
return seq_list_next(v, &tp->task->signal->posix_timers, pos);
}
static void timers_stop(struct seq_file *m, void *v)
{
struct timers_private *tp = m->private;
if (tp->sighand) {
unlock_task_sighand(tp->task, &tp->flags);
tp->sighand = NULL;
}
if (tp->task) {
put_task_struct(tp->task);
tp->task = NULL;
}
}
static int show_timer(struct seq_file *m, void *v)
{
struct k_itimer *timer;
struct timers_private *tp = m->private;
int notify;
static char *nstr[] = {
[SIGEV_SIGNAL] = "signal",
[SIGEV_NONE] = "none",
[SIGEV_THREAD] = "thread",
};
timer = list_entry((struct list_head *)v, struct k_itimer, list);
notify = timer->it_sigev_notify;
seq_printf(m, "ID: %d\n", timer->it_id);
seq_printf(m, "signal: %d/%p\n", timer->sigq->info.si_signo,
timer->sigq->info.si_value.sival_ptr);
seq_printf(m, "notify: %s/%s.%d\n",
nstr[notify & ~SIGEV_THREAD_ID],
(notify & SIGEV_THREAD_ID) ? "tid" : "pid",
pid_nr_ns(timer->it_pid, tp->ns));
return 0;
}
static const struct seq_operations proc_timers_seq_ops = {
.start = timers_start,
.next = timers_next,
.stop = timers_stop,
.show = show_timer,
};
static int proc_timers_open(struct inode *inode, struct file *file)
{
struct timers_private *tp;
tp = __seq_open_private(file, &proc_timers_seq_ops,
sizeof(struct timers_private));
if (!tp)
return -ENOMEM;
tp->pid = proc_pid(inode);
tp->ns = inode->i_sb->s_fs_info;
return 0;
}
static const struct file_operations proc_timers_operations = {
.open = proc_timers_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#endif /* CONFIG_CHECKPOINT_RESTORE */ #endif /* CONFIG_CHECKPOINT_RESTORE */
static struct dentry *proc_pident_instantiate(struct inode *dir, static struct dentry *proc_pident_instantiate(struct inode *dir,
...@@ -2583,6 +2680,9 @@ static const struct pid_entry tgid_base_stuff[] = { ...@@ -2583,6 +2680,9 @@ static const struct pid_entry tgid_base_stuff[] = {
REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations),
REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
#endif #endif
#ifdef CONFIG_CHECKPOINT_RESTORE
REG("timers", S_IRUGO, proc_timers_operations),
#endif
}; };
static int proc_tgid_base_readdir(struct file * filp, static int proc_tgid_base_readdir(struct file * filp,
......
...@@ -55,6 +55,11 @@ enum clock_event_nofitiers { ...@@ -55,6 +55,11 @@ enum clock_event_nofitiers {
#define CLOCK_EVT_FEAT_C3STOP 0x000008 #define CLOCK_EVT_FEAT_C3STOP 0x000008
#define CLOCK_EVT_FEAT_DUMMY 0x000010 #define CLOCK_EVT_FEAT_DUMMY 0x000010
/*
* Core shall set the interrupt affinity dynamically in broadcast mode
*/
#define CLOCK_EVT_FEAT_DYNIRQ 0x000020
/** /**
* struct clock_event_device - clock event device descriptor * struct clock_event_device - clock event device descriptor
* @event_handler: Assigned by the framework to be called by the low * @event_handler: Assigned by the framework to be called by the low
...@@ -170,6 +175,12 @@ extern void tick_broadcast(const struct cpumask *mask); ...@@ -170,6 +175,12 @@ extern void tick_broadcast(const struct cpumask *mask);
extern int tick_receive_broadcast(void); extern int tick_receive_broadcast(void);
#endif #endif
#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_TICK_ONESHOT)
extern int tick_check_broadcast_expired(void);
#else
static inline int tick_check_broadcast_expired(void) { return 0; }
#endif
#ifdef CONFIG_GENERIC_CLOCKEVENTS #ifdef CONFIG_GENERIC_CLOCKEVENTS
extern void clockevents_notify(unsigned long reason, void *arg); extern void clockevents_notify(unsigned long reason, void *arg);
#else #else
...@@ -182,6 +193,7 @@ static inline void clockevents_suspend(void) {} ...@@ -182,6 +193,7 @@ static inline void clockevents_suspend(void) {}
static inline void clockevents_resume(void) {} static inline void clockevents_resume(void) {}
#define clockevents_notify(reason, arg) do { } while (0) #define clockevents_notify(reason, arg) do { } while (0)
static inline int tick_check_broadcast_expired(void) { return 0; }
#endif #endif
......
...@@ -206,6 +206,7 @@ struct clocksource { ...@@ -206,6 +206,7 @@ struct clocksource {
#define CLOCK_SOURCE_WATCHDOG 0x10 #define CLOCK_SOURCE_WATCHDOG 0x10
#define CLOCK_SOURCE_VALID_FOR_HRES 0x20 #define CLOCK_SOURCE_VALID_FOR_HRES 0x20
#define CLOCK_SOURCE_UNSTABLE 0x40 #define CLOCK_SOURCE_UNSTABLE 0x40
#define CLOCK_SOURCE_SUSPEND_NONSTOP 0x80
/* simplify initialization of mask field */ /* simplify initialization of mask field */
#define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1) #define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1)
......
...@@ -157,6 +157,7 @@ enum hrtimer_base_type { ...@@ -157,6 +157,7 @@ enum hrtimer_base_type {
HRTIMER_BASE_MONOTONIC, HRTIMER_BASE_MONOTONIC,
HRTIMER_BASE_REALTIME, HRTIMER_BASE_REALTIME,
HRTIMER_BASE_BOOTTIME, HRTIMER_BASE_BOOTTIME,
HRTIMER_BASE_TAI,
HRTIMER_MAX_CLOCK_BASES, HRTIMER_MAX_CLOCK_BASES,
}; };
...@@ -327,7 +328,9 @@ extern ktime_t ktime_get(void); ...@@ -327,7 +328,9 @@ extern ktime_t ktime_get(void);
extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_real(void);
extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_boottime(void);
extern ktime_t ktime_get_monotonic_offset(void); extern ktime_t ktime_get_monotonic_offset(void);
extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot); extern ktime_t ktime_get_clocktai(void);
extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot,
ktime_t *offs_tai);
DECLARE_PER_CPU(struct tick_device, tick_cpu_device); DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
......
...@@ -75,7 +75,6 @@ extern int register_refined_jiffies(long clock_tick_rate); ...@@ -75,7 +75,6 @@ extern int register_refined_jiffies(long clock_tick_rate);
*/ */
extern u64 __jiffy_data jiffies_64; extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies; extern unsigned long volatile __jiffy_data jiffies;
extern seqlock_t jiffies_lock;
#if (BITS_PER_LONG < 64) #if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void); u64 get_jiffies_64(void);
......
...@@ -55,6 +55,7 @@ struct cpu_timer_list { ...@@ -55,6 +55,7 @@ struct cpu_timer_list {
/* POSIX.1b interval timer structure. */ /* POSIX.1b interval timer structure. */
struct k_itimer { struct k_itimer {
struct list_head list; /* free/ allocate list */ struct list_head list; /* free/ allocate list */
struct hlist_node t_hash;
spinlock_t it_lock; spinlock_t it_lock;
clockid_t it_clock; /* which timer type */ clockid_t it_clock; /* which timer type */
timer_t it_id; /* timer id */ timer_t it_id; /* timer id */
......
...@@ -514,7 +514,8 @@ struct signal_struct { ...@@ -514,7 +514,8 @@ struct signal_struct {
unsigned int has_child_subreaper:1; unsigned int has_child_subreaper:1;
/* POSIX.1b Interval Timers */ /* POSIX.1b Interval Timers */
struct list_head posix_timers; int posix_timer_id;
struct list_head posix_timers;
/* ITIMER_REAL timer for the process */ /* ITIMER_REAL timer for the process */
struct hrtimer real_timer; struct hrtimer real_timer;
......
...@@ -181,6 +181,9 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran); ...@@ -181,6 +181,9 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
extern int timekeeping_valid_for_hres(void); extern int timekeeping_valid_for_hres(void);
extern u64 timekeeping_max_deferment(void); extern u64 timekeeping_max_deferment(void);
extern int timekeeping_inject_offset(struct timespec *ts); extern int timekeeping_inject_offset(struct timespec *ts);
extern s32 timekeeping_get_tai_offset(void);
extern void timekeeping_set_tai_offset(s32 tai_offset);
extern void timekeeping_clocktai(struct timespec *ts);
struct tms; struct tms;
extern void do_sys_times(struct tms *); extern void do_sys_times(struct tms *);
......
...@@ -20,6 +20,8 @@ struct timekeeper { ...@@ -20,6 +20,8 @@ struct timekeeper {
u32 shift; u32 shift;
/* Number of clock cycles in one NTP interval. */ /* Number of clock cycles in one NTP interval. */
cycle_t cycle_interval; cycle_t cycle_interval;
/* Last cycle value (also stored in clock->cycle_last) */
cycle_t cycle_last;
/* Number of clock shifted nano seconds in one NTP interval. */ /* Number of clock shifted nano seconds in one NTP interval. */
u64 xtime_interval; u64 xtime_interval;
/* shifted nano seconds left over when rounding cycle_interval */ /* shifted nano seconds left over when rounding cycle_interval */
...@@ -62,8 +64,11 @@ struct timekeeper { ...@@ -62,8 +64,11 @@ struct timekeeper {
ktime_t offs_boot; ktime_t offs_boot;
/* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
struct timespec raw_time; struct timespec raw_time;
/* Seqlock for all timekeeper values */ /* The current UTC to TAI offset in seconds */
seqlock_t lock; s32 tai_offset;
/* Offset clock monotonic -> clock tai */
ktime_t offs_tai;
}; };
static inline struct timespec tk_xtime(struct timekeeper *tk) static inline struct timespec tk_xtime(struct timekeeper *tk)
......
...@@ -125,9 +125,6 @@ ...@@ -125,9 +125,6 @@
extern unsigned long tick_usec; /* USER_HZ period (usec) */ extern unsigned long tick_usec; /* USER_HZ period (usec) */
extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */ extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */
extern void ntp_init(void);
extern void ntp_clear(void);
/* Required to safely shift negative values */ /* Required to safely shift negative values */
#define shift_right(x, s) ({ \ #define shift_right(x, s) ({ \
__typeof__(x) __x = (x); \ __typeof__(x) __x = (x); \
...@@ -140,10 +137,6 @@ extern void ntp_clear(void); ...@@ -140,10 +137,6 @@ extern void ntp_clear(void);
#define NTP_INTERVAL_FREQ (HZ) #define NTP_INTERVAL_FREQ (HZ)
#define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ) #define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
extern u64 ntp_tick_length(void);
extern int second_overflow(unsigned long secs);
extern int do_adjtimex(struct timex *); extern int do_adjtimex(struct timex *);
extern void hardpps(const struct timespec *, const struct timespec *); extern void hardpps(const struct timespec *, const struct timespec *);
......
...@@ -54,11 +54,9 @@ struct itimerval { ...@@ -54,11 +54,9 @@ struct itimerval {
#define CLOCK_BOOTTIME 7 #define CLOCK_BOOTTIME 7
#define CLOCK_REALTIME_ALARM 8 #define CLOCK_REALTIME_ALARM 8
#define CLOCK_BOOTTIME_ALARM 9 #define CLOCK_BOOTTIME_ALARM 9
#define CLOCK_SGI_CYCLE 10 /* Hardware specific */
#define CLOCK_TAI 11
/*
* The IDs of various hardware clocks:
*/
#define CLOCK_SGI_CYCLE 10
#define MAX_CLOCKS 16 #define MAX_CLOCKS 16
#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO CLOCK_MONOTONIC #define CLOCKS_MONO CLOCK_MONOTONIC
......
...@@ -495,7 +495,6 @@ asmlinkage void __init start_kernel(void) ...@@ -495,7 +495,6 @@ asmlinkage void __init start_kernel(void)
* Interrupts are still disabled. Do necessary setups, then * Interrupts are still disabled. Do necessary setups, then
* enable them * enable them
*/ */
tick_init();
boot_cpu_init(); boot_cpu_init();
page_address_init(); page_address_init();
pr_notice("%s", linux_banner); pr_notice("%s", linux_banner);
...@@ -549,6 +548,7 @@ asmlinkage void __init start_kernel(void) ...@@ -549,6 +548,7 @@ asmlinkage void __init start_kernel(void)
/* init some links before init_ISA_irqs() */ /* init some links before init_ISA_irqs() */
early_irq_init(); early_irq_init();
init_IRQ(); init_IRQ();
tick_init();
init_timers(); init_timers();
hrtimers_init(); hrtimers_init();
softirq_init(); softirq_init();
......
...@@ -76,7 +76,16 @@ static void cpu_idle_loop(void) ...@@ -76,7 +76,16 @@ static void cpu_idle_loop(void)
local_irq_disable(); local_irq_disable();
arch_cpu_idle_enter(); arch_cpu_idle_enter();
if (cpu_idle_force_poll) { /*
* In poll mode we reenable interrupts and spin.
*
* Also if we detected in the wakeup from idle
* path that the tick broadcast device expired
* for us, we don't want to go deep idle as we
* know that the IPI is going to arrive right
* away
*/
if (cpu_idle_force_poll || tick_check_broadcast_expired()) {
cpu_idle_poll(); cpu_idle_poll();
} else { } else {
current_clr_polling(); current_clr_polling();
......
...@@ -84,6 +84,12 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = ...@@ -84,6 +84,12 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
.get_time = &ktime_get_boottime, .get_time = &ktime_get_boottime,
.resolution = KTIME_LOW_RES, .resolution = KTIME_LOW_RES,
}, },
{
.index = HRTIMER_BASE_TAI,
.clockid = CLOCK_TAI,
.get_time = &ktime_get_clocktai,
.resolution = KTIME_LOW_RES,
},
} }
}; };
...@@ -91,6 +97,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = { ...@@ -91,6 +97,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME, [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC, [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME, [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
[CLOCK_TAI] = HRTIMER_BASE_TAI,
}; };
static inline int hrtimer_clockid_to_base(clockid_t clock_id) static inline int hrtimer_clockid_to_base(clockid_t clock_id)
...@@ -107,8 +114,10 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) ...@@ -107,8 +114,10 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
{ {
ktime_t xtim, mono, boot; ktime_t xtim, mono, boot;
struct timespec xts, tom, slp; struct timespec xts, tom, slp;
s32 tai_offset;
get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp);
tai_offset = timekeeping_get_tai_offset();
xtim = timespec_to_ktime(xts); xtim = timespec_to_ktime(xts);
mono = ktime_add(xtim, timespec_to_ktime(tom)); mono = ktime_add(xtim, timespec_to_ktime(tom));
...@@ -116,6 +125,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) ...@@ -116,6 +125,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
base->clock_base[HRTIMER_BASE_TAI].softirq_time =
ktime_add(xtim, ktime_set(tai_offset, 0));
} }
/* /*
...@@ -276,6 +287,10 @@ ktime_t ktime_add_ns(const ktime_t kt, u64 nsec) ...@@ -276,6 +287,10 @@ ktime_t ktime_add_ns(const ktime_t kt, u64 nsec)
} else { } else {
unsigned long rem = do_div(nsec, NSEC_PER_SEC); unsigned long rem = do_div(nsec, NSEC_PER_SEC);
/* Make sure nsec fits into long */
if (unlikely(nsec > KTIME_SEC_MAX))
return (ktime_t){ .tv64 = KTIME_MAX };
tmp = ktime_set((long)nsec, rem); tmp = ktime_set((long)nsec, rem);
} }
...@@ -652,8 +667,9 @@ static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) ...@@ -652,8 +667,9 @@ static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
{ {
ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset; ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset;
ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset; ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset;
ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset;
return ktime_get_update_offsets(offs_real, offs_boot); return ktime_get_update_offsets(offs_real, offs_boot, offs_tai);
} }
/* /*
...@@ -1011,7 +1027,8 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, ...@@ -1011,7 +1027,8 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
* @timer: the timer to be added * @timer: the timer to be added
* @tim: expiry time * @tim: expiry time
* @delta_ns: "slack" range for the timer * @delta_ns: "slack" range for the timer
* @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) * @mode: expiry mode: absolute (HRTIMER_MODE_ABS) or
* relative (HRTIMER_MODE_REL)
* *
* Returns: * Returns:
* 0 on success * 0 on success
...@@ -1028,7 +1045,8 @@ EXPORT_SYMBOL_GPL(hrtimer_start_range_ns); ...@@ -1028,7 +1045,8 @@ EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
* hrtimer_start - (re)start an hrtimer on the current CPU * hrtimer_start - (re)start an hrtimer on the current CPU
* @timer: the timer to be added * @timer: the timer to be added
* @tim: expiry time * @tim: expiry time
* @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) * @mode: expiry mode: absolute (HRTIMER_MODE_ABS) or
* relative (HRTIMER_MODE_REL)
* *
* Returns: * Returns:
* 0 on success * 0 on success
...@@ -1310,6 +1328,8 @@ void hrtimer_interrupt(struct clock_event_device *dev) ...@@ -1310,6 +1328,8 @@ void hrtimer_interrupt(struct clock_event_device *dev)
expires = ktime_sub(hrtimer_get_expires(timer), expires = ktime_sub(hrtimer_get_expires(timer),
base->offset); base->offset);
if (expires.tv64 < 0)
expires.tv64 = KTIME_MAX;
if (expires.tv64 < expires_next.tv64) if (expires.tv64 < expires_next.tv64)
expires_next = expires; expires_next = expires;
break; break;
......
...@@ -40,38 +40,31 @@ ...@@ -40,38 +40,31 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/idr.h> #include <linux/hash.h>
#include <linux/posix-clock.h> #include <linux/posix-clock.h>
#include <linux/posix-timers.h> #include <linux/posix-timers.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/hashtable.h>
/* /*
* Management arrays for POSIX timers. Timers are kept in slab memory * Management arrays for POSIX timers. Timers are now kept in static hash table
* Timer ids are allocated by an external routine that keeps track of the * with 512 entries.
* id and the timer. The external interface is: * Timer ids are allocated by local routine, which selects proper hash head by
* * key, constructed from current->signal address and per signal struct counter.
* void *idr_find(struct idr *idp, int id); to find timer_id <id> * This keeps timer ids unique per process, but now they can intersect between
* int idr_get_new(struct idr *idp, void *ptr); to get a new id and * processes.
* related it to <ptr>
* void idr_remove(struct idr *idp, int id); to release <id>
* void idr_init(struct idr *idp); to initialize <idp>
* which we supply.
* The idr_get_new *may* call slab for more memory so it must not be
* called under a spin lock. Likewise idr_remore may release memory
* (but it may be ok to do this under a lock...).
* idr_find is just a memory look up and is quite fast. A -1 return
* indicates that the requested id does not exist.
*/ */
/* /*
* Lets keep our timers in a slab cache :-) * Lets keep our timers in a slab cache :-)
*/ */
static struct kmem_cache *posix_timers_cache; static struct kmem_cache *posix_timers_cache;
static struct idr posix_timers_id;
static DEFINE_SPINLOCK(idr_lock); static DEFINE_HASHTABLE(posix_timers_hashtable, 9);
static DEFINE_SPINLOCK(hash_lock);
/* /*
* we assume that the new SIGEV_THREAD_ID shares no bits with the other * we assume that the new SIGEV_THREAD_ID shares no bits with the other
...@@ -152,6 +145,56 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags); ...@@ -152,6 +145,56 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags);
__timr; \ __timr; \
}) })
static int hash(struct signal_struct *sig, unsigned int nr)
{
return hash_32(hash32_ptr(sig) ^ nr, HASH_BITS(posix_timers_hashtable));
}
static struct k_itimer *__posix_timers_find(struct hlist_head *head,
struct signal_struct *sig,
timer_t id)
{
struct k_itimer *timer;
hlist_for_each_entry_rcu(timer, head, t_hash) {
if ((timer->it_signal == sig) && (timer->it_id == id))
return timer;
}
return NULL;
}
static struct k_itimer *posix_timer_by_id(timer_t id)
{
struct signal_struct *sig = current->signal;
struct hlist_head *head = &posix_timers_hashtable[hash(sig, id)];
return __posix_timers_find(head, sig, id);
}
static int posix_timer_add(struct k_itimer *timer)
{
struct signal_struct *sig = current->signal;
int first_free_id = sig->posix_timer_id;
struct hlist_head *head;
int ret = -ENOENT;
do {
spin_lock(&hash_lock);
head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)];
if (!__posix_timers_find(head, sig, sig->posix_timer_id)) {
hlist_add_head_rcu(&timer->t_hash, head);
ret = sig->posix_timer_id;
}
if (++sig->posix_timer_id < 0)
sig->posix_timer_id = 0;
if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT))
/* Loop over all possible ids completed */
ret = -EAGAIN;
spin_unlock(&hash_lock);
} while (ret == -ENOENT);
return ret;
}
static inline void unlock_timer(struct k_itimer *timr, unsigned long flags) static inline void unlock_timer(struct k_itimer *timr, unsigned long flags)
{ {
spin_unlock_irqrestore(&timr->it_lock, flags); spin_unlock_irqrestore(&timr->it_lock, flags);
...@@ -221,6 +264,11 @@ static int posix_get_boottime(const clockid_t which_clock, struct timespec *tp) ...@@ -221,6 +264,11 @@ static int posix_get_boottime(const clockid_t which_clock, struct timespec *tp)
return 0; return 0;
} }
static int posix_get_tai(clockid_t which_clock, struct timespec *tp)
{
timekeeping_clocktai(tp);
return 0;
}
/* /*
* Initialize everything, well, just everything in Posix clocks/timers ;) * Initialize everything, well, just everything in Posix clocks/timers ;)
...@@ -261,6 +309,16 @@ static __init int init_posix_timers(void) ...@@ -261,6 +309,16 @@ static __init int init_posix_timers(void)
.clock_getres = posix_get_coarse_res, .clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse, .clock_get = posix_get_monotonic_coarse,
}; };
struct k_clock clock_tai = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_tai,
.nsleep = common_nsleep,
.nsleep_restart = hrtimer_nanosleep_restart,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
.timer_get = common_timer_get,
.timer_del = common_timer_del,
};
struct k_clock clock_boottime = { struct k_clock clock_boottime = {
.clock_getres = hrtimer_get_res, .clock_getres = hrtimer_get_res,
.clock_get = posix_get_boottime, .clock_get = posix_get_boottime,
...@@ -278,11 +336,11 @@ static __init int init_posix_timers(void) ...@@ -278,11 +336,11 @@ static __init int init_posix_timers(void)
posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse); posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse);
posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse); posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse);
posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime); posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime);
posix_timers_register_clock(CLOCK_TAI, &clock_tai);
posix_timers_cache = kmem_cache_create("posix_timers_cache", posix_timers_cache = kmem_cache_create("posix_timers_cache",
sizeof (struct k_itimer), 0, SLAB_PANIC, sizeof (struct k_itimer), 0, SLAB_PANIC,
NULL); NULL);
idr_init(&posix_timers_id);
return 0; return 0;
} }
...@@ -504,9 +562,9 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set) ...@@ -504,9 +562,9 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
{ {
if (it_id_set) { if (it_id_set) {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&idr_lock, flags); spin_lock_irqsave(&hash_lock, flags);
idr_remove(&posix_timers_id, tmr->it_id); hlist_del_rcu(&tmr->t_hash);
spin_unlock_irqrestore(&idr_lock, flags); spin_unlock_irqrestore(&hash_lock, flags);
} }
put_pid(tmr->it_pid); put_pid(tmr->it_pid);
sigqueue_free(tmr->sigq); sigqueue_free(tmr->sigq);
...@@ -552,22 +610,11 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock, ...@@ -552,22 +610,11 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
return -EAGAIN; return -EAGAIN;
spin_lock_init(&new_timer->it_lock); spin_lock_init(&new_timer->it_lock);
new_timer_id = posix_timer_add(new_timer);
idr_preload(GFP_KERNEL); if (new_timer_id < 0) {
spin_lock_irq(&idr_lock); error = new_timer_id;
error = idr_alloc(&posix_timers_id, new_timer, 0, 0, GFP_NOWAIT);
spin_unlock_irq(&idr_lock);
idr_preload_end();
if (error < 0) {
/*
* Weird looking, but we return EAGAIN if the IDR is
* full (proper POSIX return value for this)
*/
if (error == -ENOSPC)
error = -EAGAIN;
goto out; goto out;
} }
new_timer_id = error;
it_id_set = IT_ID_SET; it_id_set = IT_ID_SET;
new_timer->it_id = (timer_t) new_timer_id; new_timer->it_id = (timer_t) new_timer_id;
...@@ -645,7 +692,7 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags) ...@@ -645,7 +692,7 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags)
return NULL; return NULL;
rcu_read_lock(); rcu_read_lock();
timr = idr_find(&posix_timers_id, (int)timer_id); timr = posix_timer_by_id(timer_id);
if (timr) { if (timr) {
spin_lock_irqsave(&timr->it_lock, *flags); spin_lock_irqsave(&timr->it_lock, *flags);
if (timr->it_signal == current->signal) { if (timr->it_signal == current->signal) {
......
...@@ -138,13 +138,14 @@ int persistent_clock_is_local; ...@@ -138,13 +138,14 @@ int persistent_clock_is_local;
*/ */
static inline void warp_clock(void) static inline void warp_clock(void)
{ {
struct timespec adjust; if (sys_tz.tz_minuteswest != 0) {
struct timespec adjust;
adjust = current_kernel_time();
if (sys_tz.tz_minuteswest != 0)
persistent_clock_is_local = 1; persistent_clock_is_local = 1;
adjust.tv_sec += sys_tz.tz_minuteswest * 60; adjust.tv_sec = sys_tz.tz_minuteswest * 60;
do_settimeofday(&adjust); adjust.tv_nsec = 0;
timekeeping_inject_offset(&adjust);
}
} }
/* /*
......
...@@ -18,13 +18,14 @@ ...@@ -18,13 +18,14 @@
#include <linux/rtc.h> #include <linux/rtc.h>
#include "tick-internal.h" #include "tick-internal.h"
#include "ntp_internal.h"
/* /*
* NTP timekeeping variables: * NTP timekeeping variables:
*
* Note: All of the NTP state is protected by the timekeeping locks.
*/ */
DEFINE_RAW_SPINLOCK(ntp_lock);
/* USER_HZ period (usecs): */ /* USER_HZ period (usecs): */
unsigned long tick_usec = TICK_USEC; unsigned long tick_usec = TICK_USEC;
...@@ -53,9 +54,6 @@ static int time_state = TIME_OK; ...@@ -53,9 +54,6 @@ static int time_state = TIME_OK;
/* clock status bits: */ /* clock status bits: */
static int time_status = STA_UNSYNC; static int time_status = STA_UNSYNC;
/* TAI offset (secs): */
static long time_tai;
/* time adjustment (nsecs): */ /* time adjustment (nsecs): */
static s64 time_offset; static s64 time_offset;
...@@ -134,8 +132,6 @@ static inline void pps_reset_freq_interval(void) ...@@ -134,8 +132,6 @@ static inline void pps_reset_freq_interval(void)
/** /**
* pps_clear - Clears the PPS state variables * pps_clear - Clears the PPS state variables
*
* Must be called while holding a write on the ntp_lock
*/ */
static inline void pps_clear(void) static inline void pps_clear(void)
{ {
...@@ -150,8 +146,6 @@ static inline void pps_clear(void) ...@@ -150,8 +146,6 @@ static inline void pps_clear(void)
/* Decrease pps_valid to indicate that another second has passed since /* Decrease pps_valid to indicate that another second has passed since
* the last PPS signal. When it reaches 0, indicate that PPS signal is * the last PPS signal. When it reaches 0, indicate that PPS signal is
* missing. * missing.
*
* Must be called while holding a write on the ntp_lock
*/ */
static inline void pps_dec_valid(void) static inline void pps_dec_valid(void)
{ {
...@@ -346,10 +340,6 @@ static void ntp_update_offset(long offset) ...@@ -346,10 +340,6 @@ static void ntp_update_offset(long offset)
*/ */
void ntp_clear(void) void ntp_clear(void)
{ {
unsigned long flags;
raw_spin_lock_irqsave(&ntp_lock, flags);
time_adjust = 0; /* stop active adjtime() */ time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC; time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
...@@ -362,20 +352,12 @@ void ntp_clear(void) ...@@ -362,20 +352,12 @@ void ntp_clear(void)
/* Clear PPS state variables */ /* Clear PPS state variables */
pps_clear(); pps_clear();
raw_spin_unlock_irqrestore(&ntp_lock, flags);
} }
u64 ntp_tick_length(void) u64 ntp_tick_length(void)
{ {
unsigned long flags; return tick_length;
s64 ret;
raw_spin_lock_irqsave(&ntp_lock, flags);
ret = tick_length;
raw_spin_unlock_irqrestore(&ntp_lock, flags);
return ret;
} }
...@@ -393,9 +375,6 @@ int second_overflow(unsigned long secs) ...@@ -393,9 +375,6 @@ int second_overflow(unsigned long secs)
{ {
s64 delta; s64 delta;
int leap = 0; int leap = 0;
unsigned long flags;
raw_spin_lock_irqsave(&ntp_lock, flags);
/* /*
* Leap second processing. If in leap-insert state at the end of the * Leap second processing. If in leap-insert state at the end of the
...@@ -415,7 +394,6 @@ int second_overflow(unsigned long secs) ...@@ -415,7 +394,6 @@ int second_overflow(unsigned long secs)
else if (secs % 86400 == 0) { else if (secs % 86400 == 0) {
leap = -1; leap = -1;
time_state = TIME_OOP; time_state = TIME_OOP;
time_tai++;
printk(KERN_NOTICE printk(KERN_NOTICE
"Clock: inserting leap second 23:59:60 UTC\n"); "Clock: inserting leap second 23:59:60 UTC\n");
} }
...@@ -425,7 +403,6 @@ int second_overflow(unsigned long secs) ...@@ -425,7 +403,6 @@ int second_overflow(unsigned long secs)
time_state = TIME_OK; time_state = TIME_OK;
else if ((secs + 1) % 86400 == 0) { else if ((secs + 1) % 86400 == 0) {
leap = 1; leap = 1;
time_tai--;
time_state = TIME_WAIT; time_state = TIME_WAIT;
printk(KERN_NOTICE printk(KERN_NOTICE
"Clock: deleting leap second 23:59:59 UTC\n"); "Clock: deleting leap second 23:59:59 UTC\n");
...@@ -479,8 +456,6 @@ int second_overflow(unsigned long secs) ...@@ -479,8 +456,6 @@ int second_overflow(unsigned long secs)
time_adjust = 0; time_adjust = 0;
out: out:
raw_spin_unlock_irqrestore(&ntp_lock, flags);
return leap; return leap;
} }
...@@ -575,11 +550,10 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts) ...@@ -575,11 +550,10 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)
time_status |= txc->status & ~STA_RONLY; time_status |= txc->status & ~STA_RONLY;
} }
/*
* Called with ntp_lock held, so we can access and modify static inline void process_adjtimex_modes(struct timex *txc,
* all the global NTP state: struct timespec *ts,
*/ s32 *time_tai)
static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts)
{ {
if (txc->modes & ADJ_STATUS) if (txc->modes & ADJ_STATUS)
process_adj_status(txc, ts); process_adj_status(txc, ts);
...@@ -613,7 +587,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts ...@@ -613,7 +587,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
} }
if (txc->modes & ADJ_TAI && txc->constant > 0) if (txc->modes & ADJ_TAI && txc->constant > 0)
time_tai = txc->constant; *time_tai = txc->constant;
if (txc->modes & ADJ_OFFSET) if (txc->modes & ADJ_OFFSET)
ntp_update_offset(txc->offset); ntp_update_offset(txc->offset);
...@@ -625,16 +599,13 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts ...@@ -625,16 +599,13 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
ntp_update_frequency(); ntp_update_frequency();
} }
/*
* adjtimex mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd. /**
* ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
*/ */
int do_adjtimex(struct timex *txc) int ntp_validate_timex(struct timex *txc)
{ {
struct timespec ts;
int result;
/* Validate the data before disabling interrupts */
if (txc->modes & ADJ_ADJTIME) { if (txc->modes & ADJ_ADJTIME) {
/* singleshot must not be used with any other mode bits */ /* singleshot must not be used with any other mode bits */
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT)) if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
...@@ -646,7 +617,6 @@ int do_adjtimex(struct timex *txc) ...@@ -646,7 +617,6 @@ int do_adjtimex(struct timex *txc)
/* In order to modify anything, you gotta be super-user! */ /* In order to modify anything, you gotta be super-user! */
if (txc->modes && !capable(CAP_SYS_TIME)) if (txc->modes && !capable(CAP_SYS_TIME))
return -EPERM; return -EPERM;
/* /*
* if the quartz is off by more than 10% then * if the quartz is off by more than 10% then
* something is VERY wrong! * something is VERY wrong!
...@@ -657,22 +627,20 @@ int do_adjtimex(struct timex *txc) ...@@ -657,22 +627,20 @@ int do_adjtimex(struct timex *txc)
return -EINVAL; return -EINVAL;
} }
if (txc->modes & ADJ_SETOFFSET) { if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME)))
struct timespec delta; return -EPERM;
delta.tv_sec = txc->time.tv_sec;
delta.tv_nsec = txc->time.tv_usec;
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (!(txc->modes & ADJ_NANO))
delta.tv_nsec *= 1000;
result = timekeeping_inject_offset(&delta);
if (result)
return result;
}
getnstimeofday(&ts); return 0;
}
raw_spin_lock_irq(&ntp_lock);
/*
* adjtimex mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd.
*/
int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
{
int result;
if (txc->modes & ADJ_ADJTIME) { if (txc->modes & ADJ_ADJTIME) {
long save_adjust = time_adjust; long save_adjust = time_adjust;
...@@ -687,7 +655,7 @@ int do_adjtimex(struct timex *txc) ...@@ -687,7 +655,7 @@ int do_adjtimex(struct timex *txc)
/* If there are input parameters, then process them: */ /* If there are input parameters, then process them: */
if (txc->modes) if (txc->modes)
process_adjtimex_modes(txc, &ts); process_adjtimex_modes(txc, ts, time_tai);
txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,
NTP_SCALE_SHIFT); NTP_SCALE_SHIFT);
...@@ -709,15 +677,13 @@ int do_adjtimex(struct timex *txc) ...@@ -709,15 +677,13 @@ int do_adjtimex(struct timex *txc)
txc->precision = 1; txc->precision = 1;
txc->tolerance = MAXFREQ_SCALED / PPM_SCALE; txc->tolerance = MAXFREQ_SCALED / PPM_SCALE;
txc->tick = tick_usec; txc->tick = tick_usec;
txc->tai = time_tai; txc->tai = *time_tai;
/* fill PPS status fields */ /* fill PPS status fields */
pps_fill_timex(txc); pps_fill_timex(txc);
raw_spin_unlock_irq(&ntp_lock); txc->time.tv_sec = ts->tv_sec;
txc->time.tv_usec = ts->tv_nsec;
txc->time.tv_sec = ts.tv_sec;
txc->time.tv_usec = ts.tv_nsec;
if (!(time_status & STA_NANO)) if (!(time_status & STA_NANO))
txc->time.tv_usec /= NSEC_PER_USEC; txc->time.tv_usec /= NSEC_PER_USEC;
...@@ -894,7 +860,7 @@ static void hardpps_update_phase(long error) ...@@ -894,7 +860,7 @@ static void hardpps_update_phase(long error)
} }
/* /*
* hardpps() - discipline CPU clock oscillator to external PPS signal * __hardpps() - discipline CPU clock oscillator to external PPS signal
* *
* This routine is called at each PPS signal arrival in order to * This routine is called at each PPS signal arrival in order to
* discipline the CPU clock oscillator to the PPS signal. It takes two * discipline the CPU clock oscillator to the PPS signal. It takes two
...@@ -905,15 +871,13 @@ static void hardpps_update_phase(long error) ...@@ -905,15 +871,13 @@ static void hardpps_update_phase(long error)
* This code is based on David Mills's reference nanokernel * This code is based on David Mills's reference nanokernel
* implementation. It was mostly rewritten but keeps the same idea. * implementation. It was mostly rewritten but keeps the same idea.
*/ */
void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
{ {
struct pps_normtime pts_norm, freq_norm; struct pps_normtime pts_norm, freq_norm;
unsigned long flags; unsigned long flags;
pts_norm = pps_normalize_ts(*phase_ts); pts_norm = pps_normalize_ts(*phase_ts);
raw_spin_lock_irqsave(&ntp_lock, flags);
/* clear the error bits, they will be set again if needed */ /* clear the error bits, they will be set again if needed */
time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR); time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR);
...@@ -925,7 +889,6 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) ...@@ -925,7 +889,6 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
* just start the frequency interval */ * just start the frequency interval */
if (unlikely(pps_fbase.tv_sec == 0)) { if (unlikely(pps_fbase.tv_sec == 0)) {
pps_fbase = *raw_ts; pps_fbase = *raw_ts;
raw_spin_unlock_irqrestore(&ntp_lock, flags);
return; return;
} }
...@@ -940,7 +903,6 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) ...@@ -940,7 +903,6 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
time_status |= STA_PPSJITTER; time_status |= STA_PPSJITTER;
/* restart the frequency calibration interval */ /* restart the frequency calibration interval */
pps_fbase = *raw_ts; pps_fbase = *raw_ts;
raw_spin_unlock_irqrestore(&ntp_lock, flags);
pr_err("hardpps: PPSJITTER: bad pulse\n"); pr_err("hardpps: PPSJITTER: bad pulse\n");
return; return;
} }
...@@ -957,10 +919,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) ...@@ -957,10 +919,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
hardpps_update_phase(pts_norm.nsec); hardpps_update_phase(pts_norm.nsec);
raw_spin_unlock_irqrestore(&ntp_lock, flags);
} }
EXPORT_SYMBOL(hardpps);
#endif /* CONFIG_NTP_PPS */ #endif /* CONFIG_NTP_PPS */
static int __init ntp_tick_adj_setup(char *str) static int __init ntp_tick_adj_setup(char *str)
......
#ifndef _LINUX_NTP_INTERNAL_H
#define _LINUX_NTP_INTERNAL_H
extern void ntp_init(void);
extern void ntp_clear(void);
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
extern u64 ntp_tick_length(void);
extern int second_overflow(unsigned long secs);
extern int ntp_validate_timex(struct timex *);
extern int __do_adjtimex(struct timex *, struct timespec *, s32 *);
extern void __hardpps(const struct timespec *, const struct timespec *);
#endif /* _LINUX_NTP_INTERNAL_H */
This diff is collapsed.
...@@ -323,6 +323,7 @@ static void tick_shutdown(unsigned int *cpup) ...@@ -323,6 +323,7 @@ static void tick_shutdown(unsigned int *cpup)
*/ */
dev->mode = CLOCK_EVT_MODE_UNUSED; dev->mode = CLOCK_EVT_MODE_UNUSED;
clockevents_exchange_device(dev, NULL); clockevents_exchange_device(dev, NULL);
dev->event_handler = clockevents_handle_noop;
td->evtdev = NULL; td->evtdev = NULL;
} }
raw_spin_unlock_irqrestore(&tick_device_lock, flags); raw_spin_unlock_irqrestore(&tick_device_lock, flags);
...@@ -416,4 +417,5 @@ static struct notifier_block tick_notifier = { ...@@ -416,4 +417,5 @@ static struct notifier_block tick_notifier = {
void __init tick_init(void) void __init tick_init(void)
{ {
clockevents_register_notifier(&tick_notifier); clockevents_register_notifier(&tick_notifier);
tick_broadcast_init();
} }
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/tick.h> #include <linux/tick.h>
extern seqlock_t jiffies_lock;
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD #ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD
#define TICK_DO_TIMER_NONE -1 #define TICK_DO_TIMER_NONE -1
...@@ -94,7 +96,7 @@ extern void tick_broadcast_on_off(unsigned long reason, int *oncpu); ...@@ -94,7 +96,7 @@ extern void tick_broadcast_on_off(unsigned long reason, int *oncpu);
extern void tick_shutdown_broadcast(unsigned int *cpup); extern void tick_shutdown_broadcast(unsigned int *cpup);
extern void tick_suspend_broadcast(void); extern void tick_suspend_broadcast(void);
extern int tick_resume_broadcast(void); extern int tick_resume_broadcast(void);
extern void tick_broadcast_init(void);
extern void extern void
tick_set_periodic_handler(struct clock_event_device *dev, int broadcast); tick_set_periodic_handler(struct clock_event_device *dev, int broadcast);
...@@ -119,6 +121,7 @@ static inline void tick_broadcast_on_off(unsigned long reason, int *oncpu) { } ...@@ -119,6 +121,7 @@ static inline void tick_broadcast_on_off(unsigned long reason, int *oncpu) { }
static inline void tick_shutdown_broadcast(unsigned int *cpup) { } static inline void tick_shutdown_broadcast(unsigned int *cpup) { }
static inline void tick_suspend_broadcast(void) { } static inline void tick_suspend_broadcast(void) { }
static inline int tick_resume_broadcast(void) { return 0; } static inline int tick_resume_broadcast(void) { return 0; }
static inline void tick_broadcast_init(void) { }
/* /*
* Set the periodic handler in non broadcast mode * Set the periodic handler in non broadcast mode
......
...@@ -482,8 +482,8 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts) ...@@ -482,8 +482,8 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
if (ratelimit < 10 && if (ratelimit < 10 &&
(local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) { (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) {
printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", pr_warn("NOHZ: local_softirq_pending %02x\n",
(unsigned int) local_softirq_pending()); (unsigned int) local_softirq_pending());
ratelimit++; ratelimit++;
} }
return false; return false;
......
This diff is collapsed.
...@@ -20,6 +20,13 @@ ...@@ -20,6 +20,13 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
struct timer_list_iter {
int cpu;
bool second_pass;
u64 now;
};
typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes); typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes);
DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases); DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
...@@ -133,7 +140,6 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) ...@@ -133,7 +140,6 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu); struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);
int i; int i;
SEQ_printf(m, "\n");
SEQ_printf(m, "cpu: %d\n", cpu); SEQ_printf(m, "cpu: %d\n", cpu);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
SEQ_printf(m, " clock %d:\n", i); SEQ_printf(m, " clock %d:\n", i);
...@@ -187,6 +193,7 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) ...@@ -187,6 +193,7 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
#undef P #undef P
#undef P_ns #undef P_ns
SEQ_printf(m, "\n");
} }
#ifdef CONFIG_GENERIC_CLOCKEVENTS #ifdef CONFIG_GENERIC_CLOCKEVENTS
...@@ -195,7 +202,6 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) ...@@ -195,7 +202,6 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
{ {
struct clock_event_device *dev = td->evtdev; struct clock_event_device *dev = td->evtdev;
SEQ_printf(m, "\n");
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode); SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
if (cpu < 0) if (cpu < 0)
SEQ_printf(m, "Broadcast device\n"); SEQ_printf(m, "Broadcast device\n");
...@@ -230,12 +236,11 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) ...@@ -230,12 +236,11 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
print_name_offset(m, dev->event_handler); print_name_offset(m, dev->event_handler);
SEQ_printf(m, "\n"); SEQ_printf(m, "\n");
SEQ_printf(m, " retries: %lu\n", dev->retries); SEQ_printf(m, " retries: %lu\n", dev->retries);
SEQ_printf(m, "\n");
} }
static void timer_list_show_tickdevices(struct seq_file *m) static void timer_list_show_tickdevices_header(struct seq_file *m)
{ {
int cpu;
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
print_tickdevice(m, tick_get_broadcast_device(), -1); print_tickdevice(m, tick_get_broadcast_device(), -1);
SEQ_printf(m, "tick_broadcast_mask: %08lx\n", SEQ_printf(m, "tick_broadcast_mask: %08lx\n",
...@@ -246,47 +251,104 @@ static void timer_list_show_tickdevices(struct seq_file *m) ...@@ -246,47 +251,104 @@ static void timer_list_show_tickdevices(struct seq_file *m)
#endif #endif
SEQ_printf(m, "\n"); SEQ_printf(m, "\n");
#endif #endif
for_each_online_cpu(cpu)
print_tickdevice(m, tick_get_device(cpu), cpu);
SEQ_printf(m, "\n");
} }
#else
static void timer_list_show_tickdevices(struct seq_file *m) { }
#endif #endif
static inline void timer_list_header(struct seq_file *m, u64 now)
{
SEQ_printf(m, "Timer List Version: v0.7\n");
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
SEQ_printf(m, "\n");
}
static int timer_list_show(struct seq_file *m, void *v) static int timer_list_show(struct seq_file *m, void *v)
{
struct timer_list_iter *iter = v;
u64 now = ktime_to_ns(ktime_get());
if (iter->cpu == -1 && !iter->second_pass)
timer_list_header(m, now);
else if (!iter->second_pass)
print_cpu(m, iter->cpu, iter->now);
#ifdef CONFIG_GENERIC_CLOCKEVENTS
else if (iter->cpu == -1 && iter->second_pass)
timer_list_show_tickdevices_header(m);
else
print_tickdevice(m, tick_get_device(iter->cpu), iter->cpu);
#endif
return 0;
}
void sysrq_timer_list_show(void)
{ {
u64 now = ktime_to_ns(ktime_get()); u64 now = ktime_to_ns(ktime_get());
int cpu; int cpu;
SEQ_printf(m, "Timer List Version: v0.7\n"); timer_list_header(NULL, now);
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
for_each_online_cpu(cpu) for_each_online_cpu(cpu)
print_cpu(m, cpu, now); print_cpu(NULL, cpu, now);
SEQ_printf(m, "\n"); #ifdef CONFIG_GENERIC_CLOCKEVENTS
timer_list_show_tickdevices(m); timer_list_show_tickdevices_header(NULL);
for_each_online_cpu(cpu)
print_tickdevice(NULL, tick_get_device(cpu), cpu);
#endif
return;
}
return 0; static void *timer_list_start(struct seq_file *file, loff_t *offset)
{
struct timer_list_iter *iter = file->private;
if (!*offset) {
iter->cpu = -1;
iter->now = ktime_to_ns(ktime_get());
} else if (iter->cpu >= nr_cpu_ids) {
#ifdef CONFIG_GENERIC_CLOCKEVENTS
if (!iter->second_pass) {
iter->cpu = -1;
iter->second_pass = true;
} else
return NULL;
#else
return NULL;
#endif
}
return iter;
} }
void sysrq_timer_list_show(void) static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset)
{
struct timer_list_iter *iter = file->private;
iter->cpu = cpumask_next(iter->cpu, cpu_online_mask);
++*offset;
return timer_list_start(file, offset);
}
static void timer_list_stop(struct seq_file *seq, void *v)
{ {
timer_list_show(NULL, NULL);
} }
static const struct seq_operations timer_list_sops = {
.start = timer_list_start,
.next = timer_list_next,
.stop = timer_list_stop,
.show = timer_list_show,
};
static int timer_list_open(struct inode *inode, struct file *filp) static int timer_list_open(struct inode *inode, struct file *filp)
{ {
return single_open(filp, timer_list_show, NULL); return seq_open_private(filp, &timer_list_sops,
sizeof(struct timer_list_iter));
} }
static const struct file_operations timer_list_fops = { static const struct file_operations timer_list_fops = {
.open = timer_list_open, .open = timer_list_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = single_release, .release = seq_release_private,
}; };
static int __init init_timer_list_procfs(void) static int __init init_timer_list_procfs(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment