Commit 80f7c668 authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

team: add support for per-port options

This patch allows to create per-port options. That becomes handy for all
sorts of stuff, for example for userspace driven link-state, 802.3ad
implementation and so on.
Signed-off-by: default avatarJiri Pirko <jpirko@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7a5cc242
...@@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac); ...@@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac);
* Options handling * Options handling
*******************/ *******************/
struct team_option *__team_find_option(struct team *team, const char *opt_name) struct team_option_inst { /* One for each option instance */
struct list_head list;
struct team_option *option;
struct team_port *port; /* != NULL if per-port */
bool changed;
bool removed;
};
static struct team_option *__team_find_option(struct team *team,
const char *opt_name)
{ {
struct team_option *option; struct team_option *option;
...@@ -92,7 +101,119 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) ...@@ -92,7 +101,119 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
return NULL; return NULL;
} }
int __team_options_register(struct team *team, static int __team_option_inst_add(struct team *team, struct team_option *option,
struct team_port *port)
{
struct team_option_inst *opt_inst;
opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
if (!opt_inst)
return -ENOMEM;
opt_inst->option = option;
opt_inst->port = port;
opt_inst->changed = true;
opt_inst->removed = false;
list_add_tail(&opt_inst->list, &team->option_inst_list);
return 0;
}
static void __team_option_inst_del(struct team_option_inst *opt_inst)
{
list_del(&opt_inst->list);
kfree(opt_inst);
}
static void __team_option_inst_del_option(struct team *team,
struct team_option *option)
{
struct team_option_inst *opt_inst, *tmp;
list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
if (opt_inst->option == option)
__team_option_inst_del(opt_inst);
}
}
static int __team_option_inst_add_option(struct team *team,
struct team_option *option)
{
struct team_port *port;
int err;
if (!option->per_port)
return __team_option_inst_add(team, option, 0);
list_for_each_entry(port, &team->port_list, list) {
err = __team_option_inst_add(team, option, port);
if (err)
goto inst_del_option;
}
return 0;
inst_del_option:
__team_option_inst_del_option(team, option);
return err;
}
static void __team_option_inst_mark_removed_option(struct team *team,
struct team_option *option)
{
struct team_option_inst *opt_inst;
list_for_each_entry(opt_inst, &team->option_inst_list, list) {
if (opt_inst->option == option) {
opt_inst->changed = true;
opt_inst->removed = true;
}
}
}
static void __team_option_inst_del_port(struct team *team,
struct team_port *port)
{
struct team_option_inst *opt_inst, *tmp;
list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
if (opt_inst->option->per_port &&
opt_inst->port == port)
__team_option_inst_del(opt_inst);
}
}
static int __team_option_inst_add_port(struct team *team,
struct team_port *port)
{
struct team_option *option;
int err;
list_for_each_entry(option, &team->option_list, list) {
if (!option->per_port)
continue;
err = __team_option_inst_add(team, option, port);
if (err)
goto inst_del_port;
}
return 0;
inst_del_port:
__team_option_inst_del_port(team, port);
return err;
}
static void __team_option_inst_mark_removed_port(struct team *team,
struct team_port *port)
{
struct team_option_inst *opt_inst;
list_for_each_entry(opt_inst, &team->option_inst_list, list) {
if (opt_inst->port == port) {
opt_inst->changed = true;
opt_inst->removed = true;
}
}
}
static int __team_options_register(struct team *team,
const struct team_option *option, const struct team_option *option,
size_t option_count) size_t option_count)
{ {
...@@ -107,26 +228,32 @@ int __team_options_register(struct team *team, ...@@ -107,26 +228,32 @@ int __team_options_register(struct team *team,
for (i = 0; i < option_count; i++, option++) { for (i = 0; i < option_count; i++, option++) {
if (__team_find_option(team, option->name)) { if (__team_find_option(team, option->name)) {
err = -EEXIST; err = -EEXIST;
goto rollback; goto alloc_rollback;
} }
dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL); dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
if (!dst_opts[i]) { if (!dst_opts[i]) {
err = -ENOMEM; err = -ENOMEM;
goto rollback; goto alloc_rollback;
} }
} }
for (i = 0; i < option_count; i++) { for (i = 0; i < option_count; i++) {
dst_opts[i]->changed = true; err = __team_option_inst_add_option(team, dst_opts[i]);
dst_opts[i]->removed = false; if (err)
goto inst_rollback;
list_add_tail(&dst_opts[i]->list, &team->option_list); list_add_tail(&dst_opts[i]->list, &team->option_list);
} }
kfree(dst_opts); kfree(dst_opts);
return 0; return 0;
rollback: inst_rollback:
for (i = 0; i < option_count; i++) for (i--; i >= 0; i--)
__team_option_inst_del_option(team, dst_opts[i]);
i = option_count - 1;
alloc_rollback:
for (i--; i >= 0; i--)
kfree(dst_opts[i]); kfree(dst_opts[i]);
kfree(dst_opts); kfree(dst_opts);
...@@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team, ...@@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team,
struct team_option *del_opt; struct team_option *del_opt;
del_opt = __team_find_option(team, option->name); del_opt = __team_find_option(team, option->name);
if (del_opt) { if (del_opt)
del_opt->changed = true; __team_option_inst_mark_removed_option(team, del_opt);
del_opt->removed = true;
}
} }
} }
...@@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team, ...@@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team,
del_opt = __team_find_option(team, option->name); del_opt = __team_find_option(team, option->name);
if (del_opt) { if (del_opt) {
__team_option_inst_del_option(team, del_opt);
list_del(&del_opt->list); list_del(&del_opt->list);
kfree(del_opt); kfree(del_opt);
} }
...@@ -193,22 +319,42 @@ void team_options_unregister(struct team *team, ...@@ -193,22 +319,42 @@ void team_options_unregister(struct team *team,
} }
EXPORT_SYMBOL(team_options_unregister); EXPORT_SYMBOL(team_options_unregister);
static int team_option_get(struct team *team, struct team_option *option, static int team_option_port_add(struct team *team, struct team_port *port)
void *arg) {
int err;
err = __team_option_inst_add_port(team, port);
if (err)
return err;
__team_options_change_check(team);
return 0;
}
static void team_option_port_del(struct team *team, struct team_port *port)
{ {
return option->getter(team, arg); __team_option_inst_mark_removed_port(team, port);
__team_options_change_check(team);
__team_option_inst_del_port(team, port);
} }
static int team_option_set(struct team *team, struct team_option *option, static int team_option_get(struct team *team,
void *arg) struct team_option_inst *opt_inst,
struct team_gsetter_ctx *ctx)
{
return opt_inst->option->getter(team, ctx);
}
static int team_option_set(struct team *team,
struct team_option_inst *opt_inst,
struct team_gsetter_ctx *ctx)
{ {
int err; int err;
err = option->setter(team, arg); err = opt_inst->option->setter(team, ctx);
if (err) if (err)
return err; return err;
option->changed = true; opt_inst->changed = true;
__team_options_change_check(team); __team_options_change_check(team);
return err; return err;
} }
...@@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev) ...@@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_handler_register; goto err_handler_register;
} }
err = team_option_port_add(team, port);
if (err) {
netdev_err(dev, "Device %s failed to add per-port options\n",
portname);
goto err_option_port_add;
}
team_port_list_add_port(team, port); team_port_list_add_port(team, port);
team_adjust_ops(team); team_adjust_ops(team);
__team_compute_features(team); __team_compute_features(team);
...@@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) ...@@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
return 0; return 0;
err_option_port_add:
netdev_rx_handler_unregister(port_dev);
err_handler_register: err_handler_register:
netdev_set_master(port_dev, NULL); netdev_set_master(port_dev, NULL);
...@@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) ...@@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
__team_port_change_check(port, false); __team_port_change_check(port, false);
team_port_list_del_port(team, port); team_port_list_del_port(team, port);
team_adjust_ops(team); team_adjust_ops(team);
team_option_port_del(team, port);
netdev_rx_handler_unregister(port_dev); netdev_rx_handler_unregister(port_dev);
netdev_set_master(port_dev, NULL); netdev_set_master(port_dev, NULL);
vlan_vids_del_by_dev(port_dev, dev); vlan_vids_del_by_dev(port_dev, dev);
...@@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev) ...@@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
static const char team_no_mode_kind[] = "*NOMODE*"; static const char team_no_mode_kind[] = "*NOMODE*";
static int team_mode_option_get(struct team *team, void *arg) static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
{ {
const char **str = arg; ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind;
*str = team->mode ? team->mode->kind : team_no_mode_kind;
return 0; return 0;
} }
static int team_mode_option_set(struct team *team, void *arg) static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
{ {
const char **str = arg; return team_change_mode(team, ctx->data.str_val);
return team_change_mode(team, *str);
} }
static const struct team_option team_options[] = { static const struct team_option team_options[] = {
...@@ -756,6 +909,7 @@ static int team_init(struct net_device *dev) ...@@ -756,6 +909,7 @@ static int team_init(struct net_device *dev)
team_adjust_ops(team); team_adjust_ops(team);
INIT_LIST_HEAD(&team->option_list); INIT_LIST_HEAD(&team->option_list);
INIT_LIST_HEAD(&team->option_inst_list);
err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
if (err) if (err)
goto err_options_register; goto err_options_register;
...@@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb, ...@@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
{ {
struct nlattr *option_list; struct nlattr *option_list;
void *hdr; void *hdr;
struct team_option *option; struct team_option_inst *opt_inst;
int err;
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
TEAM_CMD_OPTIONS_GET); TEAM_CMD_OPTIONS_GET);
...@@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb, ...@@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
if (!option_list) if (!option_list)
return -EMSGSIZE; return -EMSGSIZE;
list_for_each_entry(option, &team->option_list, list) { list_for_each_entry(opt_inst, &team->option_inst_list, list) {
struct nlattr *option_item; struct nlattr *option_item;
long arg; struct team_option *option = opt_inst->option;
struct team_option_binary tbinary; struct team_gsetter_ctx ctx;
/* Include only changed options if fill all mode is not on */ /* Include only changed options if fill all mode is not on */
if (!fillall && !option->changed) if (!fillall && !opt_inst->changed)
continue; continue;
option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
if (!option_item) if (!option_item)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
goto nla_put_failure; goto nla_put_failure;
if (option->changed) { if (opt_inst->changed) {
if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
goto nla_put_failure; goto nla_put_failure;
option->changed = false; opt_inst->changed = false;
} }
if (option->removed && if (opt_inst->removed &&
nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
goto nla_put_failure; goto nla_put_failure;
if (opt_inst->port &&
nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
opt_inst->port->dev->ifindex))
goto nla_put_failure;
ctx.port = opt_inst->port;
switch (option->type) { switch (option->type) {
case TEAM_OPTION_TYPE_U32: case TEAM_OPTION_TYPE_U32:
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
goto nla_put_failure; goto nla_put_failure;
team_option_get(team, option, &arg); err = team_option_get(team, opt_inst, &ctx);
if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg)) if (err)
goto errout;
if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA,
ctx.data.u32_val))
goto nla_put_failure; goto nla_put_failure;
break; break;
case TEAM_OPTION_TYPE_STRING: case TEAM_OPTION_TYPE_STRING:
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
goto nla_put_failure; goto nla_put_failure;
team_option_get(team, option, &arg); err = team_option_get(team, opt_inst, &ctx);
if (err)
goto errout;
if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
(char *) arg)) ctx.data.str_val))
goto nla_put_failure; goto nla_put_failure;
break; break;
case TEAM_OPTION_TYPE_BINARY: case TEAM_OPTION_TYPE_BINARY:
if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
goto nla_put_failure; goto nla_put_failure;
arg = (long) &tbinary; err = team_option_get(team, opt_inst, &ctx);
team_option_get(team, option, &arg); if (err)
goto errout;
if (nla_put(skb, TEAM_ATTR_OPTION_DATA, if (nla_put(skb, TEAM_ATTR_OPTION_DATA,
tbinary.data_len, tbinary.data)) ctx.data.bin_val.len, ctx.data.bin_val.ptr))
goto nla_put_failure; goto nla_put_failure;
break; break;
default: default:
...@@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb, ...@@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb,
return genlmsg_end(skb, hdr); return genlmsg_end(skb, hdr);
nla_put_failure: nla_put_failure:
err = -EMSGSIZE;
errout:
genlmsg_cancel(skb, hdr); genlmsg_cancel(skb, hdr);
return -EMSGSIZE; return err;
} }
static int team_nl_fill_options_get_all(struct sk_buff *skb, static int team_nl_fill_options_get_all(struct sk_buff *skb,
...@@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
} }
nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1]; struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
struct nlattr *attr_port_ifindex;
enum team_option_type opt_type; enum team_option_type opt_type;
struct team_option *option; int opt_port_ifindex = 0; /* != 0 for per-port options */
struct team_option_inst *opt_inst;
char *opt_name; char *opt_name;
bool opt_found = false; bool opt_found = false;
...@@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
err = -EINVAL; err = -EINVAL;
goto team_put; goto team_put;
} }
err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX, err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX,
nl_option, team_nl_option_policy); nl_option, team_nl_option_policy);
if (err) if (err)
goto team_put; goto team_put;
if (!mode_attrs[TEAM_ATTR_OPTION_NAME] || if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
!mode_attrs[TEAM_ATTR_OPTION_TYPE] || !opt_attrs[TEAM_ATTR_OPTION_TYPE] ||
!mode_attrs[TEAM_ATTR_OPTION_DATA]) { !opt_attrs[TEAM_ATTR_OPTION_DATA]) {
err = -EINVAL; err = -EINVAL;
goto team_put; goto team_put;
} }
switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) { switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
case NLA_U32: case NLA_U32:
opt_type = TEAM_OPTION_TYPE_U32; opt_type = TEAM_OPTION_TYPE_U32;
break; break;
...@@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
goto team_put; goto team_put;
} }
opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]); opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
list_for_each_entry(option, &team->option_list, list) { attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
long arg; if (attr_port_ifindex)
opt_port_ifindex = nla_get_u32(attr_port_ifindex);
list_for_each_entry(opt_inst, &team->option_inst_list, list) {
struct team_option *option = opt_inst->option;
struct nlattr *opt_data_attr; struct nlattr *opt_data_attr;
struct team_option_binary tbinary; struct team_gsetter_ctx ctx;
int data_len; int data_len;
int tmp_ifindex;
tmp_ifindex = opt_inst->port ?
opt_inst->port->dev->ifindex : 0;
if (option->type != opt_type || if (option->type != opt_type ||
strcmp(option->name, opt_name)) strcmp(option->name, opt_name) ||
tmp_ifindex != opt_port_ifindex)
continue; continue;
opt_found = true; opt_found = true;
opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA]; opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA];
data_len = nla_len(opt_data_attr); data_len = nla_len(opt_data_attr);
ctx.port = opt_inst->port;
switch (opt_type) { switch (opt_type) {
case TEAM_OPTION_TYPE_U32: case TEAM_OPTION_TYPE_U32:
arg = nla_get_u32(opt_data_attr); ctx.data.u32_val = nla_get_u32(opt_data_attr);
break; break;
case TEAM_OPTION_TYPE_STRING: case TEAM_OPTION_TYPE_STRING:
if (data_len > TEAM_STRING_MAX_LEN) { if (data_len > TEAM_STRING_MAX_LEN) {
err = -EINVAL; err = -EINVAL;
goto team_put; goto team_put;
} }
arg = (long) nla_data(opt_data_attr); ctx.data.str_val = nla_data(opt_data_attr);
break; break;
case TEAM_OPTION_TYPE_BINARY: case TEAM_OPTION_TYPE_BINARY:
tbinary.data_len = data_len; ctx.data.bin_val.len = data_len;
tbinary.data = nla_data(opt_data_attr); ctx.data.bin_val.ptr = nla_data(opt_data_attr);
arg = (long) &tbinary;
break; break;
default: default:
BUG(); BUG();
} }
err = team_option_set(team, option, &arg); err = team_option_set(team, opt_inst, &ctx);
if (err) if (err)
goto team_put; goto team_put;
} }
......
...@@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port) ...@@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port)
RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); RCU_INIT_POINTER(ab_priv(team)->active_port, NULL);
} }
static int ab_active_port_get(struct team *team, void *arg) static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx)
{ {
u32 *ifindex = arg;
*ifindex = 0;
if (ab_priv(team)->active_port) if (ab_priv(team)->active_port)
*ifindex = ab_priv(team)->active_port->dev->ifindex; ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex;
else
ctx->data.u32_val = 0;
return 0; return 0;
} }
static int ab_active_port_set(struct team *team, void *arg) static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx)
{ {
u32 *ifindex = arg;
struct team_port *port; struct team_port *port;
list_for_each_entry_rcu(port, &team->port_list, list) { list_for_each_entry_rcu(port, &team->port_list, list) {
if (port->dev->ifindex == *ifindex) { if (port->dev->ifindex == ctx->data.u32_val) {
rcu_assign_pointer(ab_priv(team)->active_port, port); rcu_assign_pointer(ab_priv(team)->active_port, port);
return 0; return 0;
} }
......
...@@ -52,22 +52,21 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb) ...@@ -52,22 +52,21 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb)
return false; return false;
} }
static int lb_bpf_func_get(struct team *team, void *arg) static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
{ {
struct team_option_binary *tbinary = team_optarg_tbinary(arg); if (!lb_priv(team)->orig_fprog) {
ctx->data.bin_val.len = 0;
memset(tbinary, 0, sizeof(*tbinary)); ctx->data.bin_val.ptr = NULL;
if (!lb_priv(team)->orig_fprog)
return 0; return 0;
}
tbinary->data_len = lb_priv(team)->orig_fprog->len * ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len *
sizeof(struct sock_filter); sizeof(struct sock_filter);
tbinary->data = lb_priv(team)->orig_fprog->filter; ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter;
return 0; return 0;
} }
static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
void *data) const void *data)
{ {
struct sock_fprog *fprog; struct sock_fprog *fprog;
struct sock_filter *filter = (struct sock_filter *) data; struct sock_filter *filter = (struct sock_filter *) data;
...@@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog) ...@@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog)
kfree(fprog); kfree(fprog);
} }
static int lb_bpf_func_set(struct team *team, void *arg) static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
{ {
struct team_option_binary *tbinary = team_optarg_tbinary(arg);
struct sk_filter *fp = NULL; struct sk_filter *fp = NULL;
struct sock_fprog *fprog = NULL; struct sock_fprog *fprog = NULL;
int err; int err;
if (tbinary->data_len) { if (ctx->data.bin_val.len) {
err = __fprog_create(&fprog, tbinary->data_len, err = __fprog_create(&fprog, ctx->data.bin_val.len,
tbinary->data); ctx->data.bin_val.ptr);
if (err) if (err)
return err; return err;
err = sk_unattached_filter_create(&fp, fprog); err = sk_unattached_filter_create(&fp, fprog);
......
...@@ -71,25 +71,27 @@ enum team_option_type { ...@@ -71,25 +71,27 @@ enum team_option_type {
TEAM_OPTION_TYPE_BINARY, TEAM_OPTION_TYPE_BINARY,
}; };
struct team_gsetter_ctx {
union {
u32 u32_val;
const char *str_val;
struct {
const void *ptr;
u32 len;
} bin_val;
} data;
struct team_port *port;
};
struct team_option { struct team_option {
struct list_head list; struct list_head list;
const char *name; const char *name;
bool per_port;
enum team_option_type type; enum team_option_type type;
int (*getter)(struct team *team, void *arg); int (*getter)(struct team *team, struct team_gsetter_ctx *ctx);
int (*setter)(struct team *team, void *arg); int (*setter)(struct team *team, struct team_gsetter_ctx *ctx);
/* Custom gennetlink interface related flags */
bool changed;
bool removed;
}; };
struct team_option_binary {
u32 data_len;
void *data;
};
#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg))
struct team_mode { struct team_mode {
struct list_head list; struct list_head list;
const char *kind; const char *kind;
...@@ -118,6 +120,7 @@ struct team { ...@@ -118,6 +120,7 @@ struct team {
struct list_head port_list; struct list_head port_list;
struct list_head option_list; struct list_head option_list;
struct list_head option_inst_list; /* list of option instances */
const struct team_mode *mode; const struct team_mode *mode;
struct team_mode_ops ops; struct team_mode_ops ops;
...@@ -224,6 +227,7 @@ enum { ...@@ -224,6 +227,7 @@ enum {
TEAM_ATTR_OPTION_TYPE, /* u8 */ TEAM_ATTR_OPTION_TYPE, /* u8 */
TEAM_ATTR_OPTION_DATA, /* dynamic */ TEAM_ATTR_OPTION_DATA, /* dynamic */
TEAM_ATTR_OPTION_REMOVED, /* flag */ TEAM_ATTR_OPTION_REMOVED, /* flag */
TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */
__TEAM_ATTR_OPTION_MAX, __TEAM_ATTR_OPTION_MAX,
TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment