CPU hotplug error handling fixes take2
Subsystems (3)
cover CPU hotplug error handling fixes take2
This series of patches fixes the error handling for cpu hotplug. The problem is revealed by CPU hotplug/unplug test with fault-injection.
The changes from previous patchset:
- Removed the patch titled sysfs: avoid kmem_cache_free(NULL) Because it was merged into mainline.
- Removed the patch titled sysdev: add error check in sysdev_register() It turned out that the patch needs more fixes for microcode and cpufreq. So I'll separate them from this series of patches.
- Added slab fix noticed by Gautham R Shenoy
The series of patches:
[patch 1/9] slab: cleanup cpuup_callback()
[patch 2/9] slab: fix memory leak in cpu hotplug error path [patch 3/9] cpu: deliver CPU_UP_CANCELED only to NOTIFY_OKed callbacks
with CPU_UP_PREPARE
[patch 4/9] topology: remove topology_dev_map
[patch 5/9] thermal_throttle: fix cpu hotplug error handling [patch 6/9] msr: fix cpu hotplug error handling
[patch 7/9] cpuid: fix cpu hotplug error handling
[patch 8/9] mce: fix cpu hotplug error handling
[patch 9/9] intel_cacheinfo: fix cpu hotplug error handling
The patch 1/9 factors out some code from cpuup_callback() to simplify what the patch 2/9 does.
The patch 2/9 fixes memory leak in allocation failure path. Also it avoids the memory leak that will be introduced by the patch 3/9.
The patch 3/9 changes the behavior when one of the callbacks in notifier chain returns NOTIFY_BAD with CPU_UP_PREPARE event. This change makes cpu hotplug error handling simple.
The patch 4/9 simplifies the cpu hotplug event handling in topology.c enabled by the patch 3/9.
The patch 5-9/9 are cpu hotplug error handling fixes in drivers. Mainly these patches shift some callbacks from CPU_ONLINE to CPU_UP_PREPARE.
CPU hotplug/unplug test script:
----------[ cut here ]----------
#!/bin/bash
FAILTYPE=failslab
CPU=1 CPU_ONLINE=/sys/devices/system/cpu/cpu${CPU}/online
faulty_system()
{
bash -c "echo 1 > /proc/self/make-it-fail && exec $*"
}[ "$UID" == 0 ] || exit 1
[ -n "$FAILTYPE" -a -f /debug/$FAILTYPE/probability ] || exit 1 [ -f $CPU_ONLINE ] || exit 1
echo N > /debug/$FAILTYPE/ignore-gfp-wait
echo Y > /debug/$FAILTYPE/task-filter
echo 1 > /debug/$FAILTYPE/probability
echo -1 > /debug/$FAILTYPE/times
while true
do
faulty_system "echo 0 > $CPU_ONLINE"
faulty_system "echo 1 > $CPU_ONLINE"
done
PATCH 1/9 slab: cleanup cpuup_callback()
cpuup_callback() is too long.
This patch factors out CPU_UP_CANCELLED and CPU_UP_PREPARE handlings from cpuup_callback().
--- mm/slab.c | 301 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 159 insertions(+), 142 deletions(-)
mm/slab.c +159 -142
@@ -1156,105 +1156,181 @@ static inline int cache_free_alien(struc }#endif -static int __cpuinit cpuup_callback (struct notifier_block *nfb, -unsigned long action,void *hcpu) +static void __cpuinit cpuup_canceled (long cpu) +{ +struct kmem_cache *cachep; +struct kmem_list3 *l3 =NULL ; +int node =cpu_to_node (cpu); + +list_for_each_entry (cachep, &cache_chain, next) { +struct array_cache *nc; +struct array_cache *shared; +struct array_cache **alien; +cpumask_t mask; + + mask =node_to_cpumask (node); +/* cpu is dead; no one can alloc from it. */ + nc = cachep->array[cpu]; + cachep->array[cpu] =NULL ; + l3 = cachep->nodelists[node]; + +if (!l3) + goto free_array_cache; + +spin_lock_irq (&l3->list_lock); + +/* Free limit for this kmem_list3 */ + l3->free_limit -= cachep->batchcount; +if (nc) +free_block (cachep, nc->entry, nc->avail, node); + +if (!cpus_empty (mask)) { +spin_unlock_irq (&l3->list_lock); + goto free_array_cache; + } + + shared = l3->shared; +if (shared) { +free_block (cachep, shared->entry, + shared->avail, node); + l3->shared =NULL ; + } + + alien = l3->alien; + l3->alien =NULL ; + +spin_unlock_irq (&l3->list_lock); + +kfree (shared); +if (alien) { +drain_alien_cache (cachep, alien); +free_alien_cache (alien); + } +free_array_cache: +kfree (nc); + } +/* +* In the previous loop, all the objects were freed to +* the respective cache's slabs, now we can go ahead and +* shrink each nodelist to its limit. +*/ +list_for_each_entry (cachep, &cache_chain, next) { + l3 = cachep->nodelists[node]; +if (!l3) +continue ; +drain_freelist (cachep, l3, l3->free_objects); + } +} + +static int __cpuinit cpuup_prepare (long cpu) { -long cpu = (long )hcpu;struct kmem_cache *cachep;struct kmem_list3 *l3 =NULL ;int node =cpu_to_node (cpu);const int memsize =sizeof (struct kmem_list3 ); -switch (action) { -case CPU_LOCK_ACQUIRE : -mutex_lock (&cache_chain_mutex); -break ; -case CPU_UP_PREPARE : -case CPU_UP_PREPARE_FROZEN : +/* +* We need to do this right in the beginning since +* alloc_arraycache's are going to use this list. +* kmalloc_node allows us to add the slab to the right +* kmem_list3 and not this cpu's kmem_list3 +*/ + +list_for_each_entry (cachep, &cache_chain, next) {/* -* We need to do this right in the beginning since -* alloc_arraycache's are going to use this list. -* kmalloc_node allows us to add the slab to the right -* kmem_list3 and not this cpu's kmem_list3 +* Set up the size64 kmemlist for cpu before we can +* begin anything. Make sure some other cpu on this +* node has not already allocated this */ +if (!cachep->nodelists[node]) { + l3 =kmalloc_node (memsize,GFP_KERNEL , node); +if (!l3) + goto bad; +kmem_list3_init (l3); + l3->next_reap = jiffies +REAPTIMEOUT_LIST3 + + ((unsigned long )cachep) %REAPTIMEOUT_LIST3 ; -list_for_each_entry (cachep, &cache_chain, next) {/* -* Set up the size64 kmemlist for cpu before we can -* begin anything. Make sure some other cpu on this -* node has not already allocated this +* The l3s don't come and go as CPUs come and +* go. cache_chain_mutex is sufficient +* protection here. */ -if (!cachep->nodelists[node]) { - l3 =kmalloc_node (memsize,GFP_KERNEL , node); -if (!l3) - goto bad; -kmem_list3_init (l3); - l3->next_reap = jiffies +REAPTIMEOUT_LIST3 + - ((unsigned long )cachep) %REAPTIMEOUT_LIST3 ; - -/* -* The l3s don't come and go as CPUs come and -* go. cache_chain_mutex is sufficient -* protection here. -*/ - cachep->nodelists[node] = l3; - } - -spin_lock_irq (&cachep->nodelists[node]->list_lock); - cachep->nodelists[node]->free_limit = - (1 +nr_cpus_node (node)) * - cachep->batchcount + cachep->num; -spin_unlock_irq (&cachep->nodelists[node]->list_lock); + cachep->nodelists[node] = l3; } -/* -* Now we can go ahead with allocating the shared arrays and -* array caches -*/ -list_for_each_entry (cachep, &cache_chain, next) { -struct array_cache *nc; -struct array_cache *shared =NULL ; -struct array_cache **alien =NULL ; +spin_lock_irq (&cachep->nodelists[node]->list_lock); + cachep->nodelists[node]->free_limit = + (1 +nr_cpus_node (node)) * + cachep->batchcount + cachep->num; +spin_unlock_irq (&cachep->nodelists[node]->list_lock); + } - nc =alloc_arraycache (node, cachep->limit, - cachep->batchcount); -if (!nc) +/* +* Now we can go ahead with allocating the shared arrays and +* array caches +*/ +list_for_each_entry (cachep, &cache_chain, next) { +struct array_cache *nc; +struct array_cache *shared =NULL ; +struct array_cache **alien =NULL ; + + nc =alloc_arraycache (node, cachep->limit, + cachep->batchcount); +if (!nc) + goto bad; +if (cachep->shared) { + shared =alloc_arraycache (node, + cachep->shared * cachep->batchcount, +0xbaadf00d ); +if (!shared) goto bad; -if (cachep->shared) { - shared =alloc_arraycache (node, - cachep->shared * cachep->batchcount, -0xbaadf00d ); -if (!shared) - goto bad; - } -if (use_alien_caches) { - alien =alloc_alien_cache (node, cachep->limit); -if (!alien) - goto bad; - } - cachep->array[cpu] = nc; - l3 = cachep->nodelists[node]; -BUG_ON (!l3); + } +if (use_alien_caches) { + alien =alloc_alien_cache (node, cachep->limit); +if (!alien) + goto bad; + } + cachep->array[cpu] = nc; + l3 = cachep->nodelists[node]; +BUG_ON (!l3); -spin_lock_irq (&l3->list_lock); -if (!l3->shared) { -/* -* We are serialised from CPU_DEAD or -* CPU_UP_CANCELLED by the cpucontrol lock -*/ - l3->shared = shared; - shared =NULL ; - } +spin_lock_irq (&l3->list_lock); +if (!l3->shared) { +/* +* We are serialised from CPU_DEAD or +* CPU_UP_CANCELLED by the cpucontrol lock +*/ + l3->shared = shared; + shared =NULL ; + }#ifdef CONFIG_NUMA -if (!l3->alien) { - l3->alien = alien; - alien =NULL ; - } -#endif -spin_unlock_irq (&l3->list_lock); -kfree (shared); -free_alien_cache (alien); +if (!l3->alien) { + l3->alien = alien; + alien =NULL ; } +#endif +spin_unlock_irq (&l3->list_lock); +kfree (shared); +free_alien_cache (alien); + } +return 0 ; +bad: +return -ENOMEM ; +} + +static int __cpuinit cpuup_callback (struct notifier_block *nfb, +unsigned long action,void *hcpu) +{ +long cpu = (long )hcpu; +int err =0 ; + +switch (action) { +case CPU_LOCK_ACQUIRE : +mutex_lock (&cache_chain_mutex); +break ; +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN : + err =cpuup_prepare (cpu);break ;case CPU_ONLINE :case CPU_ONLINE_FROZEN :
@@ -1291,72 +1367,13 @@ static int __cpuinit cpuup_callback(stru#endif case CPU_UP_CANCELED :case CPU_UP_CANCELED_FROZEN : -list_for_each_entry (cachep, &cache_chain, next) { -struct array_cache *nc; -struct array_cache *shared; -struct array_cache **alien; -cpumask_t mask; - - mask =node_to_cpumask (node); -/* cpu is dead; no one can alloc from it. */ - nc = cachep->array[cpu]; - cachep->array[cpu] =NULL ; - l3 = cachep->nodelists[node]; - -if (!l3) - goto free_array_cache; - -spin_lock_irq (&l3->list_lock); - -/* Free limit for this kmem_list3 */ - l3->free_limit -= cachep->batchcount; -if (nc) -free_block (cachep, nc->entry, nc->avail, node); - -if (!cpus_empty (mask)) { -spin_unlock_irq (&l3->list_lock); - goto free_array_cache; - } - - shared = l3->shared; -if (shared) { -free_block (cachep, shared->entry, - shared->avail, node); - l3->shared =NULL ; - } - - alien = l3->alien; - l3->alien =NULL ; - -spin_unlock_irq (&l3->list_lock); - -kfree (shared); -if (alien) { -drain_alien_cache (cachep, alien); -free_alien_cache (alien); - } -free_array_cache: -kfree (nc); - } -/* -* In the previous loop, all the objects were freed to -* the respective cache's slabs, now we can go ahead and -* shrink each nodelist to its limit. -*/ -list_for_each_entry (cachep, &cache_chain, next) { - l3 = cachep->nodelists[node]; -if (!l3) -continue ; -drain_freelist (cachep, l3, l3->free_objects); - } +cpuup_canceled (cpu);break ;case CPU_LOCK_RELEASE :mutex_unlock (&cache_chain_mutex);break ; } -return NOTIFY_OK ; -bad: -return NOTIFY_BAD ; +return err ?NOTIFY_BAD :NOTIFY_OK ; }static struct notifier_block __cpuinitdata cpucache_notifier = {
PATCH 2/9 slab: fix memory leak in cpu hotplug error path
In reply to Akinobu MitaThis patch fixes memory leak in error path.
In reality, we don't need to call cpuup_canceled(cpu) for now. But upcoming cpu hotplug error handling change needs this.
--- mm/slab.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
mm/slab.c +8 -2
@@ -1282,13 +1282,18 @@ static int __cpuinit cpuup_prepare(long shared =alloc_arraycache (node, cachep->shared * cachep->batchcount,0xbaadf00d ); -if (!shared) +if (!shared) { +kfree (nc); goto bad; + } }if (use_alien_caches) { alien =alloc_alien_cache (node, cachep->limit); -if (!alien) +if (!alien) { +kfree (shared); +kfree (nc); goto bad; + } } cachep->array[cpu] = nc; l3 = cachep->nodelists[node];
@@ -1315,6 +1320,7 @@ static int __cpuinit cpuup_prepare(long }return 0 ; bad: +cpuup_canceled (cpu);return -ENOMEM ; }
Re: [patch 2/9] slab: fix memory leak in cpu hotplug error path
Quoted 4 lines · Akinobu Mita
This patch fixes memory leak in error path.
In reality, we don't need to call
cpuup_canceled(cpu)for now. But upcoming cpu hotplug error handling change needs this.
This and the previous patch look good to me.
PATCH 3/9 cpu: deliver CPU_UP_CANCELED only to NOTIFY_OKed callbacks with CPU_UP_PREPARE
In reply to Akinobu MitaFrom: Akinobu Mita <[email protected]>
The functions in a CPU notifier chain is called with CPU_UP_PREPARE event before making the CPU online. If one of the callback returns NOTIFY_BAD, it stops to deliver CPU_UP_PREPARE event, and CPU online operation is canceled. Then CPU_UP_CANCELED event is delivered to the functions in a CPU notifier chain again.
This CPU_UP_CANCELED event is delivered to the functions which have been called with CPU_UP_PREPARE, not delivered to the functions which haven't been called with CPU_UP_PREPARE.
The problem that makes existing cpu hotplug error handlings complex is that the CPU_UP_CANCELED event is delivered to the function that has returned NOTIFY_BAD, too.
Usually we don't expect to call destructor function against the object that has failed to initialize. It is like:
err = register_something();
if (err) {
unregister_something();
return err;
}So it is natural to deliver CPU_UP_CANCELED event only to the functions that have returned NOTIFY_OK with CPU_UP_PREPARE event and not to call the function that have returned NOTIFY_BAD. This is what this patch is doing.
Otherwise, every cpu hotplug notifiler has to track whether notifiler event is failed or not for each cpu. (drivers/base/topology.c is doing this with topology_dev_map)
Similary this patch makes same thing with CPU_DOWN_PREPARE and CPU_DOWN_FAILED evnets.
--- kernel/cpu.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/cpu.c +2 -0
@@ -150,6 +150,7 @@ static int _cpu_down(unsigned int cpu, i err =__raw_notifier_call_chain (&cpu_chain,CPU_DOWN_PREPARE | mod, hcpu,-1 , &nr_calls);if (err ==NOTIFY_BAD ) { + nr_calls--;__raw_notifier_call_chain (&cpu_chain,CPU_DOWN_FAILED | mod, hcpu, nr_calls,NULL ); printk("%s: attempt to take down CPU %u failed\n",
@@ -233,6 +234,7 @@ static int __cpuinit _cpu_up(unsigned in ret =__raw_notifier_call_chain (&cpu_chain,CPU_UP_PREPARE | mod, hcpu,-1 , &nr_calls);if (ret ==NOTIFY_BAD ) { + nr_calls--;printk ("%s: attempt to bring up CPU %u failed\n" , __FUNCTION__, cpu); ret = -EINVAL ;
Re: [patch 3/9] cpu: deliver CPU_UP_CANCELED only to NOTIFY_OKed callbacks with CPU_UP_PREPARE
Quoted 34 lines · Akinobu Mita
plain text document attachment (cpuhotplug-nr-calls.patch)
From: Akinobu Mita <[email protected]>The functions in a CPU notifier chain is called with
CPU_UP_PREPAREevent before making the CPU online. If one of the callback returnsNOTIFY_BAD, it stops to deliverCPU_UP_PREPAREevent, and CPU online operation is canceled. ThenCPU_UP_CANCELEDevent is delivered to the functions in a CPU notifier chain again.This
CPU_UP_CANCELEDevent is delivered to the functions which have been called withCPU_UP_PREPARE, not delivered to the functions which haven't been called withCPU_UP_PREPARE.The problem that makes existing cpu hotplug error handlings complex is that the
CPU_UP_CANCELEDevent is delivered to the function that has returnedNOTIFY_BAD, too.Usually we don't expect to call destructor function against the object that has failed to initialize. It is like:
err = register_something(); if (err) { unregister_something(); return err; }So it is natural to deliver
CPU_UP_CANCELEDevent only to the functions that have returnedNOTIFY_OKwithCPU_UP_PREPAREevent and not to call the function that have returnedNOTIFY_BAD. This is what this patch is doing.Otherwise, every cpu hotplug notifiler has to track whether notifiler event is failed or not for each cpu. (
drivers/base/topology.cis doing this withtopology_dev_map)
Quoted 5 lines
Similary this patch makes same thing with
CPU_DOWN_PREPAREandCPU_DOWN_FAILEDevnets.Cc Rusty Russell <[email protected]>Signed-off-by Akinobu Mita <[email protected]>
This makes a great deal of sense; I consider it a bugfix.
Thanks!
Rusty.
PATCH 4/9 topology: remove topology_dev_map
In reply to Akinobu MitaBy previous cpu hotplug notifier change, we don't need to track topology_dev existence for each cpu by topology_dev_map.
--- drivers/base/topology.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
drivers/base/topology.c +1 -10
@@ -94,27 +94,18 @@ static struct attribute_group topology_a .name = "topology" }; -static cpumask_t topology_dev_map =CPU_MASK_NONE ; -/* Add/Remove cpu_topology interface for CPU device */ static int __cpuinit topology_add_dev (unsigned int cpu) { -int rc;struct sys_device *sys_dev =get_cpu_sysdev (cpu); - rc =sysfs_create_group (&sys_dev->kobj, &topology_attr_group); -if (!rc) -cpu_set (cpu, topology_dev_map); -return rc; +return sysfs_create_group (&sys_dev->kobj, &topology_attr_group); }static void __cpuinit topology_remove_dev (unsigned int cpu) {struct sys_device *sys_dev =get_cpu_sysdev (cpu); -if (!cpu_isset (cpu, topology_dev_map)) -return ; -cpu_clear (cpu, topology_dev_map);sysfs_remove_group (&sys_dev->kobj, &topology_attr_group); }
Re: [patch 4/9] topology: remove topology_dev_map
Quoted 5 lines · Akinobu Mita
By previous cpu hotplug notifier change, we don't need to track
topology_devexistence for each cpu bytopology_dev_map.Cc Greg Kroah-Hartman <[email protected]>Signed-off-by Akinobu Mita <[email protected]>
I'm guessing that this is going to have to go in through some other tree than mine, so feel free to add:
to it.
thanks,
greg k-h
PATCH 5/9 thermal_throttle: fix cpu hotplug error handling
In reply to Akinobu MitaDo thermal_throttle_add_dev() in CPU_UP_PREPARE instead of CPU_ONLINE.
--- arch/i386/kernel/cpu/mcheck/therm_throt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/i386/kernel/cpu/mcheck/therm_throt.c +6 -4
@@ -131,17 +131,19 @@ static __cpuinit int thermal_throttle_cp {unsigned int cpu = (unsigned long )hcpu;struct sys_device *sys_dev; -int err; +int err =0 ; sys_dev =get_cpu_sysdev (cpu);switch (action) { -case CPU_ONLINE : -case CPU_ONLINE_FROZEN : +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN :mutex_lock (&therm_cpu_lock); err =thermal_throttle_add_dev (sys_dev);mutex_unlock (&therm_cpu_lock);WARN_ON (err);break ; +case CPU_UP_CANCELED : +case CPU_UP_CANCELED_FROZEN :case CPU_DEAD :case CPU_DEAD_FROZEN :mutex_lock (&therm_cpu_lock);
@@ -149,7 +151,7 @@ static __cpuinit int thermal_throttle_cpmutex_unlock (&therm_cpu_lock);break ; } -return NOTIFY_OK ; +return err ?NOTIFY_BAD :NOTIFY_OK ; } static struct notifier_block thermal_throttle_cpu_notifier =
PATCH 6/9 msr: fix cpu hotplug error handling
In reply to Akinobu MitaDo msr_device_create() in CPU_UP_PREPARE instead of CPU_ONLINE.
--- arch/i386/kernel/msr.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-)
arch/i386/kernel/msr.c +19 -13
@@ -135,33 +135,39 @@ static const struct file_operations msr_ .open = msr_open, }; -static int msr_device_create (int i) +static int msr_device_create (int cpu) { -int err =0 ;struct device *dev; - dev =device_create (msr_class,NULL ,MKDEV (MSR_MAJOR , i),"msr%d" ,i); -if (IS_ERR (dev)) - err =PTR_ERR (dev); -return err; + dev =device_create (msr_class,NULL ,MKDEV (MSR_MAJOR , cpu), +"msr%d" , cpu); +return IS_ERR (dev) ?PTR_ERR (dev) :0 ; +} + +static void msr_device_destroy (int cpu) +{ +device_destroy (msr_class,MKDEV (MSR_MAJOR , cpu)); }static int msr_class_cpu_callback (struct notifier_block *nfb,unsigned long action,void *hcpu) {unsigned int cpu = (unsigned long )hcpu; +int err =0 ;switch (action) { -case CPU_ONLINE : -case CPU_ONLINE_FROZEN : -msr_device_create (cpu); +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN : + err =msr_device_create (cpu);break ; +case CPU_UP_CANCELED : +case CPU_UP_CANCELED_FROZEN :case CPU_DEAD :case CPU_DEAD_FROZEN : -device_destroy (msr_class,MKDEV (MSR_MAJOR , cpu)); +msr_device_destroy (cpu);break ; } -return NOTIFY_OK ; +return err ?NOTIFY_BAD :NOTIFY_OK ; } static struct notifier_block __cpuinitdata msr_class_cpu_notifier =
@@ -198,7 +204,7 @@ static int __init msr_init(void) out_class: i =0 ;for_each_online_cpu (i) -device_destroy (msr_class,MKDEV (MSR_MAJOR , i)); +msr_device_destroy (i);class_destroy (msr_class); out_chrdev:unregister_chrdev (MSR_MAJOR ,"cpu/msr" );
@@ -210,7 +216,7 @@ static void __exit msr_exit(void) {int cpu =0 ;for_each_online_cpu (cpu) -device_destroy (msr_class,MKDEV (MSR_MAJOR , cpu)); +msr_device_destroy (cpu);class_destroy (msr_class);unregister_chrdev (MSR_MAJOR ,"cpu/msr" );unregister_hotcpu_notifier (&msr_class_cpu_notifier);
PATCH 7/9 cpuid: fix cpu hotplug error handling
In reply to Akinobu MitaDo cpuid_device_create() in CPU_UP_PREPARE instead of CPU_ONLINE.
--- arch/i386/kernel/cpuid.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-)
arch/i386/kernel/cpuid.c +19 -13
@@ -152,32 +152,38 @@ static const struct file_operations cpui .open = cpuid_open, }; -static int cpuid_device_create (int i) +static int cpuid_device_create (int cpu) { -int err =0 ;struct device *dev; - dev =device_create (cpuid_class,NULL ,MKDEV (CPUID_MAJOR , i),"cpu%d" ,i); -if (IS_ERR (dev)) - err =PTR_ERR (dev); -return err; + dev =device_create (cpuid_class,NULL ,MKDEV (CPUID_MAJOR , cpu), +"cpu%d" , cpu); +return IS_ERR (dev) ?PTR_ERR (dev) :0 ; +} + +static void cpuid_device_destroy (int cpu) +{ +device_destroy (cpuid_class,MKDEV (CPUID_MAJOR , cpu)); }static int cpuid_class_cpu_callback (struct notifier_block *nfb,unsigned long action,void *hcpu) {unsigned int cpu = (unsigned long )hcpu; +int err =0 ;switch (action) { -case CPU_ONLINE : -case CPU_ONLINE_FROZEN : -cpuid_device_create (cpu); +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN : + err =cpuid_device_create (cpu);break ; +case CPU_UP_CANCELED : +case CPU_UP_CANCELED_FROZEN :case CPU_DEAD :case CPU_DEAD_FROZEN : -device_destroy (cpuid_class,MKDEV (CPUID_MAJOR , cpu)); +cpuid_device_destroy (cpu);break ; } -return NOTIFY_OK ; +return err ?NOTIFY_BAD :NOTIFY_OK ; } static struct notifier_block __cpuinitdata cpuid_class_cpu_notifier =
@@ -214,7 +220,7 @@ static int __init cpuid_init(void) out_class: i =0 ;for_each_online_cpu (i) { -device_destroy (cpuid_class,MKDEV (CPUID_MAJOR , i)); +cpuid_device_destroy (i); }class_destroy (cpuid_class); out_chrdev:
@@ -228,7 +234,7 @@ static void __exit cpuid_exit(void)int cpu =0 ;for_each_online_cpu (cpu) -device_destroy (cpuid_class,MKDEV (CPUID_MAJOR , cpu)); +cpuid_device_destroy (cpu);class_destroy (cpuid_class);unregister_chrdev (CPUID_MAJOR ,"cpu/cpuid" );unregister_hotcpu_notifier (&cpuid_class_cpu_notifier);
PATCH 8/9 mce: fix cpu hotplug error handling
In reply to Akinobu Mita- Clear kobject in percpu device_mce before calling sysdev_register() with
Because mce_create_device() may fail and it leaves kobject filled with junk. It will be the problem when mce_create_device() will be called next time.
- Fix error handling in mce_create_device()
Error handling should not do sysdev_remove_file() with not yet added attributes.
- Don't register hotcpu notifier when mce_create_device() returns error
- Do mce_create_device() in CPU_UP_PREPARE instead of CPU_ONLINE
--- arch/x86_64/kernel/mce.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-)
arch/x86_64/kernel/mce.c +29 -10
@@ -690,16 +690,29 @@ static __cpuinit int mce_create_device(uif (!mce_available (&cpu_data[cpu]))return -EIO ; +memset (&per_cpu (device_mce, cpu).kobj,0 ,sizeof (struct kobject ));per_cpu (device_mce,cpu).id = cpu;per_cpu (device_mce,cpu).cls = &mce_sysclass; err =sysdev_register (&per_cpu (device_mce,cpu)); +if (err) +return err; + +for (i =0 ; mce_attributes[i]; i++) { + err =sysdev_create_file (&per_cpu (device_mce,cpu), + mce_attributes[i]); +if (err) + goto error; + } -if (!err) { -for (i =0 ; mce_attributes[i]; i++) -sysdev_create_file (&per_cpu (device_mce,cpu), - mce_attributes[i]); +return 0 ; +error: +while (i--) { +sysdev_remove_file (&per_cpu (device_mce,cpu), + mce_attributes[i]); } +sysdev_unregister (&per_cpu (device_mce,cpu)); +return err; }
@@ -711,7 +724,6 @@ static void mce_remove_device(unsigned isysdev_remove_file (&per_cpu (device_mce,cpu), mce_attributes[i]);sysdev_unregister (&per_cpu (device_mce,cpu)); -memset (&per_cpu (device_mce, cpu).kobj,0 ,sizeof (struct kobject )); }/* Get notified when a cpu comes on/off. Be hotplug friendly. */
@@ -719,18 +731,21 @@ static int mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; +int err =0 ;switch (action) { -case CPU_ONLINE : -case CPU_ONLINE_FROZEN : -mce_create_device (cpu); +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN : + err =mce_create_device (cpu);break ; +case CPU_UP_CANCELED : +case CPU_UP_CANCELED_FROZEN :case CPU_DEAD :case CPU_DEAD_FROZEN :mce_remove_device (cpu);break ; } -return NOTIFY_OK ; +return err ?NOTIFY_BAD :NOTIFY_OK ; } static struct notifier_block mce_cpu_notifier = {
@@ -745,9 +760,13 @@ static __init int mce_init_device(void)if (!mce_available (&boot_cpu_data))return -EIO ; err =sysdev_class_register (&mce_sysclass); +if (err) +return err;for_each_online_cpu (i) { -mce_create_device (i); + err =mce_create_device (i); +if (err) +return err; }register_hotcpu_notifier (&mce_cpu_notifier);
PATCH 9/9 intel_cacheinfo: fix cpu hotplug error handling
In reply to Akinobu Mita- Fix resource leakage in error case within detect_cache_attributes()
- Don't register hotcpu notifier when cache_add_dev() returns error
- Introduce cache_dev_map cpumask to track whether cache interface for CPU is successfully added by cache_add_dev() or not.
cache_add_dev() may fail with out of memory error. In order to avoid cache_remove_dev() with that uninitialized cache interface when CPU_DEAD event is delivered we need to have the cache_dev_map cpumask.
(We cannot change cache_add_dev() from CPU_ONLINE event handler to CPU_UP_PREPARE event handler. Because cache_add_dev() needs to do cpuid and store the results with its CPU online.)
--- arch/i386/kernel/cpu/intel_cacheinfo.c | 64 +++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 19 deletions(-)
arch/i386/kernel/cpu/intel_cacheinfo.c +45 -19
@@ -466,6 +466,11 @@ static void __init cache_remove_shared_cstatic void free_cache_attributes (unsigned int cpu) { +int i; + +for (i =0 ; i < num_cache_leaves; i++) +cache_remove_shared_cpu_map (cpu, i); +kfree (cpuid4_info[cpu]); cpuid4_info[cpu] =NULL ; }
@@ -473,8 +478,8 @@ static void free_cache_attributes(unsignstatic int __cpuinit detect_cache_attributes (unsigned int cpu) {struct _cpuid4_info *this_leaf; -unsigned long j; -int retval; +unsigned long j; +int retval;cpumask_t oldmask;if (num_cache_leaves ==0 )
@@ -491,19 +496,26 @@ static int __cpuinit detect_cache_attrib goto out;/* Do cpuid and store the results */ - retval =0 ;for (j =0 ; j < num_cache_leaves; j++) { this_leaf =CPUID4_INFO_IDX (cpu, j); retval =cpuid4_cache_lookup (j, this_leaf); -if (unlikely (retval <0 )) +if (unlikely (retval <0 )) { +int i; + +for (i =0 ; i < j; i++) +cache_remove_shared_cpu_map (cpu, i);break ; + }cache_shared_cpu_map_setup (cpu, j); }set_cpus_allowed (current, oldmask); out: -if (retval) -free_cache_attributes (cpu); +if (retval) { +kfree (cpuid4_info[cpu]); + cpuid4_info[cpu] =NULL ; + } +return retval; }
@@ -647,13 +659,14 @@ static void cpuid4_cache_sysfs_exit(unsistatic int __cpuinit cpuid4_cache_sysfs_init (unsigned int cpu) { +int err;if (num_cache_leaves ==0 )return -ENOENT ; -detect_cache_attributes (cpu); -if (cpuid4_info[cpu] ==NULL ) -return -ENOENT ; + err =detect_cache_attributes (cpu); +if (err) +return err;/* Allocate all required memory */ cache_kobject[cpu] =kzalloc (sizeof (struct kobject ),GFP_KERNEL );
@@ -672,13 +685,15 @@ err_out:return -ENOMEM ; } +static cpumask_t cache_dev_map =CPU_MASK_NONE ; +/* Add/Remove cache interface for CPU device */ static int __cpuinit cache_add_dev (struct sys_device * sys_dev) {unsigned int cpu = sys_dev->id;unsigned long i, j;struct _index_kobject *this_object; -int retval =0 ; +int retval; retval =cpuid4_cache_sysfs_init (cpu);if (unlikely (retval <0 ))
@@ -688,6 +703,10 @@ static int __cpuinit cache_add_dev(struckobject_set_name (cache_kobject[cpu],"%s" ,"cache" ); cache_kobject[cpu]->ktype = &ktype_percpu_entry; retval =kobject_register (cache_kobject[cpu]); +if (retval <0 ) { +cpuid4_cache_sysfs_exit (cpu); +return retval; + }for (i =0 ; i < num_cache_leaves; i++) { this_object =INDEX_KOBJECT_PTR (cpu,i);
@@ -707,6 +726,9 @@ static int __cpuinit cache_add_dev(strucbreak ; } } +if (!retval) +cpu_set (cpu, cache_dev_map); +return retval; }
@@ -715,13 +737,14 @@ static void __cpuexit cache_remove_dev(sunsigned int cpu = sys_dev->id;unsigned long i; -for (i =0 ; i < num_cache_leaves; i++) { -cache_remove_shared_cpu_map (cpu, i); +if (!cpu_isset (cpu, cache_dev_map)) +return ; +cpu_clear (cpu, cache_dev_map); + +for (i =0 ; i < num_cache_leaves; i++)kobject_unregister (&(INDEX_KOBJECT_PTR (cpu,i)->kobj)); - }kobject_unregister (cache_kobject[cpu]);cpuid4_cache_sysfs_exit (cpu); -return ; } static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb,
@@ -746,7 +769,7 @@ static int __cpuinit cacheinfo_cpu_callbstatic struct notifier_block __cpuinitdata cacheinfo_cpu_notifier = { - .notifier_call = cacheinfo_cpu_callback, + .notifier_call = cacheinfo_cpu_callback, };static int __cpuinit cache_sysfs_init (void )
@@ -756,12 +779,15 @@ static int __cpuinit cache_sysfs_init(voif (num_cache_leaves ==0 )return 0 ; -register_hotcpu_notifier (&cacheinfo_cpu_notifier); -for_each_online_cpu (i) { -cacheinfo_cpu_callback (&cacheinfo_cpu_notifier,CPU_ONLINE , - (void *)(long )i); +int err; +struct sys_device *sys_dev =get_cpu_sysdev (i); + + err =cache_add_dev (sys_dev); +if (err) +return err; } +register_hotcpu_notifier (&cacheinfo_cpu_notifier);return 0 ; }