aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNikolay Aleksandrov <nikolay@redhat.com>2014-01-22 08:53:16 -0500
committerDavid S. Miller <davem@davemloft.net>2014-01-22 18:38:41 -0500
commit0911736245df19b423a3b156f6709e7bba48b18a (patch)
tree17505f5989f60dc6d4371dfdce00872623f3e0ee /drivers
parent374d1125237e94f16ffa3185cff62df03977a988 (diff)
bonding: add infrastructure for an option API
This patch adds the necessary basic infrastructure to support centralized and unified option manipulation API for the bonding. The new structure bond_option will be used to describe each option with its dependencies on modes which will be checked automatically thus removing a lot of duplicated code. Also automatic range checking is added for some options. Currently the option setting function requires RTNL to be acquired prior to calling it, since many options already rely on RTNL it seemed like the best choice to protect all against common race conditions. In order to add an option the following steps need to be done: 1. Add an entry BOND_OPT_<option> to bond_options.h so it gets a unique id and a bit corresponding to the id 2. Add a bond_option entry to the bond_opts[] array in bond_options.c which describes the option, its dependencies and its manipulation function 3. Add code to export the option through sysfs and/or as a module parameter (the sysfs export will be made automatically in the future) The options can have different flags set, currently the following are supported: BOND_OPTFLAG_NOSLAVES - require that the bond device has no slaves prior to setting the option BOND_OPTFLAG_IFDOWN - require that the bond device is down prior to setting the option BOND_OPTFLAG_RAWVAL - don't parse the value but return it raw for the option to parse There's a new value structure to describe different types of values which can have the following flags: BOND_VALFLAG_DEFAULT - marks the default option (permanent string alias to this option is "default") BOND_VALFLAG_MIN - the minimum value that this option can have BOND_VALFLAG_MAX - the maximum value that this option can have An example would be nice here, so if we have an option which can have the values "off"(2), "special"(4, default) and supports a range, say 16 - 32, it should be defined as follows: "off", 2, "special", 4, BOND_VALFLAG_DEFAULT, "rangemin", 16, BOND_VALFLAG_MIN, "rangemax", 32, BOND_VALFLAG_MAX So we have the valid intervals: [2, 2], [4, 4], [16, 32] Also the valid strings: "off" = 2, "special" and "default" = 4 "rangemin" = 16, "rangemax" = 32 BOND_VALFLAG_(MIN|MAX) can be used to specify a valid range for an option, if MIN is omitted then 0 is considered as a minimum. If an exact match is found in the values[] table it will be returned, otherwise the range is tried (if available). The option parameter passing is done by using a special structure called bond_opt_value which can take either a string or a value to parse. One of the bond_opt_init(val|str) macros should be used depending on which one does the user want to parse (string or value). Then a call to __bond_opt_set should be done under RTNL. Signed-off-by: Nikolay Aleksandrov <nikolay@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-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"