hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10109 ---------------------------------------- Add the actual NAPI part of the module. In LONG_RUNNING_WITH_INTERRUPTS status the handler enables threaded NAPI (dev_set_threaded) on every monitored net device and binds the NAPI threads with bind_napi_threaded(), deriving the CPU mask of each thread from the NUMA node of its queue IRQ: - none of the target task CPUs is in the node: the whole node mask is used - the node has CPUs the target tasks are not allowed to use: those CPUs are used - the target tasks may use every CPU of the node: the first dyn_threaded_corenum CPUs of the node are used, so that the network load is capped instead of creeping into the CPUs of the target application; a thread already bound to such a subset keeps its current mask With dyn_threaded_bindirq set, the queue IRQ is bound along with its NAPI thread, round-robin over the same mask. The CPUs used by the NAPI threads are recorded in napi_cpumask, to evaluate their load in the next patch. In NOT_RUNNING and FRAGMENTATION status the handler switches NAPI back to softirq mode, as the target tasks currently do not suffer from softirq activity. Co-developed-by: Yue Haibing <yuehaibing@huawei.com> Signed-off-by: Yue Haibing <yuehaibing@huawei.com> Assisted-by: opencode:glm-5.3 Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- net/dynthread/net_dynthread.c | 192 +++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/net/dynthread/net_dynthread.c b/net/dynthread/net_dynthread.c index 719ca0d563ed..04292fba5b6e 100644 --- a/net/dynthread/net_dynthread.c +++ b/net/dynthread/net_dynthread.c @@ -25,6 +25,7 @@ #include <linux/topology.h> #include <linux/irq.h> #include <linux/netdevice.h> +#include <linux/rtnetlink.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Dynamic threaded NAPI scheduler"); @@ -116,6 +117,54 @@ module_param(dyn_threaded_irq_cpucore_per, uint, 0644); MODULE_PARM_DESC(dyn_threaded_irq_cpucore_per, "percent of rx queues to keep per NUMA node, default 50"); +/* default number of cpus used by the napi threads, computed at init */ +static u32 dyn_threaded_corenum_default; + +static u32 dyn_threaded_corenum; + +static int dyn_threaded_corenum_set(const char *val, + const struct kernel_param *kp) +{ + u32 num; + int ret; + + ret = kstrtou32(val, 0, &num); + if (ret) + return ret; + /* a zero count would leave the napi threads without cpu */ + if (!num) + return -EINVAL; + + *(u32 *)kp->arg = num; + return 0; +} + +static int dyn_threaded_corenum_get(char *buffer, + const struct kernel_param *kp) +{ + return scnprintf(buffer, PAGE_SIZE, "%u\n", *(u32 *)kp->arg); +} + +static const struct kernel_param_ops dyn_threaded_corenum_ops = { + .set = dyn_threaded_corenum_set, + .get = dyn_threaded_corenum_get, +}; + +module_param_cb(dyn_threaded_corenum, &dyn_threaded_corenum_ops, + &dyn_threaded_corenum, 0644); +MODULE_PARM_DESC(dyn_threaded_corenum, + "number of cpus used by the napi threads, computed by default"); + +static u32 dyn_threaded_corenum_lower_limit = 4; +module_param(dyn_threaded_corenum_lower_limit, uint, 0644); +MODULE_PARM_DESC(dyn_threaded_corenum_lower_limit, + "lower limit of napi thread cpus, default 4"); + +static u32 dyn_threaded_corenum_upper_limit = 32; +module_param(dyn_threaded_corenum_upper_limit, uint, 0644); +MODULE_PARM_DESC(dyn_threaded_corenum_upper_limit, + "upper limit of napi thread cpus, default 32"); + /** * struct sched_netdev_queue_info - IRQ information of one RX queue * @irq: Linux IRQ number of the queue @@ -664,6 +713,103 @@ static void recover_irq_affinity(void) affinity_to_one_node = false; } +/* CPUs the napi threads of the monitored devices may run on */ +static struct cpumask napi_cpumask; + +/* bind_napi_threaded - bind the threaded NAPIs of @dev to CPUs + * + * The mask of each NAPI thread is derived from the NUMA node of its + * queue IRQ: + * + * - if none of the target task CPUs is in that node, the whole node + * mask is used + * - otherwise, if the node has CPUs the target tasks are not allowed + * to use, those CPUs are used + * - otherwise the first dyn_threaded_corenum CPUs of the node are + * used; if the thread is already bound to such a subset, its + * current mask is kept + * + * In the latter case, with dyn_threaded_bindirq set, the queue IRQ is + * bound along with its NAPI thread, round-robin over the same mask. + * The CPUs used by the threads are recorded in napi_cpumask for the + * load evaluation. + */ +static void bind_napi_threaded(struct net_device *dev) +{ + struct napi_struct *napi; + struct cpumask bind_mask, check_mask, napi_allowed_mask; + const struct cpumask *irq_mask, *node_mask; + u32 idx = 0; + + list_for_each_entry(napi, &dev->napi_list, dev_list) { + if (!napi->thread) + continue; + + cpumask_clear(&bind_mask); + + /* the node of the irq, cpu_online_mask if unknown */ + irq_mask = irq_get_effective_affinity_mask(napi->irq); + if (unlikely(!irq_mask) || cpumask_empty(irq_mask)) + node_mask = cpu_online_mask; + else + node_mask = cpu_cpu_mask(cpumask_first(irq_mask)); + + /* no target task cpu in this node: use the whole node */ + cpumask_and(&check_mask, node_mask, &target_cpumask); + if (cpumask_empty(&check_mask)) { + set_cpus_allowed_ptr(napi->thread, node_mask); + continue; + } + + /* preferred: the node's cpus without the target tasks' ones */ + cpumask_andnot(&napi_allowed_mask, node_mask, &target_cpumask); + if (!cpumask_empty(&napi_allowed_mask)) { + set_cpus_allowed_ptr(napi->thread, &napi_allowed_mask); + continue; + } + + /* no spare cpu in the node: fall back to the whole node, */ + cpumask_copy(&napi_allowed_mask, node_mask); + + /* already bound to a subset of the right size? keep it */ + if (cpumask_weight(napi->thread->cpus_ptr) == dyn_threaded_corenum && + cpumask_subset(napi->thread->cpus_ptr, &napi_allowed_mask)) { + cpumask_or(&napi_cpumask, &napi_cpumask, napi->thread->cpus_ptr); + continue; + } + + select_napi_cpus(dyn_threaded_corenum, &napi_allowed_mask, &bind_mask); + if (cpumask_empty(&bind_mask)) + continue; + + cpumask_or(&napi_cpumask, &napi_cpumask, &bind_mask); + set_cpus_allowed_ptr(napi->thread, &bind_mask); + if (dyn_threaded_bindirq) + bind_irq_in_cpumask(napi->irq, idx++, &bind_mask); + } +} + +static void set_all_napi_threaded(bool threaded) +{ + struct sched_netdev *sched_dev; + int id; + + /* napi_list, dev_set_threaded() and set_cpus_allowed_ptr() + * are all handled under the sleepable rtnl lock + */ + rtnl_lock(); + for (id = 0; id < sched_netdev_num; id++) { + sched_dev = get_sched_netdev(id); + if (!sched_dev) + continue; + + dev_set_threaded(sched_dev->netdev, threaded); + if (threaded) + bind_napi_threaded(sched_dev->netdev); + } + rtnl_unlock(); +} + static int dynthreaded; static struct work_struct dyn_threaded_work; @@ -696,15 +842,23 @@ static void dyn_threaded_handler(struct work_struct *work) info.avg_runtime = snapshot.avg_runtime; break; case NOT_RUNNING: - /* target tasks barely running: restore the original irqs */ + /* target tasks barely running: softirq mode is fine */ + set_all_napi_threaded(false); + /* and the original irq affinity can be restored */ recover_irq_affinity(); break; case FRAGMENTATION: - /* irqs may consume cpu time again, spread them over - * their NUMA node - */ + /* softirq mode is fine, irqs spread over their NUMA node */ + set_all_napi_threaded(false); bind_irq(); break; + case LONG_RUNNING_WITH_INTERRUPTS: + /* switch NAPI processing to threads bound away from + * the target task cpus + */ + cpumask_clear(&napi_cpumask); + set_all_napi_threaded(true); + break; default: break; } @@ -788,6 +942,33 @@ static const struct kernel_param_ops dynthreaded_ops = { module_param_cb(dynthreaded, &dynthreaded_ops, NULL, 0644); MODULE_PARM_DESC(dynthreaded, "enable the scheduler, default 0"); +/* set_default_dyn_threaded_corenum - compute the default napi thread cpu count + * + * The default is the number of RX queues of the busiest monitored device + * divided by the number of NUMA nodes, clamped between the core number + * lower and upper limits. + */ +static void set_default_dyn_threaded_corenum(void) +{ + struct sched_netdev *sched_dev; + unsigned int max_rxq = 0; + u32 num; + int id; + + for (id = 0; id < sched_netdev_num; id++) { + sched_dev = get_sched_netdev(id); + if (sched_dev) + max_rxq = max(max_rxq, sched_dev->netdev->real_num_rx_queues); + } + + num = max_rxq / num_online_nodes(); + if (num < dyn_threaded_corenum_lower_limit) + num = dyn_threaded_corenum_lower_limit; + if (num > dyn_threaded_corenum_upper_limit) + num = dyn_threaded_corenum_upper_limit; + dyn_threaded_corenum_default = num; +} + static int __init dynthread_init(void) { int ret; @@ -796,6 +977,9 @@ static int __init dynthread_init(void) if (ret) return ret; + set_default_dyn_threaded_corenum(); + if (!dyn_threaded_corenum) + dyn_threaded_corenum = dyn_threaded_corenum_default; return 0; } -- 2.18.0.huawei.25