diff options
-rw-r--r-- | drivers/net/bonding/bond_options.c | 272 | ||||
-rw-r--r-- | drivers/net/bonding/bond_options.h | 100 | ||||
-rw-r--r-- | drivers/net/bonding/bonding.h | 1 |
3 files changed, 373 insertions, 0 deletions
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 85e434886f2e..3ad140bfed1a 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c | |||
@@ -16,8 +16,280 @@ | |||
16 | #include <linux/netdevice.h> | 16 | #include <linux/netdevice.h> |
17 | #include <linux/rwlock.h> | 17 | #include <linux/rwlock.h> |
18 | #include <linux/rcupdate.h> | 18 | #include <linux/rcupdate.h> |
19 | #include <linux/ctype.h> | ||
19 | #include "bonding.h" | 20 | #include "bonding.h" |
20 | 21 | ||
22 | static struct bond_option bond_opts[] = { | ||
23 | { } | ||
24 | }; | ||
25 | |||
26 | /* Searches for a value in opt's values[] table */ | ||
27 | struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val) | ||
28 | { | ||
29 | struct bond_option *opt; | ||
30 | int i; | ||
31 | |||
32 | opt = bond_opt_get(option); | ||
33 | if (WARN_ON(!opt)) | ||
34 | return NULL; | ||
35 | for (i = 0; opt->values && opt->values[i].string; i++) | ||
36 | if (opt->values[i].value == val) | ||
37 | return &opt->values[i]; | ||
38 | |||
39 | return NULL; | ||
40 | } | ||
41 | |||
42 | /* Searches for a value in opt's values[] table which matches the flagmask */ | ||
43 | static struct bond_opt_value *bond_opt_get_flags(const struct bond_option *opt, | ||
44 | u32 flagmask) | ||
45 | { | ||
46 | int i; | ||
47 | |||
48 | for (i = 0; opt->values && opt->values[i].string; i++) | ||
49 | if (opt->values[i].flags & flagmask) | ||
50 | return &opt->values[i]; | ||
51 | |||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | /* If maxval is missing then there's no range to check. In case minval is | ||
56 | * missing then it's considered to be 0. | ||
57 | */ | ||
58 | static bool bond_opt_check_range(const struct bond_option *opt, u64 val) | ||
59 | { | ||
60 | struct bond_opt_value *minval, *maxval; | ||
61 | |||
62 | minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN); | ||
63 | maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); | ||
64 | if (!maxval || (minval && val < minval->value) || val > maxval->value) | ||
65 | return false; | ||
66 | |||
67 | return true; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * bond_opt_parse - parse option value | ||
72 | * @opt: the option to parse against | ||
73 | * @val: value to parse | ||
74 | * | ||
75 | * This function tries to extract the value from @val and check if it's | ||
76 | * a possible match for the option and returns NULL if a match isn't found, | ||
77 | * or the struct_opt_value that matched. It also strips the new line from | ||
78 | * @val->string if it's present. | ||
79 | */ | ||
80 | struct bond_opt_value *bond_opt_parse(const struct bond_option *opt, | ||
81 | struct bond_opt_value *val) | ||
82 | { | ||
83 | char *p, valstr[BOND_OPT_MAX_NAMELEN + 1] = { 0, }; | ||
84 | struct bond_opt_value *tbl, *ret = NULL; | ||
85 | bool checkval; | ||
86 | int i, rv; | ||
87 | |||
88 | /* No parsing if the option wants a raw val */ | ||
89 | if (opt->flags & BOND_OPTFLAG_RAWVAL) | ||
90 | return val; | ||
91 | |||
92 | tbl = opt->values; | ||
93 | if (!tbl) | ||
94 | goto out; | ||
95 | |||
96 | /* ULLONG_MAX is used to bypass string processing */ | ||
97 | checkval = val->value != ULLONG_MAX; | ||
98 | if (!checkval) { | ||
99 | if (!val->string) | ||
100 | goto out; | ||
101 | p = strchr(val->string, '\n'); | ||
102 | if (p) | ||
103 | *p = '\0'; | ||
104 | for (p = val->string; *p; p++) | ||
105 | if (!(isdigit(*p) || isspace(*p))) | ||
106 | break; | ||
107 | /* The following code extracts the string to match or the value | ||
108 | * and sets checkval appropriately | ||
109 | */ | ||
110 | if (*p) { | ||
111 | rv = sscanf(val->string, "%32s", valstr); | ||
112 | } else { | ||
113 | rv = sscanf(val->string, "%llu", &val->value); | ||
114 | checkval = true; | ||
115 | } | ||
116 | if (!rv) | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | for (i = 0; tbl[i].string; i++) { | ||
121 | /* Check for exact match */ | ||
122 | if (checkval) { | ||
123 | if (val->value == tbl[i].value) | ||
124 | ret = &tbl[i]; | ||
125 | } else { | ||
126 | if (!strcmp(valstr, "default") && | ||
127 | (tbl[i].flags & BOND_VALFLAG_DEFAULT)) | ||
128 | ret = &tbl[i]; | ||
129 | |||
130 | if (!strcmp(valstr, tbl[i].string)) | ||
131 | ret = &tbl[i]; | ||
132 | } | ||
133 | /* Found an exact match */ | ||
134 | if (ret) | ||
135 | goto out; | ||
136 | } | ||
137 | /* Possible range match */ | ||
138 | if (checkval && bond_opt_check_range(opt, val->value)) | ||
139 | ret = val; | ||
140 | out: | ||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | /* Check opt's dependencies against bond mode and currently set options */ | ||
145 | static int bond_opt_check_deps(struct bonding *bond, | ||
146 | const struct bond_option *opt) | ||
147 | { | ||
148 | struct bond_params *params = &bond->params; | ||
149 | |||
150 | if (test_bit(params->mode, &opt->unsuppmodes)) | ||
151 | return -EACCES; | ||
152 | if ((opt->flags & BOND_OPTFLAG_NOSLAVES) && bond_has_slaves(bond)) | ||
153 | return -ENOTEMPTY; | ||
154 | if ((opt->flags & BOND_OPTFLAG_IFDOWN) && (bond->dev->flags & IFF_UP)) | ||
155 | return -EBUSY; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static void bond_opt_dep_print(struct bonding *bond, | ||
161 | const struct bond_option *opt) | ||
162 | { | ||
163 | struct bond_params *params; | ||
164 | |||
165 | params = &bond->params; | ||
166 | if (test_bit(params->mode, &opt->unsuppmodes)) | ||
167 | pr_err("%s: option %s: mode dependency failed\n", | ||
168 | bond->dev->name, opt->name); | ||
169 | } | ||
170 | |||
171 | static void bond_opt_error_interpret(struct bonding *bond, | ||
172 | const struct bond_option *opt, | ||
173 | int error, struct bond_opt_value *val) | ||
174 | { | ||
175 | struct bond_opt_value *minval, *maxval; | ||
176 | char *p; | ||
177 | |||
178 | switch (error) { | ||
179 | case -EINVAL: | ||
180 | if (val) { | ||
181 | if (val->string) { | ||
182 | /* sometimes RAWVAL opts may have new lines */ | ||
183 | p = strchr(val->string, '\n'); | ||
184 | if (p) | ||
185 | *p = '\0'; | ||
186 | pr_err("%s: option %s: invalid value (%s).\n", | ||
187 | bond->dev->name, opt->name, val->string); | ||
188 | } else { | ||
189 | pr_err("%s: option %s: invalid value (%llu).\n", | ||
190 | bond->dev->name, opt->name, val->value); | ||
191 | } | ||
192 | } | ||
193 | minval = bond_opt_get_flags(opt, BOND_VALFLAG_MIN); | ||
194 | maxval = bond_opt_get_flags(opt, BOND_VALFLAG_MAX); | ||
195 | if (!maxval) | ||
196 | break; | ||
197 | pr_err("%s: option %s: allowed values %llu - %llu.\n", | ||
198 | bond->dev->name, opt->name, minval ? minval->value : 0, | ||
199 | maxval->value); | ||
200 | break; | ||
201 | case -EACCES: | ||
202 | bond_opt_dep_print(bond, opt); | ||
203 | break; | ||
204 | case -ENOTEMPTY: | ||
205 | pr_err("%s: option %s: unable to set because the bond device has slaves.\n", | ||
206 | bond->dev->name, opt->name); | ||
207 | break; | ||
208 | case -EBUSY: | ||
209 | pr_err("%s: option %s: unable to set because the bond device is up.\n", | ||
210 | bond->dev->name, opt->name); | ||
211 | break; | ||
212 | default: | ||
213 | break; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * __bond_opt_set - set a bonding option | ||
219 | * @bond: target bond device | ||
220 | * @option: option to set | ||
221 | * @val: value to set it to | ||
222 | * | ||
223 | * This function is used to change the bond's option value, it can be | ||
224 | * used for both enabling/changing an option and for disabling it. RTNL lock | ||
225 | * must be obtained before calling this function. | ||
226 | */ | ||
227 | int __bond_opt_set(struct bonding *bond, | ||
228 | unsigned int option, struct bond_opt_value *val) | ||
229 | { | ||
230 | struct bond_opt_value *retval = NULL; | ||
231 | const struct bond_option *opt; | ||
232 | int ret = -ENOENT; | ||
233 | |||
234 | ASSERT_RTNL(); | ||
235 | |||
236 | opt = bond_opt_get(option); | ||
237 | if (WARN_ON(!val) || WARN_ON(!opt)) | ||
238 | goto out; | ||
239 | ret = bond_opt_check_deps(bond, opt); | ||
240 | if (ret) | ||
241 | goto out; | ||
242 | retval = bond_opt_parse(opt, val); | ||
243 | if (!retval) { | ||
244 | ret = -EINVAL; | ||
245 | goto out; | ||
246 | } | ||
247 | ret = opt->set(bond, retval); | ||
248 | out: | ||
249 | if (ret) | ||
250 | bond_opt_error_interpret(bond, opt, ret, val); | ||
251 | |||
252 | return ret; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * bond_opt_tryset_rtnl - try to acquire rtnl and call __bond_opt_set | ||
257 | * @bond: target bond device | ||
258 | * @option: option to set | ||
259 | * @buf: value to set it to | ||
260 | * | ||
261 | * This function tries to acquire RTNL without blocking and if successful | ||
262 | * calls __bond_opt_set. It is mainly used for sysfs option manipulation. | ||
263 | */ | ||
264 | int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf) | ||
265 | { | ||
266 | struct bond_opt_value optval; | ||
267 | int ret; | ||
268 | |||
269 | if (!rtnl_trylock()) | ||
270 | return restart_syscall(); | ||
271 | bond_opt_initstr(&optval, buf); | ||
272 | ret = __bond_opt_set(bond, option, &optval); | ||
273 | rtnl_unlock(); | ||
274 | |||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * bond_opt_get - get a pointer to an option | ||
280 | * @option: option for which to return a pointer | ||
281 | * | ||
282 | * This function checks if option is valid and if so returns a pointer | ||
283 | * to its entry in the bond_opts[] option array. | ||
284 | */ | ||
285 | struct bond_option *bond_opt_get(unsigned int option) | ||
286 | { | ||
287 | if (!BOND_OPT_VALID(option)) | ||
288 | return NULL; | ||
289 | |||
290 | return &bond_opts[option]; | ||
291 | } | ||
292 | |||
21 | int bond_option_mode_set(struct bonding *bond, int mode) | 293 | int bond_option_mode_set(struct bonding *bond, int mode) |
22 | { | 294 | { |
23 | if (bond_parm_tbl_lookup(mode, bond_mode_tbl) < 0) { | 295 | if (bond_parm_tbl_lookup(mode, bond_mode_tbl) < 0) { |
diff --git a/drivers/net/bonding/bond_options.h b/drivers/net/bonding/bond_options.h new file mode 100644 index 000000000000..e20f2ebaf5c3 --- /dev/null +++ b/drivers/net/bonding/bond_options.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * drivers/net/bond/bond_options.h - bonding options | ||
3 | * Copyright (c) 2013 Nikolay Aleksandrov <nikolay@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 | #ifndef _BOND_OPTIONS_H | ||
12 | #define _BOND_OPTIONS_H | ||
13 | |||
14 | #define BOND_OPT_MAX_NAMELEN 32 | ||
15 | #define BOND_OPT_VALID(opt) ((opt) < BOND_OPT_LAST) | ||
16 | #define BOND_MODE_ALL_EX(x) (~(x)) | ||
17 | |||
18 | /* Option flags: | ||
19 | * BOND_OPTFLAG_NOSLAVES - check if the bond device is empty before setting | ||
20 | * BOND_OPTFLAG_IFDOWN - check if the bond device is down before setting | ||
21 | * BOND_OPTFLAG_RAWVAL - the option parses the value itself | ||
22 | */ | ||
23 | enum { | ||
24 | BOND_OPTFLAG_NOSLAVES = BIT(0), | ||
25 | BOND_OPTFLAG_IFDOWN = BIT(1), | ||
26 | BOND_OPTFLAG_RAWVAL = BIT(2) | ||
27 | }; | ||
28 | |||
29 | /* Value type flags: | ||
30 | * BOND_VALFLAG_DEFAULT - mark the value as default | ||
31 | * BOND_VALFLAG_(MIN|MAX) - mark the value as min/max | ||
32 | */ | ||
33 | enum { | ||
34 | BOND_VALFLAG_DEFAULT = BIT(0), | ||
35 | BOND_VALFLAG_MIN = BIT(1), | ||
36 | BOND_VALFLAG_MAX = BIT(2) | ||
37 | }; | ||
38 | |||
39 | /* Option IDs, their bit positions correspond to their IDs */ | ||
40 | enum { | ||
41 | BOND_OPT_LAST | ||
42 | }; | ||
43 | |||
44 | /* This structure is used for storing option values and for passing option | ||
45 | * values when changing an option. The logic when used as an arg is as follows: | ||
46 | * - if string != NULL -> parse it, if the opt is RAW type then return it, else | ||
47 | * return the parse result | ||
48 | * - if string == NULL -> parse value | ||
49 | */ | ||
50 | struct bond_opt_value { | ||
51 | char *string; | ||
52 | u64 value; | ||
53 | u32 flags; | ||
54 | }; | ||
55 | |||
56 | struct bonding; | ||
57 | |||
58 | struct bond_option { | ||
59 | int id; | ||
60 | char *name; | ||
61 | char *desc; | ||
62 | u32 flags; | ||
63 | |||
64 | /* unsuppmodes is used to denote modes in which the option isn't | ||
65 | * supported. | ||
66 | */ | ||
67 | unsigned long unsuppmodes; | ||
68 | /* supported values which this option can have, can be a subset of | ||
69 | * BOND_OPTVAL_RANGE's value range | ||
70 | */ | ||
71 | struct bond_opt_value *values; | ||
72 | |||
73 | int (*set)(struct bonding *bond, struct bond_opt_value *val); | ||
74 | }; | ||
75 | |||
76 | int __bond_opt_set(struct bonding *bond, unsigned int option, | ||
77 | struct bond_opt_value *val); | ||
78 | int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf); | ||
79 | struct bond_opt_value *bond_opt_parse(const struct bond_option *opt, | ||
80 | struct bond_opt_value *val); | ||
81 | struct bond_option *bond_opt_get(unsigned int option); | ||
82 | struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val); | ||
83 | |||
84 | /* This helper is used to initialize a bond_opt_value structure for parameter | ||
85 | * passing. There should be either a valid string or value, but not both. | ||
86 | * When value is ULLONG_MAX then string will be used. | ||
87 | */ | ||
88 | static inline void __bond_opt_init(struct bond_opt_value *optval, | ||
89 | char *string, u64 value) | ||
90 | { | ||
91 | memset(optval, 0, sizeof(*optval)); | ||
92 | optval->value = ULLONG_MAX; | ||
93 | if (value == ULLONG_MAX) | ||
94 | optval->string = string; | ||
95 | else | ||
96 | optval->value = value; | ||
97 | } | ||
98 | #define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value) | ||
99 | #define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX) | ||
100 | #endif /* _BOND_OPTIONS_H */ | ||
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 0a616c41dc94..8c3c94aa04d7 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | #include "bond_3ad.h" | 28 | #include "bond_3ad.h" |
29 | #include "bond_alb.h" | 29 | #include "bond_alb.h" |
30 | #include "bond_options.h" | ||
30 | 31 | ||
31 | #define DRV_VERSION "3.7.1" | 32 | #define DRV_VERSION "3.7.1" |
32 | #define DRV_RELDATE "April 27, 2011" | 33 | #define DRV_RELDATE "April 27, 2011" |