CPU Frequency scaling with LTTng

Richard Purdie <[email protected]> Fri, 09 Nov 2007 16:30:02 +0000
Newsgroups gmane.linux.kernel.tracing
Message-ID <[email protected]>
Hi,

I've been experimenting with CPU frequency scaling and its effect on
LTTng. I notice some of the header fields hint at planned support for
frequency scaling but the userland tools (LTTV) don't seem to support
it?

>From a kernel point of view the easiest way to handle it seemed to be to
force a subbuffer switch immediately after the frequency change. This is
relatively straightforward to do with minor alterations to existing
code.

I also added ltt-heartbeat to the cpufreq notifier chain so the
heartbeat frequency would work correctly (cpufreq updates
loops_per_jiffy).

I've attached a patch of my experiment in case its interesting for
others. It has a few potential issues like:

* the mod_timer in heartbeat has a race
* the heatbeat precalc init function is too verbose for this use
* the hook into the OMAP2 amounts to a hack:
  - would fail to compile if LTT was disabled
  - too verbose
  (would it be better to call ltt_switch_buffers() as part of the
cpufreq postchange notifier?)

I'm also not sure about the change to the top of ltt-tracer.c to call
ltt_frequency(). If I don't do that, the correct start and end
frequencies are logged in the headers for each buffer but the trace
header in each buffer lists the start frequency. LTTV seems to ignore
the start/end values and just looks at the header value. If I change the
header value as shown, I get corrupt timestamp output from LTTV (not
that this particularly surprises me)...

Are there any plans to support frequency scaling and is this approach
along the right lines? Any thoughts on the subject are welcome!

Regards,

Richard

_______________________________________________
Ltt-dev mailing list
[email protected]
http://listserv.shafik.org/mailman/listinfo/ltt-dev
ltt-cpufreq.patch (text/x-patch, 6.2 KB)
 arch/arm/mach-omap2/clock.c |    7 +++
 include/linux/ltt-tracer.h  |    2 +
 ltt/ltt-heartbeat.c         |   24 ++++++++++++
 ltt/ltt-relay.c             |   16 ++++++++
 ltt/ltt-tracer.c            |   22 ++++++++++-

Index: linux/arch/arm/mach-omap2/clock.c
===================================================================
--- linux.orig/arch/arm/mach-omap2/clock.c	2007-11-08 16:35:32.000000000 +0000
+++ linux/arch/arm/mach-omap2/clock.c	2007-11-08 18:54:51.000000000 +0000
@@ -22,6 +22,7 @@
 #include <linux/errno.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
+#include <linux/ltt-tracer.h>
 
 #include <asm/io.h>
 
@@ -339,6 +340,12 @@ static void omap2_clksel_recalc(struct c
 		clk->rate = clk->parent->rate / div;
 	}
 
+	if (clk == &mpu_ck) {
+		cpu_khz = clk->rate / 1000;
+		ltt_switch_buffers();
+		printk("MPU clock changed to %dkhz\n", cpu_khz);
+	}
+
 	if (unlikely(clk->flags & RATE_PROPAGATES))
 		propagate_rate(clk);
 }
