From f0bf90def3528cebed45ebd81d9b5d0fa17d7422 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 3 Nov 2016 15:50:04 +0100 Subject: net/dev: Convert to hotplug state machine Install the callbacks via the state machine. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: netdev@vger.kernel.org Cc: "David S. Miller" Cc: rt@linutronix.de Link: http://lkml.kernel.org/r/20161103145021.28528-9-bigeasy@linutronix.de Signed-off-by: Thomas Gleixner --- net/core/dev.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 820bac239738..8e909b2a5f2f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7953,18 +7953,13 @@ out: } EXPORT_SYMBOL_GPL(dev_change_net_namespace); -static int dev_cpu_callback(struct notifier_block *nfb, - unsigned long action, - void *ocpu) +static int dev_cpu_dead(unsigned int oldcpu) { struct sk_buff **list_skb; struct sk_buff *skb; - unsigned int cpu, oldcpu = (unsigned long)ocpu; + unsigned int cpu; struct softnet_data *sd, *oldsd; - if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) - return NOTIFY_OK; - local_irq_disable(); cpu = smp_processor_id(); sd = &per_cpu(softnet_data, cpu); @@ -8014,10 +8009,9 @@ static int dev_cpu_callback(struct notifier_block *nfb, input_queue_head_incr(oldsd); } - return NOTIFY_OK; + return 0; } - /** * netdev_increment_features - increment feature set by one * @all: current feature set @@ -8351,7 +8345,9 @@ static int __init net_dev_init(void) open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action); - hotcpu_notifier(dev_cpu_callback, 0); + rc = cpuhp_setup_state_nocalls(CPUHP_NET_DEV_DEAD, "net/dev:dead", + NULL, dev_cpu_dead); + WARN_ON(rc < 0); dst_subsys_init(); rc = 0; out: -- cgit v1.2.3 From a4fc1bfc42062e8bc7b2271a90d17403b096ce5d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 3 Nov 2016 15:50:05 +0100 Subject: net/flowcache: Convert to hotplug state machine Install the callbacks via the state machine. Use multi state support to avoid custom list handling for the multiple instances. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: Steffen Klassert Cc: Herbert Xu Cc: netdev@vger.kernel.org Cc: rt@linutronix.de Cc: "David S. Miller" Link: http://lkml.kernel.org/r/20161103145021.28528-10-bigeasy@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/cpuhotplug.h | 1 + include/net/flow.h | 1 + include/net/flowcache.h | 2 +- net/core/flow.c | 60 ++++++++++++++++++++-------------------------- net/xfrm/xfrm_policy.c | 1 + 5 files changed, 30 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 394eb7ed53be..86b940f19df8 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -56,6 +56,7 @@ enum cpuhp_state { CPUHP_ARM_SHMOBILE_SCU_PREPARE, CPUHP_SH_SH3X_PREPARE, CPUHP_BLK_MQ_PREPARE, + CPUHP_NET_FLOW_PREPARE, CPUHP_TIMERS_DEAD, CPUHP_NOTF_ERR_INJ_PREPARE, CPUHP_MIPS_SOC_PREPARE, diff --git a/include/net/flow.h b/include/net/flow.h index 035aa7716967..2e386bd6ee63 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -239,6 +239,7 @@ struct flow_cache_object *flow_cache_lookup(struct net *net, void *ctx); int flow_cache_init(struct net *net); void flow_cache_fini(struct net *net); +void flow_cache_hp_init(void); void flow_cache_flush(struct net *net); void flow_cache_flush_deferred(struct net *net); diff --git a/include/net/flowcache.h b/include/net/flowcache.h index c8f665ec6e0d..9caf3bfc8d2d 100644 --- a/include/net/flowcache.h +++ b/include/net/flowcache.h @@ -17,7 +17,7 @@ struct flow_cache_percpu { struct flow_cache { u32 hash_shift; struct flow_cache_percpu __percpu *percpu; - struct notifier_block hotcpu_notifier; + struct hlist_node node; int low_watermark; int high_watermark; struct timer_list rnd_timer; diff --git a/net/core/flow.c b/net/core/flow.c index 3937b1b68d5b..841fd7f87b30 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -419,28 +419,20 @@ static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu) return 0; } -static int flow_cache_cpu(struct notifier_block *nfb, - unsigned long action, - void *hcpu) +static int flow_cache_cpu_up_prep(unsigned int cpu, struct hlist_node *node) { - struct flow_cache *fc = container_of(nfb, struct flow_cache, - hotcpu_notifier); - int res, cpu = (unsigned long) hcpu; + struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node); + + return flow_cache_cpu_prepare(fc, cpu); +} + +static int flow_cache_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node); struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - res = flow_cache_cpu_prepare(fc, cpu); - if (res) - return notifier_from_errno(res); - break; - case CPU_DEAD: - case CPU_DEAD_FROZEN: - __flow_cache_shrink(fc, fcp, 0); - break; - } - return NOTIFY_OK; + __flow_cache_shrink(fc, fcp, 0); + return 0; } int flow_cache_init(struct net *net) @@ -467,18 +459,8 @@ int flow_cache_init(struct net *net) if (!fc->percpu) return -ENOMEM; - cpu_notifier_register_begin(); - - for_each_online_cpu(i) { - if (flow_cache_cpu_prepare(fc, i)) - goto err; - } - fc->hotcpu_notifier = (struct notifier_block){ - .notifier_call = flow_cache_cpu, - }; - __register_hotcpu_notifier(&fc->hotcpu_notifier); - - cpu_notifier_register_done(); + if (cpuhp_state_add_instance(CPUHP_NET_FLOW_PREPARE, &fc->node)) + goto err; setup_timer(&fc->rnd_timer, flow_cache_new_hashrnd, (unsigned long) fc); @@ -494,8 +476,6 @@ err: fcp->hash_table = NULL; } - cpu_notifier_register_done(); - free_percpu(fc->percpu); fc->percpu = NULL; @@ -509,7 +489,8 @@ void flow_cache_fini(struct net *net) struct flow_cache *fc = &net->xfrm.flow_cache_global; del_timer_sync(&fc->rnd_timer); - unregister_hotcpu_notifier(&fc->hotcpu_notifier); + + cpuhp_state_remove_instance_nocalls(CPUHP_NET_FLOW_PREPARE, &fc->node); for_each_possible_cpu(i) { struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i); @@ -521,3 +502,14 @@ void flow_cache_fini(struct net *net) fc->percpu = NULL; } EXPORT_SYMBOL(flow_cache_fini); + +void __init flow_cache_hp_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_NET_FLOW_PREPARE, + "net/flow:prepare", + flow_cache_cpu_up_prep, + flow_cache_cpu_dead); + WARN_ON(ret < 0); +} diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index fd6986634e6f..4a8eff11bdad 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3111,6 +3111,7 @@ static struct pernet_operations __net_initdata xfrm_net_ops = { void __init xfrm_init(void) { + flow_cache_hp_init(); register_pernet_subsys(&xfrm_net_ops); seqcount_init(&xfrm_policy_hash_generation); xfrm_input_init(); -- cgit v1.2.3 From 38b482929e8f96dfe459d2ef757d0a5c3a74cea3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 17 Nov 2016 19:35:33 +0100 Subject: net/iucv: Convert to hotplug state machine Install the callbacks via the state machine and let the core invoke the callbacks on the already online CPUs. The smp function calls in the online/downprep callbacks are not required as the callback is guaranteed to be invoked on the upcoming/outgoing cpu. Signed-off-by: Sebastian Andrzej Siewior Cc: "David S. Miller" Cc: linux-s390@vger.kernel.org Cc: netdev@vger.kernel.org Cc: Ursula Braun Cc: rt@linuxtronix.de Link: http://lkml.kernel.org/r/20161117183541.8588-13-bigeasy@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/cpuhotplug.h | 1 + net/iucv/iucv.c | 118 +++++++++++++++++---------------------------- 2 files changed, 45 insertions(+), 74 deletions(-) (limited to 'net') diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 94c6a189421f..12bbcf3ded70 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -60,6 +60,7 @@ enum cpuhp_state { CPUHP_BLK_MQ_PREPARE, CPUHP_NET_FLOW_PREPARE, CPUHP_TOPOLOGY_PREPARE, + CPUHP_NET_IUCV_PREPARE, CPUHP_TIMERS_DEAD, CPUHP_NOTF_ERR_INJ_PREPARE, CPUHP_MIPS_SOC_PREPARE, diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 88a2a3ba4212..f0d6afc5d4a9 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -639,7 +639,7 @@ static void iucv_disable(void) put_online_cpus(); } -static void free_iucv_data(int cpu) +static int iucv_cpu_dead(unsigned int cpu) { kfree(iucv_param_irq[cpu]); iucv_param_irq[cpu] = NULL; @@ -647,9 +647,10 @@ static void free_iucv_data(int cpu) iucv_param[cpu] = NULL; kfree(iucv_irq_data[cpu]); iucv_irq_data[cpu] = NULL; + return 0; } -static int alloc_iucv_data(int cpu) +static int iucv_cpu_prepare(unsigned int cpu) { /* Note: GFP_DMA used to get memory below 2G */ iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data), @@ -671,58 +672,38 @@ static int alloc_iucv_data(int cpu) return 0; out_free: - free_iucv_data(cpu); + iucv_cpu_dead(cpu); return -ENOMEM; } -static int iucv_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) +static int iucv_cpu_online(unsigned int cpu) { - cpumask_t cpumask; - long cpu = (long) hcpu; - - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - if (alloc_iucv_data(cpu)) - return notifier_from_errno(-ENOMEM); - break; - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: - case CPU_DEAD: - case CPU_DEAD_FROZEN: - free_iucv_data(cpu); - break; - case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - case CPU_DOWN_FAILED: - case CPU_DOWN_FAILED_FROZEN: - if (!iucv_path_table) - break; - smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); - break; - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - if (!iucv_path_table) - break; - cpumask_copy(&cpumask, &iucv_buffer_cpumask); - cpumask_clear_cpu(cpu, &cpumask); - if (cpumask_empty(&cpumask)) - /* Can't offline last IUCV enabled cpu. */ - return notifier_from_errno(-EINVAL); - smp_call_function_single(cpu, iucv_retrieve_cpu, NULL, 1); - if (cpumask_empty(&iucv_irq_cpumask)) - smp_call_function_single( - cpumask_first(&iucv_buffer_cpumask), - iucv_allow_cpu, NULL, 1); - break; - } - return NOTIFY_OK; + if (!iucv_path_table) + return 0; + iucv_declare_cpu(NULL); + return 0; } -static struct notifier_block __refdata iucv_cpu_notifier = { - .notifier_call = iucv_cpu_notify, -}; +static int iucv_cpu_down_prep(unsigned int cpu) +{ + cpumask_t cpumask; + + if (!iucv_path_table) + return 0; + + cpumask_copy(&cpumask, &iucv_buffer_cpumask); + cpumask_clear_cpu(cpu, &cpumask); + if (cpumask_empty(&cpumask)) + /* Can't offline last IUCV enabled cpu. */ + return -EINVAL; + + iucv_retrieve_cpu(NULL); + if (!cpumask_empty(&iucv_irq_cpumask)) + return 0; + smp_call_function_single(cpumask_first(&iucv_buffer_cpumask), + iucv_allow_cpu, NULL, 1); + return 0; +} /** * iucv_sever_pathid @@ -2027,6 +2008,7 @@ struct iucv_interface iucv_if = { }; EXPORT_SYMBOL(iucv_if); +static enum cpuhp_state iucv_online; /** * iucv_init * @@ -2035,7 +2017,6 @@ EXPORT_SYMBOL(iucv_if); static int __init iucv_init(void) { int rc; - int cpu; if (!MACHINE_IS_VM) { rc = -EPROTONOSUPPORT; @@ -2054,23 +2035,19 @@ static int __init iucv_init(void) goto out_int; } - cpu_notifier_register_begin(); - - for_each_online_cpu(cpu) { - if (alloc_iucv_data(cpu)) { - rc = -ENOMEM; - goto out_free; - } - } - rc = __register_hotcpu_notifier(&iucv_cpu_notifier); + rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare", + iucv_cpu_prepare, iucv_cpu_dead); if (rc) goto out_free; - - cpu_notifier_register_done(); + rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online", + iucv_cpu_online, iucv_cpu_down_prep); + if (rc < 0) + goto out_free; + iucv_online = rc; rc = register_reboot_notifier(&iucv_reboot_notifier); if (rc) - goto out_cpu; + goto out_free; ASCEBC(iucv_error_no_listener, 16); ASCEBC(iucv_error_no_memory, 16); ASCEBC(iucv_error_pathid, 16); @@ -2084,14 +2061,10 @@ static int __init iucv_init(void) out_reboot: unregister_reboot_notifier(&iucv_reboot_notifier); -out_cpu: - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&iucv_cpu_notifier); out_free: - for_each_possible_cpu(cpu) - free_iucv_data(cpu); - - cpu_notifier_register_done(); + if (iucv_online) + cpuhp_remove_state(iucv_online); + cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE); root_device_unregister(iucv_root); out_int: @@ -2110,7 +2083,6 @@ out: static void __exit iucv_exit(void) { struct iucv_irq_list *p, *n; - int cpu; spin_lock_irq(&iucv_queue_lock); list_for_each_entry_safe(p, n, &iucv_task_queue, list) @@ -2119,11 +2091,9 @@ static void __exit iucv_exit(void) kfree(p); spin_unlock_irq(&iucv_queue_lock); unregister_reboot_notifier(&iucv_reboot_notifier); - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&iucv_cpu_notifier); - for_each_possible_cpu(cpu) - free_iucv_data(cpu); - cpu_notifier_register_done(); + + cpuhp_remove_state_nocalls(iucv_online); + cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE); root_device_unregister(iucv_root); bus_unregister(&iucv_bus); unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt); -- cgit v1.2.3 From 9c6bafab03dec222237b6eb8b5adf5c18ec76264 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 24 Nov 2016 17:10:13 +0100 Subject: net/iucv: Use explicit clean up labels in iucv_init() Ursula suggested to use explicit labels for clean up in the error path instead of one `out_free' label, which handles multiple exits, introduced in commit 38b482929e8f ("net/iucv: Convert to hotplug state machine"). Suggested-by: Ursula Braun Signed-off-by: Sebastian Andrzej Siewior Cc: linux-s390@vger.kernel.org Cc: netdev@vger.kernel.org Cc: rt@linutronix.de Cc: "David S. Miller" Link: http://lkml.kernel.org/r/20161124161013.dukr42y2nwscosk6@linutronix.de Signed-off-by: Thomas Gleixner --- net/iucv/iucv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index f0d6afc5d4a9..8f7ef167c45a 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -2038,16 +2038,16 @@ static int __init iucv_init(void) rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare", iucv_cpu_prepare, iucv_cpu_dead); if (rc) - goto out_free; + goto out_dev; rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online", iucv_cpu_online, iucv_cpu_down_prep); if (rc < 0) - goto out_free; + goto out_prep; iucv_online = rc; rc = register_reboot_notifier(&iucv_reboot_notifier); if (rc) - goto out_free; + goto out_remove_hp; ASCEBC(iucv_error_no_listener, 16); ASCEBC(iucv_error_no_memory, 16); ASCEBC(iucv_error_pathid, 16); @@ -2061,11 +2061,11 @@ static int __init iucv_init(void) out_reboot: unregister_reboot_notifier(&iucv_reboot_notifier); -out_free: - if (iucv_online) - cpuhp_remove_state(iucv_online); +out_remove_hp: + cpuhp_remove_state(iucv_online); +out_prep: cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE); - +out_dev: root_device_unregister(iucv_root); out_int: unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt); -- cgit v1.2.3