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

cpuidle/powerpc: Fix smt_snooze_delay functionality.

smt_snooze_delay was designed to delay idle loop's nap entry
in the native idle code before it got ported over to use as part of
the cpuidle framework.

A -ve value assigned to smt_snooze_delay should result in
busy looping, in other words disabling the entry to nap state.

- https://lists.ozlabs.org/pipermail/linuxppc-dev/2010-May/082450.html

This particular functionality can be achieved currently by
echo 1 > /sys/devices/system/cpu/cpu*/state1/disable
but it is broken when one assigns -ve value to the smt_snooze_delay
variable either via sysfs entry or ppc64_cpu util.

This patch aims to fix this, by disabling nap state when smt_snooze_delay
variable is set to -ve value.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Deepthi Dharwar and committed by
Benjamin Herrenschmidt
8ea959a1 817deb05

+20 -10
+2 -2
arch/powerpc/include/asm/processor.h
··· 388 388 extern void power7_nap(void); 389 389 390 390 #ifdef CONFIG_PSERIES_IDLE 391 - extern void update_smt_snooze_delay(int snooze); 391 + extern void update_smt_snooze_delay(int cpu, int residency); 392 392 #else 393 - static inline void update_smt_snooze_delay(int snooze) {} 393 + static inline void update_smt_snooze_delay(int cpu, int residency) {} 394 394 #endif 395 395 396 396 extern void flush_instruction_cache(void);
+1 -1
arch/powerpc/kernel/sysfs.c
··· 50 50 return -EINVAL; 51 51 52 52 per_cpu(smt_snooze_delay, cpu->dev.id) = snooze; 53 - update_smt_snooze_delay(snooze); 53 + update_smt_snooze_delay(cpu->dev.id, snooze); 54 54 55 55 return count; 56 56 }
+17 -7
arch/powerpc/platforms/pseries/processor_idle.c
··· 33 33 static struct cpuidle_device __percpu *pseries_cpuidle_devices; 34 34 static struct cpuidle_state *cpuidle_state_table; 35 35 36 - void update_smt_snooze_delay(int snooze) 37 - { 38 - struct cpuidle_driver *drv = cpuidle_get_driver(); 39 - if (drv) 40 - drv->states[0].target_residency = snooze; 41 - } 42 - 43 36 static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before) 44 37 { 45 38 ··· 182 189 .target_residency = 0, 183 190 .enter = &shared_cede_loop }, 184 191 }; 192 + 193 + void update_smt_snooze_delay(int cpu, int residency) 194 + { 195 + struct cpuidle_driver *drv = cpuidle_get_driver(); 196 + struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); 197 + 198 + if (cpuidle_state_table != dedicated_states) 199 + return; 200 + 201 + if (residency < 0) { 202 + /* Disable the Nap state on that cpu */ 203 + if (dev) 204 + dev->states_usage[1].disable = 1; 205 + } else 206 + if (drv) 207 + drv->states[0].target_residency = residency; 208 + } 185 209 186 210 static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, 187 211 unsigned long action, void *hcpu)