Commit 0eeda71b authored by Thomas Gleixner's avatar Thomas Gleixner

timer: Replace timer base by a cpu index

Instead of storing a pointer to the per cpu tvec_base we can simply
cache a CPU index in the timer_list and use that to get hold of the
correct per cpu tvec_base. This is only used in lock_timer_base() and
the slightly larger code is peanuts versus the spinlock operation and
the d-cache foot print of the timer wheel.

Aside of that this allows to get rid of following nuisances:

 - boot_tvec_base

   That statically allocated 4k bss data is just kept around so the
   timer has a home when it gets statically initialized. It serves no
   other purpose.

   With the CPU index we assign the timer to CPU0 at static
   initialization time and therefor can avoid the whole boot_tvec_base
   dance.  That also simplifies the init code, which just can use the
   per cpu base.

   Before:
     text	   data	    bss	    dec	    hex	filename
    17491	   9201	   4160	  30852	   7884	../build/kernel/time/timer.o
   After:
     text	   data	    bss	    dec	    hex	filename
    17440	   9193	      0	  26633	   6809	../build/kernel/time/timer.o

 - Overloading the base pointer with various flags

   The CPU index has enough space to hold the flags (deferrable,
   irqsafe) so we can get rid of the extra masking and bit fiddling
   with the base pointer.

As a benefit we reduce the size of struct timer_list on 64 bit
machines. 4 - 8 bytes, a size reduction up to 15% per struct timer_list,
which is a real win as we have tons of them embedded in other structs.

This changes also the newly added deferrable printout of the timer
start trace point to capture and print all timer->flags, which allows
us to decode the target cpu of the timer as well.

We might have used bitfields for this, but that would change the
static initializers and the init function for no value to accomodate
big endian bitfields.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Joonwoo Park <joonwoop@codeaurora.org>
Cc: Wenbo Wang <wenbo.wang@memblaze.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Badhri Jagan Sridharan <Badhri@google.com>
Link: http://lkml.kernel.org/r/20150526224511.950084301@linutronix.deSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 1dabbcec
...@@ -14,27 +14,23 @@ struct timer_list { ...@@ -14,27 +14,23 @@ struct timer_list {
* All fields that change during normal runtime grouped to the * All fields that change during normal runtime grouped to the
* same cacheline * same cacheline
*/ */
struct hlist_node entry; struct hlist_node entry;
unsigned long expires; unsigned long expires;
struct tvec_base *base; void (*function)(unsigned long);
unsigned long data;
void (*function)(unsigned long); u32 flags;
unsigned long data; int slack;
int slack;
#ifdef CONFIG_TIMER_STATS #ifdef CONFIG_TIMER_STATS
int start_pid; int start_pid;
void *start_site; void *start_site;
char start_comm[16]; char start_comm[16];
#endif #endif
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map; struct lockdep_map lockdep_map;
#endif #endif
}; };
extern struct tvec_base boot_tvec_bases;
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
/* /*
* NB: because we have to copy the lockdep_map, setting the lockdep_map key * NB: because we have to copy the lockdep_map, setting the lockdep_map key
...@@ -49,9 +45,6 @@ extern struct tvec_base boot_tvec_bases; ...@@ -49,9 +45,6 @@ extern struct tvec_base boot_tvec_bases;
#endif #endif
/* /*
* Note that all tvec_bases are at least 4 byte aligned and lower two bits
* of base in timer_list is guaranteed to be zero. Use them for flags.
*
* A deferrable timer will work normally when the system is busy, but * A deferrable timer will work normally when the system is busy, but
* will not cause a CPU to come out of idle just to service it; instead, * will not cause a CPU to come out of idle just to service it; instead,
* the timer will be serviced when the CPU eventually wakes up with a * the timer will be serviced when the CPU eventually wakes up with a
...@@ -65,17 +58,18 @@ extern struct tvec_base boot_tvec_bases; ...@@ -65,17 +58,18 @@ extern struct tvec_base boot_tvec_bases;
* workqueue locking issues. It's not meant for executing random crap * workqueue locking issues. It's not meant for executing random crap
* with interrupts disabled. Abuse is monitored! * with interrupts disabled. Abuse is monitored!
*/ */
#define TIMER_DEFERRABLE 0x1LU #define TIMER_CPUMASK 0x0007FFFF
#define TIMER_IRQSAFE 0x2LU #define TIMER_MIGRATING 0x00080000
#define TIMER_BASEMASK (TIMER_CPUMASK | TIMER_MIGRATING)
#define TIMER_FLAG_MASK 0x3LU #define TIMER_DEFERRABLE 0x00100000
#define TIMER_IRQSAFE 0x00200000
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \ #define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
.entry = { .next = TIMER_ENTRY_STATIC }, \ .entry = { .next = TIMER_ENTRY_STATIC }, \
.function = (_function), \ .function = (_function), \
.expires = (_expires), \ .expires = (_expires), \
.data = (_data), \ .data = (_data), \
.base = (void *)((unsigned long)&boot_tvec_bases + (_flags)), \ .flags = (_flags), \
.slack = -1, \ .slack = -1, \
__TIMER_LOCKDEP_MAP_INITIALIZER( \ __TIMER_LOCKDEP_MAP_INITIALIZER( \
__FILE__ ":" __stringify(__LINE__)) \ __FILE__ ":" __stringify(__LINE__)) \
......
...@@ -45,16 +45,16 @@ TRACE_EVENT(timer_start, ...@@ -45,16 +45,16 @@ TRACE_EVENT(timer_start,
TP_PROTO(struct timer_list *timer, TP_PROTO(struct timer_list *timer,
unsigned long expires, unsigned long expires,
unsigned int deferrable), unsigned int flags),
TP_ARGS(timer, expires, deferrable), TP_ARGS(timer, expires, flags),
TP_STRUCT__entry( TP_STRUCT__entry(
__field( void *, timer ) __field( void *, timer )
__field( void *, function ) __field( void *, function )
__field( unsigned long, expires ) __field( unsigned long, expires )
__field( unsigned long, now ) __field( unsigned long, now )
__field( unsigned int, deferrable ) __field( unsigned int, flags )
), ),
TP_fast_assign( TP_fast_assign(
...@@ -62,13 +62,12 @@ TRACE_EVENT(timer_start, ...@@ -62,13 +62,12 @@ TRACE_EVENT(timer_start,
__entry->function = timer->function; __entry->function = timer->function;
__entry->expires = expires; __entry->expires = expires;
__entry->now = jiffies; __entry->now = jiffies;
__entry->deferrable = deferrable; __entry->flags = flags;
), ),
TP_printk("timer=%p function=%pf expires=%lu [timeout=%ld] defer=%c", TP_printk("timer=%p function=%pf expires=%lu [timeout=%ld] flags=0x%08x",
__entry->timer, __entry->function, __entry->expires, __entry->timer, __entry->function, __entry->expires,
(long)__entry->expires - __entry->now, (long)__entry->expires - __entry->now, __entry->flags)
__entry->deferrable > 0 ? 'y':'n')
); );
/** /**
......
This diff is collapsed.
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