diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2015-05-26 18:50:28 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-06-19 09:18:27 -0400 |
commit | 1dabbcec2c0a36fe43509d06499b9e512e70a028 (patch) | |
tree | afa43477a43f82b5681b4ac328463a39f4aad99d /kernel/time/timer.c | |
parent | 1bd04bf6f68d65f5422b2b85c495d65d49587a54 (diff) |
timer: Use hlist for the timer wheel hash buckets
This reduces the size of struct tvec_base by 50% and results in
slightly smaller code as well.
Before:
struct tvec_base: size: 8256, cachelines: 129
text data bss dec hex filename
17698 13297 8256 39251 9953 ../build/kernel/time/timer.o
After:
struct tvec_base: 4160, cachelines: 65
text data bss dec hex filename
17491 9201 4160 30852 7884 ../build/kernel/time/timer.o
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
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: John Stultz <john.stultz@linaro.org>
Cc: Joonwoo Park <joonwoop@codeaurora.org>
Cc: Wenbo Wang <wenbo.wang@memblaze.com>
Link: http://lkml.kernel.org/r/20150526224511.854731214@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/time/timer.c')
-rw-r--r-- | kernel/time/timer.c | 64 |
1 files changed, 27 insertions, 37 deletions
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index e212df24ad3f..3a5e0c840884 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c | |||
@@ -70,11 +70,11 @@ EXPORT_SYMBOL(jiffies_64); | |||
70 | #define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1)) | 70 | #define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1)) |
71 | 71 | ||
72 | struct tvec { | 72 | struct tvec { |
73 | struct list_head vec[TVN_SIZE]; | 73 | struct hlist_head vec[TVN_SIZE]; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct tvec_root { | 76 | struct tvec_root { |
77 | struct list_head vec[TVR_SIZE]; | 77 | struct hlist_head vec[TVR_SIZE]; |
78 | }; | 78 | }; |
79 | 79 | ||
80 | struct tvec_base { | 80 | struct tvec_base { |
@@ -356,7 +356,7 @@ __internal_add_timer(struct tvec_base *base, struct timer_list *timer) | |||
356 | { | 356 | { |
357 | unsigned long expires = timer->expires; | 357 | unsigned long expires = timer->expires; |
358 | unsigned long idx = expires - base->timer_jiffies; | 358 | unsigned long idx = expires - base->timer_jiffies; |
359 | struct list_head *vec; | 359 | struct hlist_head *vec; |
360 | 360 | ||
361 | if (idx < TVR_SIZE) { | 361 | if (idx < TVR_SIZE) { |
362 | int i = expires & TVR_MASK; | 362 | int i = expires & TVR_MASK; |
@@ -390,7 +390,7 @@ __internal_add_timer(struct tvec_base *base, struct timer_list *timer) | |||
390 | vec = base->tv5.vec + i; | 390 | vec = base->tv5.vec + i; |
391 | } | 391 | } |
392 | 392 | ||
393 | list_add(&timer->entry, vec); | 393 | hlist_add_head(&timer->entry, vec); |
394 | } | 394 | } |
395 | 395 | ||
396 | static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) | 396 | static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) |
@@ -504,8 +504,8 @@ static int timer_fixup_activate(void *addr, enum debug_obj_state state) | |||
504 | * statically initialized. We just make sure that it | 504 | * statically initialized. We just make sure that it |
505 | * is tracked in the object tracker. | 505 | * is tracked in the object tracker. |
506 | */ | 506 | */ |
507 | if (timer->entry.next == NULL && | 507 | if (timer->entry.pprev == NULL && |
508 | timer->entry.prev == TIMER_ENTRY_STATIC) { | 508 | timer->entry.next == TIMER_ENTRY_STATIC) { |
509 | debug_object_init(timer, &timer_debug_descr); | 509 | debug_object_init(timer, &timer_debug_descr); |
510 | debug_object_activate(timer, &timer_debug_descr); | 510 | debug_object_activate(timer, &timer_debug_descr); |
511 | return 0; | 511 | return 0; |
@@ -551,7 +551,7 @@ static int timer_fixup_assert_init(void *addr, enum debug_obj_state state) | |||
551 | 551 | ||
552 | switch (state) { | 552 | switch (state) { |
553 | case ODEBUG_STATE_NOTAVAILABLE: | 553 | case ODEBUG_STATE_NOTAVAILABLE: |
554 | if (timer->entry.prev == TIMER_ENTRY_STATIC) { | 554 | if (timer->entry.next == TIMER_ENTRY_STATIC) { |
555 | /* | 555 | /* |
556 | * This is not really a fixup. The timer was | 556 | * This is not really a fixup. The timer was |
557 | * statically initialized. We just make sure that it | 557 | * statically initialized. We just make sure that it |
@@ -655,7 +655,7 @@ static void do_init_timer(struct timer_list *timer, unsigned int flags, | |||
655 | { | 655 | { |
656 | struct tvec_base *base = raw_cpu_read(tvec_bases); | 656 | struct tvec_base *base = raw_cpu_read(tvec_bases); |
657 | 657 | ||
658 | timer->entry.next = NULL; | 658 | timer->entry.pprev = NULL; |
659 | timer->base = (void *)((unsigned long)base | flags); | 659 | timer->base = (void *)((unsigned long)base | flags); |
660 | timer->slack = -1; | 660 | timer->slack = -1; |
661 | #ifdef CONFIG_TIMER_STATS | 661 | #ifdef CONFIG_TIMER_STATS |
@@ -687,14 +687,14 @@ EXPORT_SYMBOL(init_timer_key); | |||
687 | 687 | ||
688 | static inline void detach_timer(struct timer_list *timer, bool clear_pending) | 688 | static inline void detach_timer(struct timer_list *timer, bool clear_pending) |
689 | { | 689 | { |
690 | struct list_head *entry = &timer->entry; | 690 | struct hlist_node *entry = &timer->entry; |
691 | 691 | ||
692 | debug_deactivate(timer); | 692 | debug_deactivate(timer); |
693 | 693 | ||
694 | __list_del(entry->prev, entry->next); | 694 | __hlist_del(entry); |
695 | if (clear_pending) | 695 | if (clear_pending) |
696 | entry->next = NULL; | 696 | entry->pprev = NULL; |
697 | entry->prev = LIST_POISON2; | 697 | entry->next = LIST_POISON2; |
698 | } | 698 | } |
699 | 699 | ||
700 | static inline void | 700 | static inline void |
@@ -1095,16 +1095,17 @@ EXPORT_SYMBOL(del_timer_sync); | |||
1095 | static int cascade(struct tvec_base *base, struct tvec *tv, int index) | 1095 | static int cascade(struct tvec_base *base, struct tvec *tv, int index) |
1096 | { | 1096 | { |
1097 | /* cascade all the timers from tv up one level */ | 1097 | /* cascade all the timers from tv up one level */ |
1098 | struct timer_list *timer, *tmp; | 1098 | struct timer_list *timer; |
1099 | struct list_head tv_list; | 1099 | struct hlist_node *tmp; |
1100 | struct hlist_head tv_list; | ||
1100 | 1101 | ||
1101 | list_replace_init(tv->vec + index, &tv_list); | 1102 | hlist_move_list(tv->vec + index, &tv_list); |
1102 | 1103 | ||
1103 | /* | 1104 | /* |
1104 | * We are removing _all_ timers from the list, so we | 1105 | * We are removing _all_ timers from the list, so we |
1105 | * don't have to detach them individually. | 1106 | * don't have to detach them individually. |
1106 | */ | 1107 | */ |
1107 | list_for_each_entry_safe(timer, tmp, &tv_list, entry) { | 1108 | hlist_for_each_entry_safe(timer, tmp, &tv_list, entry) { |
1108 | BUG_ON(tbase_get_base(timer->base) != base); | 1109 | BUG_ON(tbase_get_base(timer->base) != base); |
1109 | /* No accounting, while moving them */ | 1110 | /* No accounting, while moving them */ |
1110 | __internal_add_timer(base, timer); | 1111 | __internal_add_timer(base, timer); |
@@ -1172,8 +1173,8 @@ static inline void __run_timers(struct tvec_base *base) | |||
1172 | spin_lock_irq(&base->lock); | 1173 | spin_lock_irq(&base->lock); |
1173 | 1174 | ||
1174 | while (time_after_eq(jiffies, base->timer_jiffies)) { | 1175 | while (time_after_eq(jiffies, base->timer_jiffies)) { |
1175 | struct list_head work_list; | 1176 | struct hlist_head work_list; |
1176 | struct list_head *head = &work_list; | 1177 | struct hlist_head *head = &work_list; |
1177 | int index; | 1178 | int index; |
1178 | 1179 | ||
1179 | if (!base->all_timers) { | 1180 | if (!base->all_timers) { |
@@ -1192,13 +1193,13 @@ static inline void __run_timers(struct tvec_base *base) | |||
1192 | !cascade(base, &base->tv4, INDEX(2))) | 1193 | !cascade(base, &base->tv4, INDEX(2))) |
1193 | cascade(base, &base->tv5, INDEX(3)); | 1194 | cascade(base, &base->tv5, INDEX(3)); |
1194 | ++base->timer_jiffies; | 1195 | ++base->timer_jiffies; |
1195 | list_replace_init(base->tv1.vec + index, head); | 1196 | hlist_move_list(base->tv1.vec + index, head); |
1196 | while (!list_empty(head)) { | 1197 | while (!hlist_empty(head)) { |
1197 | void (*fn)(unsigned long); | 1198 | void (*fn)(unsigned long); |
1198 | unsigned long data; | 1199 | unsigned long data; |
1199 | bool irqsafe; | 1200 | bool irqsafe; |
1200 | 1201 | ||
1201 | timer = list_first_entry(head, struct timer_list,entry); | 1202 | timer = hlist_entry(head->first, struct timer_list, entry); |
1202 | fn = timer->function; | 1203 | fn = timer->function; |
1203 | data = timer->data; | 1204 | data = timer->data; |
1204 | irqsafe = tbase_get_irqsafe(timer->base); | 1205 | irqsafe = tbase_get_irqsafe(timer->base); |
@@ -1240,7 +1241,7 @@ static unsigned long __next_timer_interrupt(struct tvec_base *base) | |||
1240 | /* Look for timer events in tv1. */ | 1241 | /* Look for timer events in tv1. */ |
1241 | index = slot = timer_jiffies & TVR_MASK; | 1242 | index = slot = timer_jiffies & TVR_MASK; |
1242 | do { | 1243 | do { |
1243 | list_for_each_entry(nte, base->tv1.vec + slot, entry) { | 1244 | hlist_for_each_entry(nte, base->tv1.vec + slot, entry) { |
1244 | if (tbase_get_deferrable(nte->base)) | 1245 | if (tbase_get_deferrable(nte->base)) |
1245 | continue; | 1246 | continue; |
1246 | 1247 | ||
@@ -1271,7 +1272,7 @@ cascade: | |||
1271 | 1272 | ||
1272 | index = slot = timer_jiffies & TVN_MASK; | 1273 | index = slot = timer_jiffies & TVN_MASK; |
1273 | do { | 1274 | do { |
1274 | list_for_each_entry(nte, varp->vec + slot, entry) { | 1275 | hlist_for_each_entry(nte, varp->vec + slot, entry) { |
1275 | if (tbase_get_deferrable(nte->base)) | 1276 | if (tbase_get_deferrable(nte->base)) |
1276 | continue; | 1277 | continue; |
1277 | 1278 | ||
@@ -1530,12 +1531,12 @@ signed long __sched schedule_timeout_uninterruptible(signed long timeout) | |||
1530 | EXPORT_SYMBOL(schedule_timeout_uninterruptible); | 1531 | EXPORT_SYMBOL(schedule_timeout_uninterruptible); |
1531 | 1532 | ||
1532 | #ifdef CONFIG_HOTPLUG_CPU | 1533 | #ifdef CONFIG_HOTPLUG_CPU |
1533 | static void migrate_timer_list(struct tvec_base *new_base, struct list_head *head) | 1534 | static void migrate_timer_list(struct tvec_base *new_base, struct hlist_head *head) |
1534 | { | 1535 | { |
1535 | struct timer_list *timer; | 1536 | struct timer_list *timer; |
1536 | 1537 | ||
1537 | while (!list_empty(head)) { | 1538 | while (!hlist_empty(head)) { |
1538 | timer = list_first_entry(head, struct timer_list, entry); | 1539 | timer = hlist_entry(head->first, struct timer_list, entry); |
1539 | /* We ignore the accounting on the dying cpu */ | 1540 | /* We ignore the accounting on the dying cpu */ |
1540 | detach_timer(timer, false); | 1541 | detach_timer(timer, false); |
1541 | timer_set_base(timer, new_base); | 1542 | timer_set_base(timer, new_base); |
@@ -1603,23 +1604,12 @@ static inline void timer_register_cpu_notifier(void) { } | |||
1603 | 1604 | ||
1604 | static void __init init_timer_cpu(struct tvec_base *base, int cpu) | 1605 | static void __init init_timer_cpu(struct tvec_base *base, int cpu) |
1605 | { | 1606 | { |
1606 | int j; | ||
1607 | |||
1608 | BUG_ON(base != tbase_get_base(base)); | 1607 | BUG_ON(base != tbase_get_base(base)); |
1609 | 1608 | ||
1610 | base->cpu = cpu; | 1609 | base->cpu = cpu; |
1611 | per_cpu(tvec_bases, cpu) = base; | 1610 | per_cpu(tvec_bases, cpu) = base; |
1612 | spin_lock_init(&base->lock); | 1611 | spin_lock_init(&base->lock); |
1613 | 1612 | ||
1614 | for (j = 0; j < TVN_SIZE; j++) { | ||
1615 | INIT_LIST_HEAD(base->tv5.vec + j); | ||
1616 | INIT_LIST_HEAD(base->tv4.vec + j); | ||
1617 | INIT_LIST_HEAD(base->tv3.vec + j); | ||
1618 | INIT_LIST_HEAD(base->tv2.vec + j); | ||
1619 | } | ||
1620 | for (j = 0; j < TVR_SIZE; j++) | ||
1621 | INIT_LIST_HEAD(base->tv1.vec + j); | ||
1622 | |||
1623 | base->timer_jiffies = jiffies; | 1613 | base->timer_jiffies = jiffies; |
1624 | base->next_timer = base->timer_jiffies; | 1614 | base->next_timer = base->timer_jiffies; |
1625 | } | 1615 | } |