aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAshok Raj <ashok.raj@intel.com>2005-11-09 00:34:24 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-09 10:55:50 -0500
commit90d45d17f3e68608ac7ba8fc3d7acce022a19c8e (patch)
tree615b2f21c3e02e0ec901febd180014fed64a6a01
parent330d57fb98a916fa8e1363846540dd420e99499a (diff)
[PATCH] cpu hotplug: fix locking in cpufreq drivers
When calling target drivers to set frequency, we take cpucontrol lock. When we modified the code to accomodate CPU hotplug, there was an attempt to take a double lock of cpucontrol leading to a deadlock. Since the current thread context is already holding the cpucontrol lock, we dont need to make another attempt to acquire it. Now we leave a trace in current->flags indicating current thread already is under cpucontrol lock held, so we dont attempt to do this another time. Thanks to Andrew Morton for the beating:-) From: Brice Goglin <Brice.Goglin@ens-lyon.org> Build fix (akpm: this patch is still unpleasant. Ashok continues to look for a cleaner solution, doesn't he? ;)) Signed-off-by: Ashok Raj <ashok.raj@intel.com> Signed-off-by: Brice Goglin <Brice.Goglin@ens-lyon.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--include/linux/cpu.h5
-rw-r--r--include/linux/sched.h1
-rw-r--r--kernel/cpu.c33
4 files changed, 49 insertions, 14 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 25acf478c9e8..23a63207d747 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -38,7 +38,6 @@ static struct cpufreq_driver *cpufreq_driver;
38static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; 38static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS];
39static DEFINE_SPINLOCK(cpufreq_driver_lock); 39static DEFINE_SPINLOCK(cpufreq_driver_lock);
40 40
41
42/* internal prototypes */ 41/* internal prototypes */
43static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); 42static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
44static void handle_update(void *data); 43static void handle_update(void *data);
@@ -1115,24 +1114,21 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
1115 int retval = -EINVAL; 1114 int retval = -EINVAL;
1116 1115
1117 /* 1116 /*
1118 * Converted the lock_cpu_hotplug to preempt_disable() 1117 * If we are already in context of hotplug thread, we dont need to
1119 * and preempt_enable(). This is a bit kludgy and relies on how cpu 1118 * acquire the hotplug lock. Otherwise acquire cpucontrol to prevent
1120 * hotplug works. All we need is a guarantee that cpu hotplug won't make 1119 * hotplug from removing this cpu that we are working on.
1121 * progress on any cpu. Once we do preempt_disable(), this would ensure
1122 * that hotplug threads don't get onto this cpu, thereby delaying
1123 * the cpu remove process.
1124 *
1125 * We removed the lock_cpu_hotplug since we need to call this function
1126 * via cpu hotplug callbacks, which result in locking the cpu hotplug
1127 * thread itself. Agree this is not very clean, cpufreq community
1128 * could improve this if required. - Ashok Raj <ashok.raj@intel.com>
1129 */ 1120 */
1130 preempt_disable(); 1121 if (!current_in_cpu_hotplug())
1122 lock_cpu_hotplug();
1123
1131 dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu, 1124 dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
1132 target_freq, relation); 1125 target_freq, relation);
1133 if (cpu_online(policy->cpu) && cpufreq_driver->target) 1126 if (cpu_online(policy->cpu) && cpufreq_driver->target)
1134 retval = cpufreq_driver->target(policy, target_freq, relation); 1127 retval = cpufreq_driver->target(policy, target_freq, relation);
1135 preempt_enable(); 1128
1129 if (!current_in_cpu_hotplug())
1130 unlock_cpu_hotplug();
1131
1136 return retval; 1132 return retval;
1137} 1133}
1138EXPORT_SYMBOL_GPL(__cpufreq_driver_target); 1134EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 1f7b2c097503..43c44530ef9d 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -42,6 +42,7 @@ struct notifier_block;
42/* Need to know about CPUs going up/down? */ 42/* Need to know about CPUs going up/down? */
43extern int register_cpu_notifier(struct notifier_block *nb); 43extern int register_cpu_notifier(struct notifier_block *nb);
44extern void unregister_cpu_notifier(struct notifier_block *nb); 44extern void unregister_cpu_notifier(struct notifier_block *nb);
45extern int current_in_cpu_hotplug(void);
45 46
46int cpu_up(unsigned int cpu); 47int cpu_up(unsigned int cpu);
47 48
@@ -54,6 +55,10 @@ static inline int register_cpu_notifier(struct notifier_block *nb)
54static inline void unregister_cpu_notifier(struct notifier_block *nb) 55static inline void unregister_cpu_notifier(struct notifier_block *nb)
55{ 56{
56} 57}
58static inline int current_in_cpu_hotplug(void)
59{
60 return 0;
61}
57 62
58#endif /* CONFIG_SMP */ 63#endif /* CONFIG_SMP */
59extern struct sysdev_class cpu_sysdev_class; 64extern struct sysdev_class cpu_sysdev_class;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 03b68a7b4b82..2bbf968b23d9 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -909,6 +909,7 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
909#define PF_SYNCWRITE 0x00200000 /* I am doing a sync write */ 909#define PF_SYNCWRITE 0x00200000 /* I am doing a sync write */
910#define PF_BORROWED_MM 0x00400000 /* I am a kthread doing use_mm */ 910#define PF_BORROWED_MM 0x00400000 /* I am a kthread doing use_mm */
911#define PF_RANDOMIZE 0x00800000 /* randomize virtual address space */ 911#define PF_RANDOMIZE 0x00800000 /* randomize virtual address space */
912#define PF_HOTPLUG_CPU 0x01000000 /* Currently performing CPU hotplug */
912 913
913/* 914/*
914 * Only the _current_ task can read/write to tsk->flags, but other 915 * Only the _current_ task can read/write to tsk->flags, but other
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 3619e939182e..d61ba88f34e5 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -21,6 +21,24 @@ EXPORT_SYMBOL_GPL(cpucontrol);
21 21
22static struct notifier_block *cpu_chain; 22static struct notifier_block *cpu_chain;
23 23
24/*
25 * Used to check by callers if they need to acquire the cpucontrol
26 * or not to protect a cpu from being removed. Its sometimes required to
27 * call these functions both for normal operations, and in response to
28 * a cpu being added/removed. If the context of the call is in the same
29 * thread context as a CPU hotplug thread, we dont need to take the lock
30 * since its already protected
31 * check drivers/cpufreq/cpufreq.c for its usage - Ashok Raj
32 */
33
34int current_in_cpu_hotplug(void)
35{
36 return (current->flags & PF_HOTPLUG_CPU);
37}
38
39EXPORT_SYMBOL_GPL(current_in_cpu_hotplug);
40
41
24/* Need to know about CPUs going up/down? */ 42/* Need to know about CPUs going up/down? */
25int register_cpu_notifier(struct notifier_block *nb) 43int register_cpu_notifier(struct notifier_block *nb)
26{ 44{
@@ -94,6 +112,13 @@ int cpu_down(unsigned int cpu)
94 goto out; 112 goto out;
95 } 113 }
96 114
115 /*
116 * Leave a trace in current->flags indicating we are already in
117 * process of performing CPU hotplug. Callers can check if cpucontrol
118 * is already acquired by current thread, and if so not cause
119 * a dead lock by not acquiring the lock
120 */
121 current->flags |= PF_HOTPLUG_CPU;
97 err = notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE, 122 err = notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE,
98 (void *)(long)cpu); 123 (void *)(long)cpu);
99 if (err == NOTIFY_BAD) { 124 if (err == NOTIFY_BAD) {
@@ -146,6 +171,7 @@ out_thread:
146out_allowed: 171out_allowed:
147 set_cpus_allowed(current, old_allowed); 172 set_cpus_allowed(current, old_allowed);
148out: 173out:
174 current->flags &= ~PF_HOTPLUG_CPU;
149 unlock_cpu_hotplug(); 175 unlock_cpu_hotplug();
150 return err; 176 return err;
151} 177}
@@ -163,6 +189,12 @@ int __devinit cpu_up(unsigned int cpu)
163 ret = -EINVAL; 189 ret = -EINVAL;
164 goto out; 190 goto out;
165 } 191 }
192
193 /*
194 * Leave a trace in current->flags indicating we are already in
195 * process of performing CPU hotplug.
196 */
197 current->flags |= PF_HOTPLUG_CPU;
166 ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu); 198 ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
167 if (ret == NOTIFY_BAD) { 199 if (ret == NOTIFY_BAD) {
168 printk("%s: attempt to bring up CPU %u failed\n", 200 printk("%s: attempt to bring up CPU %u failed\n",
@@ -185,6 +217,7 @@ out_notify:
185 if (ret != 0) 217 if (ret != 0)
186 notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu); 218 notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu);
187out: 219out:
220 current->flags &= ~PF_HOTPLUG_CPU;
188 up(&cpucontrol); 221 up(&cpucontrol);
189 return ret; 222 return ret;
190} 223}