diff --git a/include/linux/sched.h b/include/linux/sched.h
index 7bf67f8..82f3b62 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1894,6 +1894,9 @@ struct task_struct {
 #ifdef CONFIG_TRACE_DELAYED_TIMER_OFFSETS
 	long timer_offset;
 #endif /* CONFIG_TRACE_DELAYED_TIMER_OFFSETS */
+#ifdef CONFIG_TRACE_EVENTS_WAKEUP_LATENCY
+	u64 wakeup_timestamp_start;
+#endif
 #ifdef CONFIG_KCOV
 	/* Coverage collection mode enabled for this task (0 if disabled). */
 	enum kcov_mode kcov_mode;
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 9b90c57..c8b81d0 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -562,6 +562,40 @@ TRACE_EVENT(sched_wake_idle_without_ipi,
 
 	TP_printk("cpu=%d", __entry->cpu)
 );
+
+#ifdef CONFIG_TRACE_EVENTS_WAKEUP_LATENCY
+/**
+ * latency_wakeup - Called when process is woken up
+ * @next:	task to be woken up
+ * @wakeup_lat:	process wakeup latency in nano seconds
+ */
+TRACE_EVENT(latency_wakeup,
+
+	TP_PROTO(struct task_struct *next, u64 wakeup_latency),
+	TP_ARGS(next, wakeup_latency),
+
+	TP_STRUCT__entry(
+		__array(char,		ccomm,  TASK_COMM_LEN)
+		__field(int,		cprio)
+		__field(unsigned long,	wakeup_lat)
+		__field(unsigned long,	timeroffset)
+		__field(unsigned long,	total_lat)
+	),
+
+	TP_fast_assign(
+		memcpy(__entry->ccomm, next->comm, TASK_COMM_LEN);
+		__entry->cprio  = next->prio;
+		__entry->wakeup_lat = wakeup_latency;
+		__entry->timeroffset = next->timer_offset;
+		__entry->total_lat = wakeup_latency + next->timer_offset;
+	),
+
+	TP_printk("curr=%s[%d] wakeup_lat=%lu timeroffset=%ld total_lat=%lu",
+		__entry->ccomm, __entry->cprio, __entry->wakeup_lat,
+		__entry->timeroffset, __entry->total_lat)
+);
+#endif
+
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index f4b86e8..20cf135 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -634,6 +634,16 @@ config RING_BUFFER_STARTUP_TEST
 
 	 If unsure, say N
 
+config TRACE_EVENTS_WAKEUP_LATENCY
+       bool "Trace wakeup latency events"
+       depends on TRACE_DELAYED_TIMER_OFFSETS
+       depends on SCHED_TRACER
+       help
+        Generate the total wakeup latency of a process. It includes the
+        wakeup latency and the timer offset latency. Wakeup latency is the
+        difference in the time when a process is scheduled to be woken up
+        and when it is actually woken up. It depends on the wakeup tracer.
+
 config TRACE_ENUM_MAP_FILE
        bool "Show enum mappings for trace events"
        depends on TRACING
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 9d4399b..ec9e5a8 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -423,6 +423,29 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
 		trace_buffer_unlock_commit(tr, buffer, event, flags, pc);
 }
 
+#ifdef CONFIG_TRACE_EVENTS_WAKEUP_LATENCY
+static inline void latency_wakeup_start(struct task_struct *task, u64 ts)
+{
+	task->wakeup_timestamp_start = ts;
+}
+
+static inline void latency_wakeup_stop(struct task_struct *next, u64 ts)
+{
+	if (next->wakeup_timestamp_start != 0) {
+		trace_latency_wakeup(next, ts - next->wakeup_timestamp_start);
+		next->wakeup_timestamp_start = 0;
+	}
+}
+#else
+static inline void latency_wakeup_start(struct task_struct *task, u64 ts)
+{
+}
+
+static inline void latency_wakeup_stop(struct task_struct *next, u64 ts)
+{
+}
+#endif
+
 static void notrace
 probe_wakeup_sched_switch(void *ignore, bool preempt,
 			  struct task_struct *prev, struct task_struct *next)
@@ -436,6 +459,10 @@ probe_wakeup_sched_switch(void *ignore, bool preempt,
 
 	tracing_record_cmdline(prev);
 
+	cpu = raw_smp_processor_id();
+	T1 = ftrace_now(cpu);
+	latency_wakeup_stop(next, (u64) T1);
+
 	if (unlikely(!tracer_enabled))
 		return;
 
@@ -454,7 +481,6 @@ probe_wakeup_sched_switch(void *ignore, bool preempt,
 	pc = preempt_count();
 
 	/* disable local data, not wakeup_cpu data */
-	cpu = raw_smp_processor_id();
 	disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->trace_buffer.data, cpu)->disabled);
 	if (likely(disabled != 1))
 		goto out;
@@ -473,7 +499,6 @@ probe_wakeup_sched_switch(void *ignore, bool preempt,
 	tracing_sched_switch_trace(wakeup_trace, prev, next, flags, pc);
 
 	T0 = data->preempt_timestamp;
-	T1 = ftrace_now(cpu);
 	delta = T1-T0;
 
 	if (!report_latency(wakeup_trace, delta))
@@ -525,6 +550,10 @@ probe_wakeup(void *ignore, struct task_struct *p)
 	unsigned long flags;
 	long disabled;
 	int pc;
+	cycle_t T0;
+
+	T0 = ftrace_now(cpu);
+	latency_wakeup_start(p, (u64) T0);
 
 	if (likely(!tracer_enabled))
 		return;
@@ -580,7 +609,7 @@ probe_wakeup(void *ignore, struct task_struct *p)
 	local_save_flags(flags);
 
 	data = per_cpu_ptr(wakeup_trace->trace_buffer.data, wakeup_cpu);
-	data->preempt_timestamp = ftrace_now(cpu);
+	data->preempt_timestamp = T0;
 	tracing_sched_wakeup_trace(wakeup_trace, p, current, flags, pc);
 
 	/*
