patch-2.3.7 linux/arch/i386/kernel/smp.c

Next file: linux/arch/i386/mm/init.c
Previous file: linux/arch/i386/kernel/mca.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.6/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
@@ -1164,6 +1164,7 @@
 }
 
 unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_old_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
 
 /*
@@ -1187,6 +1188,7 @@
 	for (i = 0; i < NR_CPUS; i++) {
 		cpu_number_map[i] = -1;
 		prof_counter[i] = 1;
+		prof_old_multiplier[i] = 1;
 		prof_multiplier[i] = 1;
 	}
 
@@ -1733,6 +1735,10 @@
 	return 0;
 }
 
+static unsigned int calibration_result;
+
+void setup_APIC_timer(unsigned int clocks);
+
 /*
  * Local timer interrupt handler. It does both profiling and
  * process statistics/rescheduling.
@@ -1745,6 +1751,7 @@
 
 void smp_local_timer_interrupt(struct pt_regs * regs)
 {
+	int user = (user_mode(regs) != 0);
 	int cpu = smp_processor_id();
 
 	/*
@@ -1753,25 +1760,34 @@
 	 * updated with atomic operations). This is especially
 	 * useful with a profiling multiplier != 1
 	 */
-	if (!user_mode(regs))
+	if (!user)
 		x86_do_profile(regs->eip);
 
 	if (!--prof_counter[cpu]) {
-		int user=0,system=0;
+		int system = 1 - user;
 		struct task_struct * p = current;
 
 		/*
+		 * The multiplier may have changed since the last time we got
+		 * to this point as a result of the user writing to
+		 * /proc/profile.  In this case we need to adjust the APIC
+		 * timer accordingly.
+		 *
+		 * Interrupts are already masked off at this point.
+		 */
+               prof_counter[cpu] = prof_multiplier[cpu];
+               if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
+                       setup_APIC_timer(calibration_result/prof_counter[cpu]);
+                       prof_old_multiplier[cpu] = prof_counter[cpu];
+               }
+
+		/*
 		 * After doing the above, we need to make like
 		 * a normal interrupt - otherwise timer interrupts
 		 * ignore the global interrupt lock, which is the
 		 * WrongThing (tm) to do.
 		 */
 
-		if (user_mode(regs))
-			user=1;
-		else
-			system=1;
-
  		irq_enter(cpu, 0);
 		update_one_process(p, 1, user, system, cpu);
 		if (p->pid) {
@@ -1791,7 +1807,6 @@
 			kstat.per_cpu_system[cpu] += system;
 
 		}
-		prof_counter[cpu]=prof_multiplier[cpu];
 		irq_exit(cpu, 0);
 	}
 
@@ -2064,8 +2079,6 @@
 	return calibration_result;
 }
 
-static unsigned int calibration_result;
-
 void __init setup_APIC_clock(void)
 {
 	unsigned long flags;
@@ -2117,13 +2130,10 @@
 /*
  * the frequency of the profiling timer can be changed
  * by writing a multiplier value into /proc/profile.
- *
- * usually you want to run this on all CPUs ;)
  */
 int setup_profiling_timer(unsigned int multiplier)
 {
-	int cpu = smp_processor_id();
-	unsigned long flags;
+	int i;
 
 	/*
 	 * Sanity check. [at least 500 APIC cycles should be
@@ -2133,11 +2143,14 @@
 	if ( (!multiplier) || (calibration_result/multiplier < 500))
 		return -EINVAL;
 
-	save_flags(flags);
-	cli();
-	setup_APIC_timer(calibration_result/multiplier);
-	prof_multiplier[cpu]=multiplier;
-	restore_flags(flags);
+	/* 
+	 * Set the new multiplier for each CPU.  CPUs don't start using the
+	 * new values until the next timer interrupt in which they do process
+	 * accounting.  At that time they also adjust their APIC timers
+	 * accordingly.
+	 */
+	for (i = 0; i < NR_CPUS; ++i)
+		prof_multiplier[i] = multiplier;
 
 	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)