Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

alarmtimer: Add tracepoints for alarm timers

Alarm timers are one of the mechanisms to wake up a system from suspend,
but there exist no tracepoints to analyse which process/thread armed an
alarmtimer.

Add tracepoints for start/cancel/expire of individual alarm timers and one
for tracing the suspend time decision when to resume the system.

The following trace excerpt illustrates the new mechanism:

Binder:3292_2-3304 [000] d..2 149.981123: alarmtimer_cancel:
alarmtimer:ffffffc1319a7800 type:REALTIME
expires:1325463120000000000 now:1325376810370370245

Binder:3292_2-3304 [000] d..2 149.981136: alarmtimer_start:
alarmtimer:ffffffc1319a7800 type:REALTIME
expires:1325376840000000000 now:1325376810370384591

Binder:3292_9-3953 [000] d..2 150.212991: alarmtimer_cancel:
alarmtimer:ffffffc1319a5a00 type:BOOTTIME
expires:179552000000 now:150154008122

Binder:3292_9-3953 [000] d..2 150.213006: alarmtimer_start:
alarmtimer:ffffffc1319a5a00 type:BOOTTIME
expires:179551000000 now:150154025622

system_server-3000 [002] ...1 162.701940: alarmtimer_suspend:
alarmtimer type:REALTIME expires:1325376840000000000

The wakeup time which is selected at suspend time allows to map it back to
the task arming the timer: Binder:3292_2.

[ tglx: Store alarm timer expiry time instead of some useless RTC relative
information, add proper type information for wakeups which are
handled via the clock_nanosleep/freezer and massage the changelog. ]

Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Prarit Bhargava <prarit@redhat.com>
Cc: Richard Cochran <richardcochran@gmail.com>
Link: http://lkml.kernel.org/r/1480372524-15181-5-git-send-email-john.stultz@linaro.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Baolin Wang and committed by
Thomas Gleixner
4a057549 2924ecd4

