aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/bonding/bond_options.c272
-rw-r--r--drivers/net/bonding/bond_options.h100
-rw-r--r--drivers/net/bonding/bonding.h1
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
22static struct bond_option bond_opts[] = {
23 { }
24};
25
26/* Searches for a value in opt's values[] table */
27struct 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 */
43static 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 */
58static 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 */
80struct 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;
140out:
141 return ret;
142}
143
144/* Check opt's dependencies against bond mode and currently set options */
145static 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
160static 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
171static 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 */
227int __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);
248out:
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 */
264int 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 */
285struct 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
21int bond_option_mode_set(struct bonding *bond, int mode) 293int 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 */
23enum {
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 */
33enum {
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 */
40enum {
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 */
50struct bond_opt_value {
51 char *string;
52 u64 value;
53 u32 flags;
54};
55
56struct bonding;
57
58struct 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
76int __bond_opt_set(struct bonding *bond, unsigned int option,
77 struct bond_opt_value *val);
78int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf);
79struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
80 struct bond_opt_value *val);
81struct bond_option *bond_opt_get(unsigned int option);
82struct 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 */
88static 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"