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

Configure Feed

Select the types of activity you want to include in your feed.

net: openvswitch: reduce cpu_used_mask memory

Use actual CPU number instead of hardcoded value to decide the size
of 'cpu_used_mask' in 'struct sw_flow'. Below is the reason.

'struct cpumask cpu_used_mask' is embedded in struct sw_flow.
Its size is hardcoded to CONFIG_NR_CPUS bits, which can be
8192 by default, it costs memory and slows down ovs_flow_alloc.

To address this:
Redefine cpu_used_mask to pointer.
Append cpumask_size() bytes after 'stat' to hold cpumask.
Initialization cpu_used_mask right after stats_last_writer.

APIs like cpumask_next and cpumask_set_cpu never access bits
beyond cpu count, cpumask_size() bytes of memory is enough.

Signed-off-by: Eddy Tao <taoyuan_eddy@hotmail.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Link: https://lore.kernel.org/r/OS3P286MB229570CCED618B20355D227AF5D59@OS3P286MB2295.JPNP286.PROD.OUTLOOK.COM
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eddy Tao and committed by
Jakub Kicinski
15ea59a0 bbe64186

+12 -7
+6 -3
net/openvswitch/flow.c
··· 107 107 108 108 rcu_assign_pointer(flow->stats[cpu], 109 109 new_stats); 110 - cpumask_set_cpu(cpu, &flow->cpu_used_mask); 110 + cpumask_set_cpu(cpu, 111 + flow->cpu_used_mask); 111 112 goto unlock; 112 113 } 113 114 } ··· 136 135 memset(ovs_stats, 0, sizeof(*ovs_stats)); 137 136 138 137 /* We open code this to make sure cpu 0 is always considered */ 139 - for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, &flow->cpu_used_mask)) { 138 + for (cpu = 0; cpu < nr_cpu_ids; 139 + cpu = cpumask_next(cpu, flow->cpu_used_mask)) { 140 140 struct sw_flow_stats *stats = rcu_dereference_ovsl(flow->stats[cpu]); 141 141 142 142 if (stats) { ··· 161 159 int cpu; 162 160 163 161 /* We open code this to make sure cpu 0 is always considered */ 164 - for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, &flow->cpu_used_mask)) { 162 + for (cpu = 0; cpu < nr_cpu_ids; 163 + cpu = cpumask_next(cpu, flow->cpu_used_mask)) { 165 164 struct sw_flow_stats *stats = ovsl_dereference(flow->stats[cpu]); 166 165 167 166 if (stats) {
+1 -1
net/openvswitch/flow.h
··· 229 229 */ 230 230 struct sw_flow_key key; 231 231 struct sw_flow_id id; 232 - struct cpumask cpu_used_mask; 232 + struct cpumask *cpu_used_mask; 233 233 struct sw_flow_mask *mask; 234 234 struct sw_flow_actions __rcu *sf_acts; 235 235 struct sw_flow_stats __rcu *stats[]; /* One for each CPU. First one
+5 -3
net/openvswitch/flow_table.c
··· 79 79 return ERR_PTR(-ENOMEM); 80 80 81 81 flow->stats_last_writer = -1; 82 + flow->cpu_used_mask = (struct cpumask *)&flow->stats[nr_cpu_ids]; 82 83 83 84 /* Initialize the default stat node. */ 84 85 stats = kmem_cache_alloc_node(flow_stats_cache, ··· 92 91 93 92 RCU_INIT_POINTER(flow->stats[0], stats); 94 93 95 - cpumask_set_cpu(0, &flow->cpu_used_mask); 94 + cpumask_set_cpu(0, flow->cpu_used_mask); 96 95 97 96 return flow; 98 97 err: ··· 116 115 flow->sf_acts); 117 116 /* We open code this to make sure cpu 0 is always considered */ 118 117 for (cpu = 0; cpu < nr_cpu_ids; 119 - cpu = cpumask_next(cpu, &flow->cpu_used_mask)) { 118 + cpu = cpumask_next(cpu, flow->cpu_used_mask)) { 120 119 if (flow->stats[cpu]) 121 120 kmem_cache_free(flow_stats_cache, 122 121 (struct sw_flow_stats __force *)flow->stats[cpu]); ··· 1197 1196 1198 1197 flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow) 1199 1198 + (nr_cpu_ids 1200 - * sizeof(struct sw_flow_stats *)), 1199 + * sizeof(struct sw_flow_stats *)) 1200 + + cpumask_size(), 1201 1201 0, 0, NULL); 1202 1202 if (flow_cache == NULL) 1203 1203 return -ENOMEM;