+144 -10
+5
include/linux/alarmtimer.h
··· 10 10 ALARM_REALTIME, 11 11 ALARM_BOOTTIME, 12 12 13 + /* Supported types end here */ 13 14 ALARM_NUMTYPE, 15 + 16 + /* Used for tracing information. No usable types. */ 17 + ALARM_REALTIME_FREEZER, 18 + ALARM_BOOTTIME_FREEZER, 14 19 }; 15 20 16 21 enum alarmtimer_restart {
+96
include/trace/events/alarmtimer.h
··· 1 + #undef TRACE_SYSTEM 2 + #define TRACE_SYSTEM alarmtimer 3 + 4 + #if !defined(_TRACE_ALARMTIMER_H) || defined(TRACE_HEADER_MULTI_READ) 5 + #define _TRACE_ALARMTIMER_H 6 + 7 + #include <linux/alarmtimer.h> 8 + #include <linux/rtc.h> 9 + #include <linux/tracepoint.h> 10 + 11 + TRACE_DEFINE_ENUM(ALARM_REALTIME); 12 + TRACE_DEFINE_ENUM(ALARM_BOOTTIME); 13 + TRACE_DEFINE_ENUM(ALARM_REALTIME_FREEZER); 14 + TRACE_DEFINE_ENUM(ALARM_BOOTTIME_FREEZER); 15 + 16 + #define show_alarm_type(type) __print_flags(type, " | ", \ 17 + { 1 << ALARM_REALTIME, "REALTIME" }, \ 18 + { 1 << ALARM_BOOTTIME, "BOOTTIME" }, \ 19 + { 1 << ALARM_REALTIME_FREEZER, "REALTIME Freezer" }, \ 20 + { 1 << ALARM_BOOTTIME_FREEZER, "BOOTTIME Freezer" }) 21 + 22 + TRACE_EVENT(alarmtimer_suspend, 23 + 24 + TP_PROTO(ktime_t expires, int flag), 25 + 26 + TP_ARGS(expires, flag), 27 + 28 + TP_STRUCT__entry( 29 + __field(s64, expires) 30 + __field(unsigned char, alarm_type) 31 + ), 32 + 33 + TP_fast_assign( 34 + __entry->expires = expires.tv64; 35 + __entry->alarm_type = flag; 36 + ), 37 + 38 + TP_printk("alarmtimer type:%s expires:%llu", 39 + show_alarm_type((1 << __entry->alarm_type)), 40 + __entry->expires 41 + ) 42 + ); 43 + 44 + DECLARE_EVENT_CLASS(alarm_class, 45 + 46 + TP_PROTO(struct alarm *alarm, ktime_t now), 47 + 48 + TP_ARGS(alarm, now), 49 + 50 + TP_STRUCT__entry( 51 + __field(void *, alarm) 52 + __field(unsigned char, alarm_type) 53 + __field(s64, expires) 54 + __field(s64, now) 55 + ), 56 + 57 + TP_fast_assign( 58 + __entry->alarm = alarm; 59 + __entry->alarm_type = alarm->type; 60 + __entry->expires = alarm->node.expires.tv64; 61 + __entry->now = now.tv64; 62 + ), 63 + 64 + TP_printk("alarmtimer:%p type:%s expires:%llu now:%llu", 65 + __entry->alarm, 66 + show_alarm_type((1 << __entry->alarm_type)), 67 + __entry->expires, 68 + __entry->now 69 + ) 70 + ); 71 + 72 + DEFINE_EVENT(alarm_class, alarmtimer_fired, 73 + 74 + TP_PROTO(struct alarm *alarm, ktime_t now), 75 + 76 + TP_ARGS(alarm, now) 77 + ); 78 + 79 + DEFINE_EVENT(alarm_class, alarmtimer_start, 80 + 81 + TP_PROTO(struct alarm *alarm, ktime_t now), 82 + 83 + TP_ARGS(alarm, now) 84 + ); 85 + 86 + DEFINE_EVENT(alarm_class, alarmtimer_cancel, 87 + 88 + TP_PROTO(struct alarm *alarm, ktime_t now), 89 + 90 + TP_ARGS(alarm, now) 91 + ); 92 + 93 + #endif /* _TRACE_ALARMTIMER_H */ 94 + 95 + /* This part must be outside protection */ 96 + #include <trace/define_trace.h>
+43 -10
kernel/time/alarmtimer.c
··· 26 26 #include <linux/workqueue.h> 27 27 #include <linux/freezer.h> 28 28 29 + #define CREATE_TRACE_POINTS 30 + #include <trace/events/alarmtimer.h> 31 + 29 32 /** 30 33 * struct alarm_base - Alarm timer bases 31 34 * @lock: Lock for syncrhonized access to the base ··· 43 40 clockid_t base_clockid; 44 41 } alarm_bases[ALARM_NUMTYPE]; 45 42 46 - /* freezer delta & lock used to handle clock_nanosleep triggered wakeups */ 43 + /* freezer information to handle clock_nanosleep triggered wakeups */ 44 + static enum alarmtimer_type freezer_alarmtype; 45 + static ktime_t freezer_expires; 47 46 static ktime_t freezer_delta; 48 47 static DEFINE_SPINLOCK(freezer_delta_lock); 49 48 ··· 199 194 } 200 195 spin_unlock_irqrestore(&base->lock, flags); 201 196 197 + trace_alarmtimer_fired(alarm, base->gettime()); 202 198 return ret; 203 199 204 200 } ··· 224 218 */ 225 219 static int alarmtimer_suspend(struct device *dev) 226 220 { 227 - struct rtc_time tm; 228 - ktime_t min, now; 229 - unsigned long flags; 221 + ktime_t min, now, expires; 222 + int i, ret, type; 230 223 struct rtc_device *rtc; 231 - int i; 232 - int ret; 224 + unsigned long flags; 225 + struct rtc_time tm; 233 226 234 227 spin_lock_irqsave(&freezer_delta_lock, flags); 235 228 min = freezer_delta; 229 + expires = freezer_expires; 230 + type = freezer_alarmtype; 236 231 freezer_delta = ktime_set(0, 0); 237 232 spin_unlock_irqrestore(&freezer_delta_lock, flags); 238 233 ··· 254 247 if (!next) 255 248 continue; 256 249 delta = ktime_sub(next->expires, base->gettime()); 257 - if (!min.tv64 || (delta.tv64 < min.tv64)) 250 + if (!min.tv64 || (delta.tv64 < min.tv64)) { 251 + expires = next->expires; 258 252 min = delta; 253 + type = i; 254 + } 259 255 } 260 256 if (min.tv64 == 0) 261 257 return 0; ··· 267 257 __pm_wakeup_event(ws, 2 * MSEC_PER_SEC); 268 258 return -EBUSY; 269 259 } 260 + 261 + trace_alarmtimer_suspend(expires, type); 270 262 271 263 /* Setup an rtc timer to fire that far in the future */ 272 264 rtc_timer_cancel(rtc, &rtctimer); ··· 307 295 308 296 static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type) 309 297 { 310 - ktime_t delta; 298 + struct alarm_base *base; 311 299 unsigned long flags; 312 - struct alarm_base *base = &alarm_bases[type]; 300 + ktime_t delta; 301 + 302 + switch(type) { 303 + case ALARM_REALTIME: 304 + base = &alarm_bases[ALARM_REALTIME]; 305 + type = ALARM_REALTIME_FREEZER; 306 + break; 307 + case ALARM_BOOTTIME: 308 + base = &alarm_bases[ALARM_BOOTTIME]; 309 + type = ALARM_BOOTTIME_FREEZER; 310 + break; 311 + default: 312 + WARN_ONCE(1, "Invalid alarm type: %d\n", type); 313 + return; 314 + } 313 315 314 316 delta = ktime_sub(absexp, base->gettime()); 315 317 316 318 spin_lock_irqsave(&freezer_delta_lock, flags); 317 - if (!freezer_delta.tv64 || (delta.tv64 < freezer_delta.tv64)) 319 + if (!freezer_delta.tv64 || (delta.tv64 < freezer_delta.tv64)) { 318 320 freezer_delta = delta; 321 + freezer_expires = absexp; 322 + freezer_alarmtype = type; 323 + } 319 324 spin_unlock_irqrestore(&freezer_delta_lock, flags); 320 325 } 321 326 ··· 371 342 alarmtimer_enqueue(base, alarm); 372 343 hrtimer_start(&alarm->timer, alarm->node.expires, HRTIMER_MODE_ABS); 373 344 spin_unlock_irqrestore(&base->lock, flags); 345 + 346 + trace_alarmtimer_start(alarm, base->gettime()); 374 347 } 375 348 EXPORT_SYMBOL_GPL(alarm_start); 376 349 ··· 421 390 if (ret >= 0) 422 391 alarmtimer_dequeue(base, alarm); 423 392 spin_unlock_irqrestore(&base->lock, flags); 393 + 394 + trace_alarmtimer_cancel(alarm, base->gettime()); 424 395 return ret; 425 396 } 426 397 EXPORT_SYMBOL_GPL(alarm_try_to_cancel);