Skip to contentSkip to conversation

use for_each_cpu in oprofile code

PATCH New Anton Blanchard 2 participants 6 messages Sep 4, 2004 – Sep 6, 2004

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 use for_each_cpu in oprofile code

Replace open coded versions with for_each_cpu()/for_each_online_cpu().

===== cpu_buffer.c 1.11 vs edited =====

cpu_buffer.c renamed from drivers/oprofile/cpu_buffer.c +4 -16
@@ -36,11 +36,8 @@
 {
 	int i;
  
-	for (i = 0; i < NR_CPUS; ++i) {
-		if (!cpu_online(i))
-			continue;
+	for_each_online_cpu(i)
 		vfree(cpu_buffer[i].buffer);
-	}
 }
  
  
@@ -50,12 +47,9 @@
  
 	unsigned long buffer_size = fs_cpu_buffer_size;
  
-	for (i = 0; i < NR_CPUS; ++i) {
+	for_each_online_cpu(i) {
 		struct oprofile_cpu_buffer * b = &cpu_buffer[i];
  
-		if (!cpu_online(i))
-			continue;
-
 		b->buffer = vmalloc(sizeof(struct op_sample) * buffer_size);
 		if (!b->buffer)
 			goto fail;
@@ -94,12 +88,9 @@
 
 	timers_enabled = 1;
 
-	for (i = 0; i < NR_CPUS; ++i) {
+	for_each_online_cpu(i) {
 		struct oprofile_cpu_buffer * b = &cpu_buffer[i];
 
-		if (!cpu_online(i))
-			continue;
-
 		add_timer_on(&b->timer, i);
 	}
 }
@@ -111,11 +102,8 @@
 
 	timers_enabled = 0;
 
-	for (i = 0; i < NR_CPUS; ++i) {
+	for_each_online_cpu(i) {
 		struct oprofile_cpu_buffer * b = &cpu_buffer[i];
-
-		if (!cpu_online(i))
-			continue;
 
 		del_timer_sync(&b->timer);
 	}

===== oprofile_stats.c 1.8 vs edited =====

oprofile_stats.c renamed from drivers/oprofile/oprofile_stats.c +2 -8
@@ -22,10 +22,7 @@
 	struct oprofile_cpu_buffer * cpu_buf; 
 	int i;
  
-	for (i = 0; i < NR_CPUS; ++i) {
-		if (!cpu_possible(i))
-			continue;
-
+	for_each_cpu(i) {
 		cpu_buf = &cpu_buffer[i]; 
 		cpu_buf->sample_received = 0;
 		cpu_buf->sample_lost_overflow = 0;
@@ -49,10 +46,7 @@
 	if (!dir)
 		return;
 
-	for (i = 0; i < NR_CPUS; ++i) {
-		if (!cpu_possible(i))
-			continue;
-
+	for_each_cpu(i) {
 		cpu_buf = &cpu_buffer[i]; 
 		snprintf(buf, 10, "cpu%d", i);
 		cpudir = oprofilefs_mkdir(sb, dir, buf);
#1 lore raw

PATCH 1/1 fix oprofile vfree warning on error

On error we can call __free_cpu_buffers with only some buffers allocated. I was getting a bunch of vfree warnings when I hit it, we should check before calling vfree.

drivers/oprofile/cpu_buffer.c renamed from drivers/oprofile/cpu_buffer.c~oprofile_vfree_fix +4 -2
diff -puN drivers/oprofile/cpu_buffer.c~oprofile_vfree_fix drivers/oprofile/cpu_buffer.c
@@ -36,8 +36,10 @@ static void __free_cpu_buffers(int num)
 {
 	int i;
  
-	for_each_online_cpu(i)
-		vfree(cpu_buffer[i].buffer);
+	for_each_online_cpu(i) {
+		if (cpu_buffer[i].buffer)
+			vfree(cpu_buffer[i].buffer);
+	}
 }
  
  

_

#2 lore raw

PATCH 1/1 Speed up oprofile buffer drain code

Hi,

I noticed a large machine was doing about 400,000 context switches per second when oprofile was enabled. Upon closer inspection it looks like we were rearming the buffer sync timer without modifying the expire time.

Now that we have schedule_delayed_work_on I believe we can remove the timer completely. Each cpu should be offset by 1 jiffy so they dont all fire at the same time. I bumped DEFAULT_TIMER_EXPIRE from 2 to 10 times a second to be sure we reap cpu buffers.

With the following patch the same large machine gets about 4000 context switches per second.

John does this look OK to you? The bump of DEFAULT_TIMER_EXPIRE was pretty arbitrary, I was seeing dropped samples before but that could be due to the timer storm before I converted it to delayed work.

drivers/oprofile/cpu_buffer.c renamed from drivers/oprofile/cpu_buffer.c~oprofile_optimise +18 -29
diff -puN drivers/oprofile/cpu_buffer.c~oprofile_optimise drivers/oprofile/cpu_buffer.c
@@ -28,8 +28,8 @@
 struct oprofile_cpu_buffer cpu_buffer[NR_CPUS] __cacheline_aligned;
 
 static void wq_sync_buffer(void *);
-static void timer_ping(unsigned long data);
-#define DEFAULT_TIMER_EXPIRE (HZ / 2)
+
+#define DEFAULT_TIMER_EXPIRE (HZ / 10)
 int timers_enabled;
 
 static void __free_cpu_buffers(int num)
@@ -64,10 +64,6 @@ int alloc_cpu_buffers(void)
 		b->sample_received = 0;
 		b->sample_lost_overflow = 0;
 		b->cpu = i;
-		init_timer(&b->timer);
-		b->timer.function = timer_ping;
-		b->timer.data = i;
-		b->timer.expires = jiffies + DEFAULT_TIMER_EXPIRE;
 		INIT_WORK(&b->work, wq_sync_buffer, b);
 	}
 	return 0;
@@ -93,7 +89,11 @@ void start_cpu_timers(void)
 	for_each_online_cpu(i) {
 		struct oprofile_cpu_buffer * b = &cpu_buffer[i];
 
-		add_timer_on(&b->timer, i);
+		/*
+		 * Spread the work by 1 jiffy per cpu so they dont all 
+		 * fire at once.
+		 */
+		schedule_delayed_work_on(i, &b->work, DEFAULT_TIMER_EXPIRE + i);
 	}
 }
 
@@ -107,7 +107,7 @@ void end_cpu_timers(void)
 	for_each_online_cpu(i) {
 		struct oprofile_cpu_buffer * b = &cpu_buffer[i];
 
-		del_timer_sync(&b->timer);
+		cancel_delayed_work(&b->work);
 	}
 
 	flush_scheduled_work();
@@ -203,7 +203,13 @@ void cpu_buffer_reset(struct oprofile_cp
 }
 
 
-/* FIXME: not guaranteed to be on our CPU */
+/*
+ * This serves to avoid cpu buffer overflow, and makes sure
+ * the task mortuary progresses
+ *
+ * By using schedule_delayed_work_on and then schedule_delayed_work
+ * we guarantee this will stay on the correct cpu
+ */
 static void wq_sync_buffer(void * data)
 {
 	struct oprofile_cpu_buffer * b = (struct oprofile_cpu_buffer *)data;
@@ -213,24 +219,7 @@ static void wq_sync_buffer(void * data)
 	}
 	sync_buffer(b->cpu);
 
-	/* don't re-add the timer if we're shutting down */
-	if (timers_enabled) {
-		del_timer_sync(&b->timer);
-		add_timer_on(&b->timer, b->cpu);
-	}
-}
-
-
-/* This serves to avoid cpu buffer overflow, and makes sure
- * the task mortuary progresses
- */
-static void timer_ping(unsigned long data)
-{
-	struct oprofile_cpu_buffer * b = &cpu_buffer[data];
-	if (b->cpu != smp_processor_id()) {
-		printk("Timer on CPU%d, prefer CPU%d\n",
-		       smp_processor_id(), b->cpu);
-	}
-	schedule_work(&b->work);
-	/* work will re-enable our timer */
+	/* don't re-add the work if we're shutting down */
+	if (timers_enabled)
+		schedule_delayed_work(&b->work, DEFAULT_TIMER_EXPIRE);
 }
drivers/oprofile/cpu_buffer.h renamed from drivers/oprofile/cpu_buffer.h~oprofile_optimise +0 -2
diff -puN drivers/oprofile/cpu_buffer.h~oprofile_optimise drivers/oprofile/cpu_buffer.h
@@ -12,7 +12,6 @@
 
 #include <linux/types.h>
 #include <linux/spinlock.h>
-#include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/cache.h>
  
@@ -42,7 +41,6 @@ struct oprofile_cpu_buffer {
 	unsigned long sample_received;
 	unsigned long sample_lost_overflow;
 	int cpu;
-	struct timer_list timer;
 	struct work_struct work;
 } ____cacheline_aligned;
 

_

#3 lore raw

Re: [PATCH] Speed up oprofile buffer drain code

Quoted 1 line · Anton Blanchard

John does this look OK to you?

Could we please rename timers_enable/start_cpu_timers()/end_cpu_timers() now there's no timer?

Other than that looks great.

thanks
john

#4 lore raw

Re: [PATCH] fix oprofile vfree warning on error

In reply to Anton Blanchard
Quoted 3 lines · Anton Blanchard

On error we can call __free_cpu_buffers with only some buffers allocated. I was getting a bunch of vfree warnings when I hit it, we should check before calling vfree.

Why does vfree() differ from free() / kfree() in not accepting NULL ? This seems like an interface wart.

cheers
john

Quoted 6 lines
drivers/oprofile/cpu_buffer.c renamed from drivers/oprofile/cpu_buffer.c~oprofile_vfree_fix +4 -2
-	for_each_online_cpu(i)
-		vfree(cpu_buffer[i].buffer);
+	for_each_online_cpu(i) {
+		if (cpu_buffer[i].buffer)
+			vfree(cpu_buffer[i].buffer);
+	}
#5 lore raw

Re: [PATCH] fix oprofile vfree warning on error

Quoted 3 lines

Why does vfree() differ from free() / kfree() in not accepting NULL ? This seems like an interface wart.

No idea, it does seem wrong to have this difference between kfree and vfree.

Anton