diff options
author | Jiri Pirko <jpirko@redhat.com> | 2012-04-04 08:16:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-04 20:30:40 -0400 |
commit | 01d7f30a9f962573b6c91ed520c73fb30658d826 (patch) | |
tree | 44103605b79d249f4e602ad97f59feccf33b7b05 | |
parent | 2615598fc100451c71b83d06bdf5faead619a40e (diff) |
team: add loadbalance mode
This patch introduces new team mode. It's TX port is selected by
user-set BPF hash function.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/team/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/team/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/team/team_mode_loadbalance.c | 188 |
3 files changed, 200 insertions, 0 deletions
diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig index 248a144033ca..89024d5fc33a 100644 --- a/drivers/net/team/Kconfig +++ b/drivers/net/team/Kconfig | |||
@@ -40,4 +40,15 @@ config NET_TEAM_MODE_ACTIVEBACKUP | |||
40 | To compile this team mode as a module, choose M here: the module | 40 | To compile this team mode as a module, choose M here: the module |
41 | will be called team_mode_activebackup. | 41 | will be called team_mode_activebackup. |
42 | 42 | ||
43 | config NET_TEAM_MODE_LOADBALANCE | ||
44 | tristate "Load-balance mode support" | ||
45 | depends on NET_TEAM | ||
46 | ---help--- | ||
47 | This mode provides load balancing functionality. Tx port selection | ||
48 | is done using BPF function set up from userspace (bpf_hash_func | ||
49 | option) | ||
50 | |||
51 | To compile this team mode as a module, choose M here: the module | ||
52 | will be called team_mode_loadbalance. | ||
53 | |||
43 | endif # NET_TEAM | 54 | endif # NET_TEAM |
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile index 85f2028a87af..fb9f4c1c51ff 100644 --- a/drivers/net/team/Makefile +++ b/drivers/net/team/Makefile | |||
@@ -5,3 +5,4 @@ | |||
5 | obj-$(CONFIG_NET_TEAM) += team.o | 5 | obj-$(CONFIG_NET_TEAM) += team.o |
6 | obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o | 6 | obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o |
7 | obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o | 7 | obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o |
8 | obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o | ||
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c new file mode 100644 index 000000000000..ed20f395be6f --- /dev/null +++ b/drivers/net/team/team_mode_loadbalance.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * drivers/net/team/team_mode_loadbalance.c - Load-balancing mode for team | ||
3 | * Copyright (c) 2012 Jiri Pirko <jpirko@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/netdevice.h> | ||
17 | #include <linux/filter.h> | ||
18 | #include <linux/if_team.h> | ||
19 | |||
20 | struct lb_priv { | ||
21 | struct sk_filter __rcu *fp; | ||
22 | struct sock_fprog *orig_fprog; | ||
23 | }; | ||
24 | |||
25 | static struct lb_priv *lb_priv(struct team *team) | ||
26 | { | ||
27 | return (struct lb_priv *) &team->mode_priv; | ||
28 | } | ||
29 | |||
30 | static bool lb_transmit(struct team *team, struct sk_buff *skb) | ||
31 | { | ||
32 | struct sk_filter *fp; | ||
33 | struct team_port *port; | ||
34 | unsigned int hash; | ||
35 | int port_index; | ||
36 | |||
37 | fp = rcu_dereference(lb_priv(team)->fp); | ||
38 | if (unlikely(!fp)) | ||
39 | goto drop; | ||
40 | hash = SK_RUN_FILTER(fp, skb); | ||
41 | port_index = hash % team->port_count; | ||
42 | port = team_get_port_by_index_rcu(team, port_index); | ||
43 | if (unlikely(!port)) | ||
44 | goto drop; | ||
45 | skb->dev = port->dev; | ||
46 | if (dev_queue_xmit(skb)) | ||
47 | return false; | ||
48 | return true; | ||
49 | |||
50 | drop: | ||
51 | dev_kfree_skb_any(skb); | ||
52 | return false; | ||
53 | } | ||
54 | |||
55 | static int lb_bpf_func_get(struct team *team, void *arg) | ||
56 | { | ||
57 | struct team_option_binary *tbinary = team_optarg_tbinary(arg); | ||
58 | |||
59 | memset(tbinary, 0, sizeof(*tbinary)); | ||
60 | if (!lb_priv(team)->orig_fprog) | ||
61 | return 0; | ||
62 | |||
63 | tbinary->data_len = lb_priv(team)->orig_fprog->len * | ||
64 | sizeof(struct sock_filter); | ||
65 | tbinary->data = lb_priv(team)->orig_fprog->filter; | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, | ||
70 | void *data) | ||
71 | { | ||
72 | struct sock_fprog *fprog; | ||
73 | struct sock_filter *filter = (struct sock_filter *) data; | ||
74 | |||
75 | if (data_len % sizeof(struct sock_filter)) | ||
76 | return -EINVAL; | ||
77 | fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL); | ||
78 | if (!fprog) | ||
79 | return -ENOMEM; | ||
80 | fprog->filter = kmemdup(filter, data_len, GFP_KERNEL); | ||
81 | if (!fprog->filter) { | ||
82 | kfree(fprog); | ||
83 | return -ENOMEM; | ||
84 | } | ||
85 | fprog->len = data_len / sizeof(struct sock_filter); | ||
86 | *pfprog = fprog; | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static void __fprog_destroy(struct sock_fprog *fprog) | ||
91 | { | ||
92 | kfree(fprog->filter); | ||
93 | kfree(fprog); | ||
94 | } | ||
95 | |||
96 | static int lb_bpf_func_set(struct team *team, void *arg) | ||
97 | { | ||
98 | struct team_option_binary *tbinary = team_optarg_tbinary(arg); | ||
99 | struct sk_filter *fp = NULL; | ||
100 | struct sock_fprog *fprog = NULL; | ||
101 | int err; | ||
102 | |||
103 | if (tbinary->data_len) { | ||
104 | err = __fprog_create(&fprog, tbinary->data_len, | ||
105 | tbinary->data); | ||
106 | if (err) | ||
107 | return err; | ||
108 | err = sk_unattached_filter_create(&fp, fprog); | ||
109 | if (err) { | ||
110 | __fprog_destroy(fprog); | ||
111 | return err; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | if (lb_priv(team)->orig_fprog) { | ||
116 | /* Clear old filter data */ | ||
117 | __fprog_destroy(lb_priv(team)->orig_fprog); | ||
118 | sk_unattached_filter_destroy(lb_priv(team)->fp); | ||
119 | } | ||
120 | |||
121 | rcu_assign_pointer(lb_priv(team)->fp, fp); | ||
122 | lb_priv(team)->orig_fprog = fprog; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static const struct team_option lb_options[] = { | ||
127 | { | ||
128 | .name = "bpf_hash_func", | ||
129 | .type = TEAM_OPTION_TYPE_BINARY, | ||
130 | .getter = lb_bpf_func_get, | ||
131 | .setter = lb_bpf_func_set, | ||
132 | }, | ||
133 | }; | ||
134 | |||
135 | int lb_init(struct team *team) | ||
136 | { | ||
137 | return team_options_register(team, lb_options, | ||
138 | ARRAY_SIZE(lb_options)); | ||
139 | } | ||
140 | |||
141 | void lb_exit(struct team *team) | ||
142 | { | ||
143 | team_options_unregister(team, lb_options, | ||
144 | ARRAY_SIZE(lb_options)); | ||
145 | } | ||
146 | |||
147 | static int lb_port_enter(struct team *team, struct team_port *port) | ||
148 | { | ||
149 | return team_port_set_team_mac(port); | ||
150 | } | ||
151 | |||
152 | static void lb_port_change_mac(struct team *team, struct team_port *port) | ||
153 | { | ||
154 | team_port_set_team_mac(port); | ||
155 | } | ||
156 | |||
157 | static const struct team_mode_ops lb_mode_ops = { | ||
158 | .init = lb_init, | ||
159 | .exit = lb_exit, | ||
160 | .transmit = lb_transmit, | ||
161 | .port_enter = lb_port_enter, | ||
162 | .port_change_mac = lb_port_change_mac, | ||
163 | }; | ||
164 | |||
165 | static struct team_mode lb_mode = { | ||
166 | .kind = "loadbalance", | ||
167 | .owner = THIS_MODULE, | ||
168 | .priv_size = sizeof(struct lb_priv), | ||
169 | .ops = &lb_mode_ops, | ||
170 | }; | ||
171 | |||
172 | static int __init lb_init_module(void) | ||
173 | { | ||
174 | return team_mode_register(&lb_mode); | ||
175 | } | ||
176 | |||
177 | static void __exit lb_cleanup_module(void) | ||
178 | { | ||
179 | team_mode_unregister(&lb_mode); | ||
180 | } | ||
181 | |||
182 | module_init(lb_init_module); | ||
183 | module_exit(lb_cleanup_module); | ||
184 | |||
185 | MODULE_LICENSE("GPL v2"); | ||
186 | MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>"); | ||
187 | MODULE_DESCRIPTION("Load-balancing mode for team"); | ||
188 | MODULE_ALIAS("team-mode-loadbalance"); | ||