Index: linux/ltt/ltt-tracer.c
===================================================================
--- linux.orig/ltt/ltt-tracer.c	2007-11-08 16:35:32.000000000 +0000
+++ linux/ltt/ltt-tracer.c	2007-11-08 19:20:45.000000000 +0000
@@ -241,7 +241,7 @@ void ltt_write_trace_header(struct ltt_t
 	header->has_alignment = 0;
 #endif
 	header->freq_scale = trace->freq_scale;
-	header->start_freq = trace->start_freq;
+	header->start_freq = ltt_frequency();
 	header->start_tsc = trace->start_tsc;
 	header->start_monotonic = trace->start_monotonic;
 	header->start_time_sec = trace->start_time.tv_sec;
@@ -880,6 +880,26 @@ int ltt_control(enum ltt_control_msg msg
 }
 EXPORT_SYMBOL_GPL(ltt_control);
 
+void ltt_switch_buffers(void)
+{
+	struct ltt_trace_struct* trace;
+
+	down(&ltt_traces_sem);
+	list_for_each_entry_rcu(trace, &ltt_traces.head, list) {
+		trace->ops->switch_channel(trace->channel.facilities);
+		trace->ops->switch_channel(trace->channel.interrupts);
+		trace->ops->switch_channel(trace->channel.processes);
+		trace->ops->switch_channel(trace->channel.modules);
+		trace->ops->switch_channel(trace->channel.network);
+		trace->ops->switch_channel(trace->channel.cpu);
+#ifdef CONFIG_LTT_HEARTBEAT_EVENT
+		trace->ops->switch_channel(trace->channel.compact);
+#endif
+	}
+	up(&ltt_traces_sem);
+}
+EXPORT_SYMBOL_GPL(ltt_switch_buffers);
+
 int ltt_filter_control(enum ltt_filter_control_msg msg, char *trace_name)
 {
 	int err;
Index: linux/include/linux/ltt-tracer.h
===================================================================
--- linux.orig/include/linux/ltt-tracer.h	2007-11-08 17:33:50.000000000 +0000
+++ linux/include/linux/ltt-tracer.h	2007-11-08 18:53:25.000000000 +0000
@@ -181,6 +181,7 @@ struct ltt_trace_ops {
 	void (*wakeup_channel) (struct ltt_channel_struct *ltt_channel);
 	void (*finish_channel) (struct ltt_channel_struct *channel);
 	void (*remove_channel) (struct ltt_channel_struct *channel);
+	void (*switch_channel) (struct ltt_channel_struct *channel);
 	void *(*reserve_slot) (struct ltt_trace_struct *trace,
 				struct ltt_channel_struct *channel,
 				void **transport_data, size_t data_size,
@@ -690,6 +691,7 @@ void ltt_write_trace_header(struct ltt_t
 		struct ltt_trace_header *header);
 extern void ltt_buffer_destroy(struct ltt_channel_struct *ltt_chan);
 extern void ltt_wakeup_writers(struct work_struct *work);
+extern void ltt_switch_buffers(void);
 
 void ltt_core_register(int (*function)(u8, void*));
 
Index: linux/ltt/ltt-relay.c
===================================================================
--- linux.orig/ltt/ltt-relay.c	2007-11-08 16:37:09.000000000 +0000
+++ linux/ltt/ltt-relay.c	2007-11-08 17:30:28.000000000 +0000
@@ -769,6 +769,21 @@ static void ltt_relay_buffer_flush(struc
 	ltt_force_switch(buf, FORCE_FLUSH);
 }
 
+static void ltt_relay_buffer_flush_active(void *info)
+{
+	struct rchan *rchan = info;
+	struct rchan_buf *buf = &rchan->buf[smp_processor_id()];
+
+	ltt_force_switch(buf, FORCE_ACTIVE);
+}
+
+static void ltt_relay_switch_buffers_chan(struct ltt_channel_struct *ltt_channel)
+{
+	struct rchan *rchan = ltt_channel->trans_channel_data;
+
+	on_each_cpu(ltt_relay_buffer_flush_active, rchan, 1, 1);
+}
+
 static void ltt_relay_async_wakeup_chan(struct ltt_channel_struct *ltt_channel)
 {
 	unsigned int i;
@@ -1226,6 +1241,7 @@ static struct ltt_transport ltt_relay_tr
 		.finish_channel = ltt_relay_finish_channel,
 		.remove_channel = ltt_relay_remove_channel,
 		.wakeup_channel = ltt_relay_async_wakeup_chan,
+		.switch_channel = ltt_relay_switch_buffers_chan,
 		.commit_slot = ltt_relay_commit_slot,
 		.reserve_slot = ltt_relay_reserve_slot,
 		.user_blocking = ltt_relay_user_blocking,
Index: linux/ltt/ltt-heartbeat.c
===================================================================
--- linux.orig/ltt/ltt-heartbeat.c	2007-11-09 10:40:46.000000000 +0000
+++ linux/ltt/ltt-heartbeat.c	2007-11-09 11:15:54.000000000 +0000
@@ -23,6 +23,7 @@
 #include <linux/cpu.h>
 #include <linux/timex.h>
 #include <linux/bitops.h>
+#include <linux/cpufreq.h>
 #include <linux/marker.h>
 #include <linux/ltt-facilities.h>
 #include <linux/ltt-tracer.h>
@@ -37,6 +38,10 @@
 static struct timer_list heartbeat_timer;
 static unsigned int precalc_heartbeat_expire = 0;
 
+#ifdef CONFIG_CPU_FREQ
+static struct notifier_block ltt_hb_freq_notifier;
+#endif
+
 int ltt_compact_data_shift = 0;
 EXPORT_SYMBOL_GPL(ltt_compact_data_shift);
 
@@ -299,6 +304,21 @@ static void init_heartbeat_timer(void)
 			"- continuing without one \n");
 }
 
+#ifdef CONFIG_CPU_FREQ
+static int
+ltt_hb_freq_freq_transition(struct notifier_block *nb, unsigned long val, void *data)
+{
+	switch (val) {
+	case CPUFREQ_POSTCHANGE:
+		init_heartbeat_timer();
+		if (timer_pending(&heartbeat_timer))
+			mod_timer(&heartbeat_timer, jiffies + precalc_heartbeat_expire);
+		break;
+	}
+	return 0;
+}
+#endif
+
 /* ltt_init_compact_facility reserves the number of bits to identify the event
  * numbers in the compact headers. It must be called every time the compact
  * facility is changed. */
@@ -415,6 +435,10 @@ static int __init ltt_heartbeat_init(voi
 	ltt_heartbeat_init_synthetic_tsc();
 #endif //CONFIG_LTT_SYNTHETIC_TSC
 	init_heartbeat_timer();
+#ifdef CONFIG_CPU_FREQ
+	ltt_hb_freq_notifier.notifier_call = ltt_hb_freq_freq_transition;
+	cpufreq_register_notifier(&ltt_hb_freq_notifier, CPUFREQ_TRANSITION_NOTIFIER);
+#endif
 	return 0;
 }