aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c49
-rw-r--r--net/core/net-procfs.c16
-rw-r--r--net/core/skbuff.c3
-rw-r--r--net/core/sysctl_net_core.c104
4 files changed, 167 insertions, 5 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index fc1e289397f5..7229bc30e509 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1629,7 +1629,6 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
1629 return NET_RX_DROP; 1629 return NET_RX_DROP;
1630 } 1630 }
1631 skb->skb_iif = 0; 1631 skb->skb_iif = 0;
1632 skb->dev = dev;
1633 skb_dst_drop(skb); 1632 skb_dst_drop(skb);
1634 skb->tstamp.tv64 = 0; 1633 skb->tstamp.tv64 = 0;
1635 skb->pkt_type = PACKET_HOST; 1634 skb->pkt_type = PACKET_HOST;
@@ -3065,6 +3064,46 @@ static int rps_ipi_queued(struct softnet_data *sd)
3065 return 0; 3064 return 0;
3066} 3065}
3067 3066
3067#ifdef CONFIG_NET_FLOW_LIMIT
3068int netdev_flow_limit_table_len __read_mostly = (1 << 12);
3069#endif
3070
3071static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen)
3072{
3073#ifdef CONFIG_NET_FLOW_LIMIT
3074 struct sd_flow_limit *fl;
3075 struct softnet_data *sd;
3076 unsigned int old_flow, new_flow;
3077
3078 if (qlen < (netdev_max_backlog >> 1))
3079 return false;
3080
3081 sd = &__get_cpu_var(softnet_data);
3082
3083 rcu_read_lock();
3084 fl = rcu_dereference(sd->flow_limit);
3085 if (fl) {
3086 new_flow = skb_get_rxhash(skb) & (fl->num_buckets - 1);
3087 old_flow = fl->history[fl->history_head];
3088 fl->history[fl->history_head] = new_flow;
3089
3090 fl->history_head++;
3091 fl->history_head &= FLOW_LIMIT_HISTORY - 1;
3092
3093 if (likely(fl->buckets[old_flow]))
3094 fl->buckets[old_flow]--;
3095
3096 if (++fl->buckets[new_flow] > (FLOW_LIMIT_HISTORY >> 1)) {
3097 fl->count++;
3098 rcu_read_unlock();
3099 return true;
3100 }
3101 }
3102 rcu_read_unlock();
3103#endif
3104 return false;
3105}
3106
3068/* 3107/*
3069 * enqueue_to_backlog is called to queue an skb to a per CPU backlog 3108 * enqueue_to_backlog is called to queue an skb to a per CPU backlog
3070 * queue (may be a remote CPU queue). 3109 * queue (may be a remote CPU queue).
@@ -3074,13 +3113,15 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
3074{ 3113{
3075 struct softnet_data *sd; 3114 struct softnet_data *sd;
3076 unsigned long flags; 3115 unsigned long flags;
3116 unsigned int qlen;
3077 3117
3078 sd = &per_cpu(softnet_data, cpu); 3118 sd = &per_cpu(softnet_data, cpu);
3079 3119
3080 local_irq_save(flags); 3120 local_irq_save(flags);
3081 3121
3082 rps_lock(sd); 3122 rps_lock(sd);
3083 if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) { 3123 qlen = skb_queue_len(&sd->input_pkt_queue);
3124 if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
3084 if (skb_queue_len(&sd->input_pkt_queue)) { 3125 if (skb_queue_len(&sd->input_pkt_queue)) {
3085enqueue: 3126enqueue:
3086 __skb_queue_tail(&sd->input_pkt_queue, skb); 3127 __skb_queue_tail(&sd->input_pkt_queue, skb);
@@ -6270,6 +6311,10 @@ static int __init net_dev_init(void)
6270 sd->backlog.weight = weight_p; 6311 sd->backlog.weight = weight_p;
6271 sd->backlog.gro_list = NULL; 6312 sd->backlog.gro_list = NULL;
6272 sd->backlog.gro_count = 0; 6313 sd->backlog.gro_count = 0;
6314
6315#ifdef CONFIG_NET_FLOW_LIMIT
6316 sd->flow_limit = NULL;
6317#endif
6273 } 6318 }
6274 6319
6275 dev_boot_phase = 0; 6320 dev_boot_phase = 0;
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 569d355fec3e..2bf83299600a 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -146,11 +146,23 @@ static void softnet_seq_stop(struct seq_file *seq, void *v)
146static int softnet_seq_show(struct seq_file *seq, void *v) 146static int softnet_seq_show(struct seq_file *seq, void *v)
147{ 147{
148 struct softnet_data *sd = v; 148 struct softnet_data *sd = v;
149 unsigned int flow_limit_count = 0;
149 150
150 seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", 151#ifdef CONFIG_NET_FLOW_LIMIT
152 struct sd_flow_limit *fl;
153
154 rcu_read_lock();
155 fl = rcu_dereference(sd->flow_limit);
156 if (fl)
157 flow_limit_count = fl->count;
158 rcu_read_unlock();
159#endif
160
161 seq_printf(seq,
162 "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
151 sd->processed, sd->dropped, sd->time_squeeze, 0, 163 sd->processed, sd->dropped, sd->time_squeeze, 0,
152 0, 0, 0, 0, /* was fastroute */ 164 0, 0, 0, 0, /* was fastroute */
153 sd->cpu_collision, sd->received_rps); 165 sd->cpu_collision, sd->received_rps, flow_limit_count);
154 return 0; 166 return 0;
155} 167}
156 168
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index af9185d0be6a..d6298914f4e7 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2853,7 +2853,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2853 doffset + tnl_hlen); 2853 doffset + tnl_hlen);
2854 2854
2855 if (fskb != skb_shinfo(skb)->frag_list) 2855 if (fskb != skb_shinfo(skb)->frag_list)
2856 continue; 2856 goto perform_csum_check;
2857 2857
2858 if (!sg) { 2858 if (!sg) {
2859 nskb->ip_summed = CHECKSUM_NONE; 2859 nskb->ip_summed = CHECKSUM_NONE;
@@ -2917,6 +2917,7 @@ skip_fraglist:
2917 nskb->len += nskb->data_len; 2917 nskb->len += nskb->data_len;
2918 nskb->truesize += nskb->data_len; 2918 nskb->truesize += nskb->data_len;
2919 2919
2920perform_csum_check:
2920 if (!csum) { 2921 if (!csum) {
2921 nskb->csum = skb_checksum(nskb, doffset, 2922 nskb->csum = skb_checksum(nskb, doffset,
2922 nskb->len - doffset, 0); 2923 nskb->len - doffset, 0);
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index cfdb46ab3a7f..741db5fc7806 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -87,6 +87,96 @@ static int rps_sock_flow_sysctl(ctl_table *table, int write,
87} 87}
88#endif /* CONFIG_RPS */ 88#endif /* CONFIG_RPS */
89 89
90#ifdef CONFIG_NET_FLOW_LIMIT
91static DEFINE_MUTEX(flow_limit_update_mutex);
92
93static int flow_limit_cpu_sysctl(ctl_table *table, int write,
94 void __user *buffer, size_t *lenp,
95 loff_t *ppos)
96{
97 struct sd_flow_limit *cur;
98 struct softnet_data *sd;
99 cpumask_var_t mask;
100 int i, len, ret = 0;
101
102 if (!alloc_cpumask_var(&mask, GFP_KERNEL))
103 return -ENOMEM;
104
105 if (write) {
106 ret = cpumask_parse_user(buffer, *lenp, mask);
107 if (ret)
108 goto done;
109
110 mutex_lock(&flow_limit_update_mutex);
111 len = sizeof(*cur) + netdev_flow_limit_table_len;
112 for_each_possible_cpu(i) {
113 sd = &per_cpu(softnet_data, i);
114 cur = rcu_dereference_protected(sd->flow_limit,
115 lockdep_is_held(&flow_limit_update_mutex));
116 if (cur && !cpumask_test_cpu(i, mask)) {
117 RCU_INIT_POINTER(sd->flow_limit, NULL);
118 synchronize_rcu();
119 kfree(cur);
120 } else if (!cur && cpumask_test_cpu(i, mask)) {
121 cur = kzalloc(len, GFP_KERNEL);
122 if (!cur) {
123 /* not unwinding previous changes */
124 ret = -ENOMEM;
125 goto write_unlock;
126 }
127 cur->num_buckets = netdev_flow_limit_table_len;
128 rcu_assign_pointer(sd->flow_limit, cur);
129 }
130 }
131write_unlock:
132 mutex_unlock(&flow_limit_update_mutex);
133 } else {
134 if (*ppos || !*lenp) {
135 *lenp = 0;
136 goto done;
137 }
138
139 cpumask_clear(mask);
140 rcu_read_lock();
141 for_each_possible_cpu(i) {
142 sd = &per_cpu(softnet_data, i);
143 if (rcu_dereference(sd->flow_limit))
144 cpumask_set_cpu(i, mask);
145 }
146 rcu_read_unlock();
147
148 len = cpumask_scnprintf(buffer, *lenp, mask);
149 *lenp = len + 1;
150 *ppos += len + 1;
151 }
152
153done:
154 free_cpumask_var(mask);
155 return ret;
156}
157
158static int flow_limit_table_len_sysctl(ctl_table *table, int write,
159 void __user *buffer, size_t *lenp,
160 loff_t *ppos)
161{
162 unsigned int old, *ptr;
163 int ret;
164
165 mutex_lock(&flow_limit_update_mutex);
166
167 ptr = table->data;
168 old = *ptr;
169 ret = proc_dointvec(table, write, buffer, lenp, ppos);
170 if (!ret && write && !is_power_of_2(*ptr)) {
171 *ptr = old;
172 ret = -EINVAL;
173 }
174
175 mutex_unlock(&flow_limit_update_mutex);
176 return ret;
177}
178#endif /* CONFIG_NET_FLOW_LIMIT */
179
90static struct ctl_table net_core_table[] = { 180static struct ctl_table net_core_table[] = {
91#ifdef CONFIG_NET 181#ifdef CONFIG_NET
92 { 182 {
@@ -180,6 +270,20 @@ static struct ctl_table net_core_table[] = {
180 .proc_handler = rps_sock_flow_sysctl 270 .proc_handler = rps_sock_flow_sysctl
181 }, 271 },
182#endif 272#endif
273#ifdef CONFIG_NET_FLOW_LIMIT
274 {
275 .procname = "flow_limit_cpu_bitmap",
276 .mode = 0644,
277 .proc_handler = flow_limit_cpu_sysctl
278 },
279 {
280 .procname = "flow_limit_table_len",
281 .data = &netdev_flow_limit_table_len,
282 .maxlen = sizeof(int),
283 .mode = 0644,
284 .proc_handler = flow_limit_table_len_sysctl
285 },
286#endif /* CONFIG_NET_FLOW_LIMIT */
183#endif /* CONFIG_NET */ 287#endif /* CONFIG_NET */
184 { 288 {
185 .procname = "netdev_budget", 289 .procname = "netdev_budget",