diff options
-rw-r--r-- | drivers/net/team/team_mode_loadbalance.c | 517 |
1 files changed, 500 insertions, 17 deletions
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index a475b1304f9a..33e30edeb202 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c | |||
@@ -17,18 +17,163 @@ | |||
17 | #include <linux/filter.h> | 17 | #include <linux/filter.h> |
18 | #include <linux/if_team.h> | 18 | #include <linux/if_team.h> |
19 | 19 | ||
20 | struct lb_priv; | ||
21 | |||
22 | typedef struct team_port *lb_select_tx_port_func_t(struct team *, | ||
23 | struct lb_priv *, | ||
24 | struct sk_buff *, | ||
25 | unsigned char); | ||
26 | |||
27 | #define LB_TX_HASHTABLE_SIZE 256 /* hash is a char */ | ||
28 | |||
29 | struct lb_stats { | ||
30 | u64 tx_bytes; | ||
31 | }; | ||
32 | |||
33 | struct lb_pcpu_stats { | ||
34 | struct lb_stats hash_stats[LB_TX_HASHTABLE_SIZE]; | ||
35 | struct u64_stats_sync syncp; | ||
36 | }; | ||
37 | |||
38 | struct lb_stats_info { | ||
39 | struct lb_stats stats; | ||
40 | struct lb_stats last_stats; | ||
41 | struct team_option_inst_info *opt_inst_info; | ||
42 | }; | ||
43 | |||
44 | struct lb_port_mapping { | ||
45 | struct team_port __rcu *port; | ||
46 | struct team_option_inst_info *opt_inst_info; | ||
47 | }; | ||
48 | |||
49 | struct lb_priv_ex { | ||
50 | struct team *team; | ||
51 | struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE]; | ||
52 | struct sock_fprog *orig_fprog; | ||
53 | struct { | ||
54 | unsigned int refresh_interval; /* in tenths of second */ | ||
55 | struct delayed_work refresh_dw; | ||
56 | struct lb_stats_info info[LB_TX_HASHTABLE_SIZE]; | ||
57 | } stats; | ||
58 | }; | ||
59 | |||
20 | struct lb_priv { | 60 | struct lb_priv { |
21 | struct sk_filter __rcu *fp; | 61 | struct sk_filter __rcu *fp; |
22 | struct sock_fprog *orig_fprog; | 62 | lb_select_tx_port_func_t __rcu *select_tx_port_func; |
63 | struct lb_pcpu_stats __percpu *pcpu_stats; | ||
64 | struct lb_priv_ex *ex; /* priv extension */ | ||
23 | }; | 65 | }; |
24 | 66 | ||
25 | static struct lb_priv *lb_priv(struct team *team) | 67 | static struct lb_priv *get_lb_priv(struct team *team) |
26 | { | 68 | { |
27 | return (struct lb_priv *) &team->mode_priv; | 69 | return (struct lb_priv *) &team->mode_priv; |
28 | } | 70 | } |
29 | 71 | ||
30 | static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv, | 72 | struct lb_port_priv { |
31 | struct sk_buff *skb) | 73 | struct lb_stats __percpu *pcpu_stats; |
74 | struct lb_stats_info stats_info; | ||
75 | }; | ||
76 | |||
77 | static struct lb_port_priv *get_lb_port_priv(struct team_port *port) | ||
78 | { | ||
79 | return (struct lb_port_priv *) &port->mode_priv; | ||
80 | } | ||
81 | |||
82 | #define LB_HTPM_PORT_BY_HASH(lp_priv, hash) \ | ||
83 | (lb_priv)->ex->tx_hash_to_port_mapping[hash].port | ||
84 | |||
85 | #define LB_HTPM_OPT_INST_INFO_BY_HASH(lp_priv, hash) \ | ||
86 | (lb_priv)->ex->tx_hash_to_port_mapping[hash].opt_inst_info | ||
87 | |||
88 | static void lb_tx_hash_to_port_mapping_null_port(struct team *team, | ||
89 | struct team_port *port) | ||
90 | { | ||
91 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
92 | bool changed = false; | ||
93 | int i; | ||
94 | |||
95 | for (i = 0; i < LB_TX_HASHTABLE_SIZE; i++) { | ||
96 | struct lb_port_mapping *pm; | ||
97 | |||
98 | pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; | ||
99 | if (pm->port == port) { | ||
100 | rcu_assign_pointer(pm->port, NULL); | ||
101 | team_option_inst_set_change(pm->opt_inst_info); | ||
102 | changed = true; | ||
103 | } | ||
104 | } | ||
105 | if (changed) | ||
106 | team_options_change_check(team); | ||
107 | } | ||
108 | |||
109 | /* Basic tx selection based solely by hash */ | ||
110 | static struct team_port *lb_hash_select_tx_port(struct team *team, | ||
111 | struct lb_priv *lb_priv, | ||
112 | struct sk_buff *skb, | ||
113 | unsigned char hash) | ||
114 | { | ||
115 | int port_index; | ||
116 | |||
117 | port_index = hash % team->en_port_count; | ||
118 | return team_get_port_by_index_rcu(team, port_index); | ||
119 | } | ||
120 | |||
121 | /* Hash to port mapping select tx port */ | ||
122 | static struct team_port *lb_htpm_select_tx_port(struct team *team, | ||
123 | struct lb_priv *lb_priv, | ||
124 | struct sk_buff *skb, | ||
125 | unsigned char hash) | ||
126 | { | ||
127 | return rcu_dereference(LB_HTPM_PORT_BY_HASH(lb_priv, hash)); | ||
128 | } | ||
129 | |||
130 | struct lb_select_tx_port { | ||
131 | char *name; | ||
132 | lb_select_tx_port_func_t *func; | ||
133 | }; | ||
134 | |||
135 | static const struct lb_select_tx_port lb_select_tx_port_list[] = { | ||
136 | { | ||
137 | .name = "hash", | ||
138 | .func = lb_hash_select_tx_port, | ||
139 | }, | ||
140 | { | ||
141 | .name = "hash_to_port_mapping", | ||
142 | .func = lb_htpm_select_tx_port, | ||
143 | }, | ||
144 | }; | ||
145 | #define LB_SELECT_TX_PORT_LIST_COUNT ARRAY_SIZE(lb_select_tx_port_list) | ||
146 | |||
147 | static char *lb_select_tx_port_get_name(lb_select_tx_port_func_t *func) | ||
148 | { | ||
149 | int i; | ||
150 | |||
151 | for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { | ||
152 | const struct lb_select_tx_port *item; | ||
153 | |||
154 | item = &lb_select_tx_port_list[i]; | ||
155 | if (item->func == func) | ||
156 | return item->name; | ||
157 | } | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | static lb_select_tx_port_func_t *lb_select_tx_port_get_func(const char *name) | ||
162 | { | ||
163 | int i; | ||
164 | |||
165 | for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { | ||
166 | const struct lb_select_tx_port *item; | ||
167 | |||
168 | item = &lb_select_tx_port_list[i]; | ||
169 | if (!strcmp(item->name, name)) | ||
170 | return item->func; | ||
171 | } | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv, | ||
176 | struct sk_buff *skb) | ||
32 | { | 177 | { |
33 | struct sk_filter *fp; | 178 | struct sk_filter *fp; |
34 | uint32_t lhash; | 179 | uint32_t lhash; |
@@ -42,18 +187,40 @@ static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv, | |||
42 | return c[0] ^ c[1] ^ c[2] ^ c[3]; | 187 | return c[0] ^ c[1] ^ c[2] ^ c[3]; |
43 | } | 188 | } |
44 | 189 | ||
190 | static void lb_update_tx_stats(unsigned int tx_bytes, struct lb_priv *lb_priv, | ||
191 | struct lb_port_priv *lb_port_priv, | ||
192 | unsigned char hash) | ||
193 | { | ||
194 | struct lb_pcpu_stats *pcpu_stats; | ||
195 | struct lb_stats *port_stats; | ||
196 | struct lb_stats *hash_stats; | ||
197 | |||
198 | pcpu_stats = this_cpu_ptr(lb_priv->pcpu_stats); | ||
199 | port_stats = this_cpu_ptr(lb_port_priv->pcpu_stats); | ||
200 | hash_stats = &pcpu_stats->hash_stats[hash]; | ||
201 | u64_stats_update_begin(&pcpu_stats->syncp); | ||
202 | port_stats->tx_bytes += tx_bytes; | ||
203 | hash_stats->tx_bytes += tx_bytes; | ||
204 | u64_stats_update_end(&pcpu_stats->syncp); | ||
205 | } | ||
206 | |||
45 | static bool lb_transmit(struct team *team, struct sk_buff *skb) | 207 | static bool lb_transmit(struct team *team, struct sk_buff *skb) |
46 | { | 208 | { |
209 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
210 | lb_select_tx_port_func_t *select_tx_port_func; | ||
47 | struct team_port *port; | 211 | struct team_port *port; |
48 | int port_index; | 212 | unsigned char hash; |
213 | unsigned int tx_bytes = skb->len; | ||
49 | 214 | ||
50 | port_index = lb_get_skb_hash(lb_priv(team), skb) % team->en_port_count; | 215 | hash = lb_get_skb_hash(lb_priv, skb); |
51 | port = team_get_port_by_index_rcu(team, port_index); | 216 | select_tx_port_func = rcu_dereference(lb_priv->select_tx_port_func); |
217 | port = select_tx_port_func(team, lb_priv, skb, hash); | ||
52 | if (unlikely(!port)) | 218 | if (unlikely(!port)) |
53 | goto drop; | 219 | goto drop; |
54 | skb->dev = port->dev; | 220 | skb->dev = port->dev; |
55 | if (dev_queue_xmit(skb)) | 221 | if (dev_queue_xmit(skb)) |
56 | return false; | 222 | return false; |
223 | lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); | ||
57 | return true; | 224 | return true; |
58 | 225 | ||
59 | drop: | 226 | drop: |
@@ -63,14 +230,16 @@ drop: | |||
63 | 230 | ||
64 | static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) | 231 | static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) |
65 | { | 232 | { |
66 | if (!lb_priv(team)->orig_fprog) { | 233 | struct lb_priv *lb_priv = get_lb_priv(team); |
234 | |||
235 | if (!lb_priv->ex->orig_fprog) { | ||
67 | ctx->data.bin_val.len = 0; | 236 | ctx->data.bin_val.len = 0; |
68 | ctx->data.bin_val.ptr = NULL; | 237 | ctx->data.bin_val.ptr = NULL; |
69 | return 0; | 238 | return 0; |
70 | } | 239 | } |
71 | ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len * | 240 | ctx->data.bin_val.len = lb_priv->ex->orig_fprog->len * |
72 | sizeof(struct sock_filter); | 241 | sizeof(struct sock_filter); |
73 | ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter; | 242 | ctx->data.bin_val.ptr = lb_priv->ex->orig_fprog->filter; |
74 | return 0; | 243 | return 0; |
75 | } | 244 | } |
76 | 245 | ||
@@ -103,6 +272,7 @@ static void __fprog_destroy(struct sock_fprog *fprog) | |||
103 | 272 | ||
104 | static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) | 273 | static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) |
105 | { | 274 | { |
275 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
106 | struct sk_filter *fp = NULL; | 276 | struct sk_filter *fp = NULL; |
107 | struct sock_fprog *fprog = NULL; | 277 | struct sock_fprog *fprog = NULL; |
108 | int err; | 278 | int err; |
@@ -119,14 +289,232 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) | |||
119 | } | 289 | } |
120 | } | 290 | } |
121 | 291 | ||
122 | if (lb_priv(team)->orig_fprog) { | 292 | if (lb_priv->ex->orig_fprog) { |
123 | /* Clear old filter data */ | 293 | /* Clear old filter data */ |
124 | __fprog_destroy(lb_priv(team)->orig_fprog); | 294 | __fprog_destroy(lb_priv->ex->orig_fprog); |
125 | sk_unattached_filter_destroy(lb_priv(team)->fp); | 295 | sk_unattached_filter_destroy(lb_priv->fp); |
126 | } | 296 | } |
127 | 297 | ||
128 | rcu_assign_pointer(lb_priv(team)->fp, fp); | 298 | rcu_assign_pointer(lb_priv->fp, fp); |
129 | lb_priv(team)->orig_fprog = fprog; | 299 | lb_priv->ex->orig_fprog = fprog; |
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) | ||
304 | { | ||
305 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
306 | char *name; | ||
307 | |||
308 | name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func); | ||
309 | BUG_ON(!name); | ||
310 | ctx->data.str_val = name; | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static int lb_tx_method_set(struct team *team, struct team_gsetter_ctx *ctx) | ||
315 | { | ||
316 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
317 | lb_select_tx_port_func_t *func; | ||
318 | |||
319 | func = lb_select_tx_port_get_func(ctx->data.str_val); | ||
320 | if (!func) | ||
321 | return -EINVAL; | ||
322 | rcu_assign_pointer(lb_priv->select_tx_port_func, func); | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int lb_tx_hash_to_port_mapping_init(struct team *team, | ||
327 | struct team_option_inst_info *info) | ||
328 | { | ||
329 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
330 | unsigned char hash = info->array_index; | ||
331 | |||
332 | LB_HTPM_OPT_INST_INFO_BY_HASH(lb_priv, hash) = info; | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | static int lb_tx_hash_to_port_mapping_get(struct team *team, | ||
337 | struct team_gsetter_ctx *ctx) | ||
338 | { | ||
339 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
340 | struct team_port *port; | ||
341 | unsigned char hash = ctx->info->array_index; | ||
342 | |||
343 | port = LB_HTPM_PORT_BY_HASH(lb_priv, hash); | ||
344 | ctx->data.u32_val = port ? port->dev->ifindex : 0; | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int lb_tx_hash_to_port_mapping_set(struct team *team, | ||
349 | struct team_gsetter_ctx *ctx) | ||
350 | { | ||
351 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
352 | struct team_port *port; | ||
353 | unsigned char hash = ctx->info->array_index; | ||
354 | |||
355 | list_for_each_entry(port, &team->port_list, list) { | ||
356 | if (ctx->data.u32_val == port->dev->ifindex) { | ||
357 | rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), | ||
358 | port); | ||
359 | return 0; | ||
360 | } | ||
361 | } | ||
362 | return -ENODEV; | ||
363 | } | ||
364 | |||
365 | static int lb_hash_stats_init(struct team *team, | ||
366 | struct team_option_inst_info *info) | ||
367 | { | ||
368 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
369 | unsigned char hash = info->array_index; | ||
370 | |||
371 | lb_priv->ex->stats.info[hash].opt_inst_info = info; | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int lb_hash_stats_get(struct team *team, struct team_gsetter_ctx *ctx) | ||
376 | { | ||
377 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
378 | unsigned char hash = ctx->info->array_index; | ||
379 | |||
380 | ctx->data.bin_val.ptr = &lb_priv->ex->stats.info[hash].stats; | ||
381 | ctx->data.bin_val.len = sizeof(struct lb_stats); | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static int lb_port_stats_init(struct team *team, | ||
386 | struct team_option_inst_info *info) | ||
387 | { | ||
388 | struct team_port *port = info->port; | ||
389 | struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); | ||
390 | |||
391 | lb_port_priv->stats_info.opt_inst_info = info; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int lb_port_stats_get(struct team *team, struct team_gsetter_ctx *ctx) | ||
396 | { | ||
397 | struct team_port *port = ctx->info->port; | ||
398 | struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); | ||
399 | |||
400 | ctx->data.bin_val.ptr = &lb_port_priv->stats_info.stats; | ||
401 | ctx->data.bin_val.len = sizeof(struct lb_stats); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static void __lb_stats_info_refresh_prepare(struct lb_stats_info *s_info) | ||
406 | { | ||
407 | memcpy(&s_info->last_stats, &s_info->stats, sizeof(struct lb_stats)); | ||
408 | memset(&s_info->stats, 0, sizeof(struct lb_stats)); | ||
409 | } | ||
410 | |||
411 | static bool __lb_stats_info_refresh_check(struct lb_stats_info *s_info, | ||
412 | struct team *team) | ||
413 | { | ||
414 | if (memcmp(&s_info->last_stats, &s_info->stats, | ||
415 | sizeof(struct lb_stats))) { | ||
416 | team_option_inst_set_change(s_info->opt_inst_info); | ||
417 | return true; | ||
418 | } | ||
419 | return false; | ||
420 | } | ||
421 | |||
422 | static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats, | ||
423 | struct lb_stats *cpu_stats, | ||
424 | struct u64_stats_sync *syncp) | ||
425 | { | ||
426 | unsigned int start; | ||
427 | struct lb_stats tmp; | ||
428 | |||
429 | do { | ||
430 | start = u64_stats_fetch_begin_bh(syncp); | ||
431 | tmp.tx_bytes = cpu_stats->tx_bytes; | ||
432 | } while (u64_stats_fetch_retry_bh(syncp, start)); | ||
433 | acc_stats->tx_bytes += tmp.tx_bytes; | ||
434 | } | ||
435 | |||
436 | static void lb_stats_refresh(struct work_struct *work) | ||
437 | { | ||
438 | struct team *team; | ||
439 | struct lb_priv *lb_priv; | ||
440 | struct lb_priv_ex *lb_priv_ex; | ||
441 | struct lb_pcpu_stats *pcpu_stats; | ||
442 | struct lb_stats *stats; | ||
443 | struct lb_stats_info *s_info; | ||
444 | struct team_port *port; | ||
445 | bool changed = false; | ||
446 | int i; | ||
447 | int j; | ||
448 | |||
449 | lb_priv_ex = container_of(work, struct lb_priv_ex, | ||
450 | stats.refresh_dw.work); | ||
451 | |||
452 | team = lb_priv_ex->team; | ||
453 | lb_priv = get_lb_priv(team); | ||
454 | |||
455 | if (!mutex_trylock(&team->lock)) { | ||
456 | schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, 0); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | for (j = 0; j < LB_TX_HASHTABLE_SIZE; j++) { | ||
461 | s_info = &lb_priv->ex->stats.info[j]; | ||
462 | __lb_stats_info_refresh_prepare(s_info); | ||
463 | for_each_possible_cpu(i) { | ||
464 | pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); | ||
465 | stats = &pcpu_stats->hash_stats[j]; | ||
466 | __lb_one_cpu_stats_add(&s_info->stats, stats, | ||
467 | &pcpu_stats->syncp); | ||
468 | } | ||
469 | changed |= __lb_stats_info_refresh_check(s_info, team); | ||
470 | } | ||
471 | |||
472 | list_for_each_entry(port, &team->port_list, list) { | ||
473 | struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); | ||
474 | |||
475 | s_info = &lb_port_priv->stats_info; | ||
476 | __lb_stats_info_refresh_prepare(s_info); | ||
477 | for_each_possible_cpu(i) { | ||
478 | pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); | ||
479 | stats = per_cpu_ptr(lb_port_priv->pcpu_stats, i); | ||
480 | __lb_one_cpu_stats_add(&s_info->stats, stats, | ||
481 | &pcpu_stats->syncp); | ||
482 | } | ||
483 | changed |= __lb_stats_info_refresh_check(s_info, team); | ||
484 | } | ||
485 | |||
486 | if (changed) | ||
487 | team_options_change_check(team); | ||
488 | |||
489 | schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, | ||
490 | (lb_priv_ex->stats.refresh_interval * HZ) / 10); | ||
491 | |||
492 | mutex_unlock(&team->lock); | ||
493 | } | ||
494 | |||
495 | static int lb_stats_refresh_interval_get(struct team *team, | ||
496 | struct team_gsetter_ctx *ctx) | ||
497 | { | ||
498 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
499 | |||
500 | ctx->data.u32_val = lb_priv->ex->stats.refresh_interval; | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static int lb_stats_refresh_interval_set(struct team *team, | ||
505 | struct team_gsetter_ctx *ctx) | ||
506 | { | ||
507 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
508 | unsigned int interval; | ||
509 | |||
510 | interval = ctx->data.u32_val; | ||
511 | if (lb_priv->ex->stats.refresh_interval == interval) | ||
512 | return 0; | ||
513 | lb_priv->ex->stats.refresh_interval = interval; | ||
514 | if (interval) | ||
515 | schedule_delayed_work(&lb_priv->ex->stats.refresh_dw, 0); | ||
516 | else | ||
517 | cancel_delayed_work(&lb_priv->ex->stats.refresh_dw); | ||
130 | return 0; | 518 | return 0; |
131 | } | 519 | } |
132 | 520 | ||
@@ -137,23 +525,117 @@ static const struct team_option lb_options[] = { | |||
137 | .getter = lb_bpf_func_get, | 525 | .getter = lb_bpf_func_get, |
138 | .setter = lb_bpf_func_set, | 526 | .setter = lb_bpf_func_set, |
139 | }, | 527 | }, |
528 | { | ||
529 | .name = "lb_tx_method", | ||
530 | .type = TEAM_OPTION_TYPE_STRING, | ||
531 | .getter = lb_tx_method_get, | ||
532 | .setter = lb_tx_method_set, | ||
533 | }, | ||
534 | { | ||
535 | .name = "lb_tx_hash_to_port_mapping", | ||
536 | .array_size = LB_TX_HASHTABLE_SIZE, | ||
537 | .type = TEAM_OPTION_TYPE_U32, | ||
538 | .init = lb_tx_hash_to_port_mapping_init, | ||
539 | .getter = lb_tx_hash_to_port_mapping_get, | ||
540 | .setter = lb_tx_hash_to_port_mapping_set, | ||
541 | }, | ||
542 | { | ||
543 | .name = "lb_hash_stats", | ||
544 | .array_size = LB_TX_HASHTABLE_SIZE, | ||
545 | .type = TEAM_OPTION_TYPE_BINARY, | ||
546 | .init = lb_hash_stats_init, | ||
547 | .getter = lb_hash_stats_get, | ||
548 | }, | ||
549 | { | ||
550 | .name = "lb_port_stats", | ||
551 | .per_port = true, | ||
552 | .type = TEAM_OPTION_TYPE_BINARY, | ||
553 | .init = lb_port_stats_init, | ||
554 | .getter = lb_port_stats_get, | ||
555 | }, | ||
556 | { | ||
557 | .name = "lb_stats_refresh_interval", | ||
558 | .type = TEAM_OPTION_TYPE_U32, | ||
559 | .getter = lb_stats_refresh_interval_get, | ||
560 | .setter = lb_stats_refresh_interval_set, | ||
561 | }, | ||
140 | }; | 562 | }; |
141 | 563 | ||
142 | static int lb_init(struct team *team) | 564 | static int lb_init(struct team *team) |
143 | { | 565 | { |
144 | return team_options_register(team, lb_options, | 566 | struct lb_priv *lb_priv = get_lb_priv(team); |
145 | ARRAY_SIZE(lb_options)); | 567 | lb_select_tx_port_func_t *func; |
568 | int err; | ||
569 | |||
570 | /* set default tx port selector */ | ||
571 | func = lb_select_tx_port_get_func("hash"); | ||
572 | BUG_ON(!func); | ||
573 | rcu_assign_pointer(lb_priv->select_tx_port_func, func); | ||
574 | |||
575 | lb_priv->ex = kzalloc(sizeof(*lb_priv->ex), GFP_KERNEL); | ||
576 | if (!lb_priv->ex) | ||
577 | return -ENOMEM; | ||
578 | lb_priv->ex->team = team; | ||
579 | |||
580 | lb_priv->pcpu_stats = alloc_percpu(struct lb_pcpu_stats); | ||
581 | if (!lb_priv->pcpu_stats) { | ||
582 | err = -ENOMEM; | ||
583 | goto err_alloc_pcpu_stats; | ||
584 | } | ||
585 | |||
586 | INIT_DELAYED_WORK(&lb_priv->ex->stats.refresh_dw, lb_stats_refresh); | ||
587 | |||
588 | err = team_options_register(team, lb_options, ARRAY_SIZE(lb_options)); | ||
589 | if (err) | ||
590 | goto err_options_register; | ||
591 | return 0; | ||
592 | |||
593 | err_options_register: | ||
594 | free_percpu(lb_priv->pcpu_stats); | ||
595 | err_alloc_pcpu_stats: | ||
596 | kfree(lb_priv->ex); | ||
597 | return err; | ||
146 | } | 598 | } |
147 | 599 | ||
148 | static void lb_exit(struct team *team) | 600 | static void lb_exit(struct team *team) |
149 | { | 601 | { |
602 | struct lb_priv *lb_priv = get_lb_priv(team); | ||
603 | |||
150 | team_options_unregister(team, lb_options, | 604 | team_options_unregister(team, lb_options, |
151 | ARRAY_SIZE(lb_options)); | 605 | ARRAY_SIZE(lb_options)); |
606 | cancel_delayed_work_sync(&lb_priv->ex->stats.refresh_dw); | ||
607 | free_percpu(lb_priv->pcpu_stats); | ||
608 | kfree(lb_priv->ex); | ||
609 | } | ||
610 | |||
611 | static int lb_port_enter(struct team *team, struct team_port *port) | ||
612 | { | ||
613 | struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); | ||
614 | |||
615 | lb_port_priv->pcpu_stats = alloc_percpu(struct lb_stats); | ||
616 | if (!lb_port_priv->pcpu_stats) | ||
617 | return -ENOMEM; | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static void lb_port_leave(struct team *team, struct team_port *port) | ||
622 | { | ||
623 | struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); | ||
624 | |||
625 | free_percpu(lb_port_priv->pcpu_stats); | ||
626 | } | ||
627 | |||
628 | static void lb_port_disabled(struct team *team, struct team_port *port) | ||
629 | { | ||
630 | lb_tx_hash_to_port_mapping_null_port(team, port); | ||
152 | } | 631 | } |
153 | 632 | ||
154 | static const struct team_mode_ops lb_mode_ops = { | 633 | static const struct team_mode_ops lb_mode_ops = { |
155 | .init = lb_init, | 634 | .init = lb_init, |
156 | .exit = lb_exit, | 635 | .exit = lb_exit, |
636 | .port_enter = lb_port_enter, | ||
637 | .port_leave = lb_port_leave, | ||
638 | .port_disabled = lb_port_disabled, | ||
157 | .transmit = lb_transmit, | 639 | .transmit = lb_transmit, |
158 | }; | 640 | }; |
159 | 641 | ||
@@ -161,6 +643,7 @@ static const struct team_mode lb_mode = { | |||
161 | .kind = "loadbalance", | 643 | .kind = "loadbalance", |
162 | .owner = THIS_MODULE, | 644 | .owner = THIS_MODULE, |
163 | .priv_size = sizeof(struct lb_priv), | 645 | .priv_size = sizeof(struct lb_priv), |
646 | .port_priv_size = sizeof(struct lb_port_priv), | ||
164 | .ops = &lb_mode_ops, | 647 | .ops = &lb_mode_ops, |
165 | }; | 648 | }; |
166 | 649 | ||