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

team: send only changed options/ports via netlink

This patch changes event message behaviour to send only updated records
instead of whole list. This fixes bug on which userspace receives non-actual
data in case multiple events occur in row.
Signed-off-by: default avatarJiri Pirko <jpirko@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c11bf1c8
...@@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) ...@@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
return NULL; return NULL;
} }
int team_options_register(struct team *team, 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)
{ {
int i; int i;
struct team_option **dst_opts; struct team_option **dst_opts;
...@@ -116,8 +116,11 @@ int team_options_register(struct team *team, ...@@ -116,8 +116,11 @@ int team_options_register(struct team *team,
} }
} }
for (i = 0; i < option_count; i++) for (i = 0; i < option_count; i++) {
dst_opts[i]->changed = true;
dst_opts[i]->removed = false;
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;
...@@ -130,10 +133,22 @@ int team_options_register(struct team *team, ...@@ -130,10 +133,22 @@ int team_options_register(struct team *team,
return err; return err;
} }
EXPORT_SYMBOL(team_options_register); static void __team_options_mark_removed(struct team *team,
const struct team_option *option,
size_t option_count)
{
int i;
for (i = 0; i < option_count; i++, option++) {
struct team_option *del_opt;
static void __team_options_change_check(struct team *team, del_opt = __team_find_option(team, option->name);
struct team_option *changed_option); if (del_opt) {
del_opt->changed = true;
del_opt->removed = true;
}
}
}
static void __team_options_unregister(struct team *team, static void __team_options_unregister(struct team *team,
const struct team_option *option, const struct team_option *option,
...@@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team, ...@@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team,
} }
} }
static void __team_options_change_check(struct team *team);
int team_options_register(struct team *team,
const struct team_option *option,
size_t option_count)
{
int err;
err = __team_options_register(team, option, option_count);
if (err)
return err;
__team_options_change_check(team);
return 0;
}
EXPORT_SYMBOL(team_options_register);
void team_options_unregister(struct team *team, void team_options_unregister(struct team *team,
const struct team_option *option, const struct team_option *option,
size_t option_count) size_t option_count)
{ {
__team_options_mark_removed(team, option, option_count);
__team_options_change_check(team);
__team_options_unregister(team, option, option_count); __team_options_unregister(team, option, option_count);
__team_options_change_check(team, NULL);
} }
EXPORT_SYMBOL(team_options_unregister); EXPORT_SYMBOL(team_options_unregister);
...@@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option, ...@@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option,
if (err) if (err)
return err; return err;
__team_options_change_check(team, option); option->changed = true;
__team_options_change_check(team);
return err; return err;
} }
...@@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) ...@@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
return -ENOENT; return -ENOENT;
} }
port->removed = true;
__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);
...@@ -1200,10 +1234,9 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team, ...@@ -1200,10 +1234,9 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team,
return err; return err;
} }
static int team_nl_fill_options_get_changed(struct sk_buff *skb, static int team_nl_fill_options_get(struct sk_buff *skb,
u32 pid, u32 seq, int flags, u32 pid, u32 seq, int flags,
struct team *team, struct team *team, bool fillall)
struct team_option *changed_option)
{ {
struct nlattr *option_list; struct nlattr *option_list;
void *hdr; void *hdr;
...@@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb, ...@@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
struct nlattr *option_item; struct nlattr *option_item;
long arg; long arg;
/* Include only changed options if fill all mode is not on */
if (!fillall && !option->changed)
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;
NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name); NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
if (option == changed_option) if (option->changed) {
NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED); NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
option->changed = false;
}
if (option->removed)
NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
switch (option->type) { switch (option->type) {
case TEAM_OPTION_TYPE_U32: case TEAM_OPTION_TYPE_U32:
NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32); NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
...@@ -1255,13 +1295,13 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb, ...@@ -1255,13 +1295,13 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
return -EMSGSIZE; return -EMSGSIZE;
} }
static int team_nl_fill_options_get(struct sk_buff *skb, static int team_nl_fill_options_get_all(struct sk_buff *skb,
struct genl_info *info, int flags, struct genl_info *info, int flags,
struct team *team) struct team *team)
{ {
return team_nl_fill_options_get_changed(skb, info->snd_pid, return team_nl_fill_options_get(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK, info->snd_seq, NLM_F_ACK,
team, NULL); team, true);
} }
static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
...@@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) ...@@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
if (!team) if (!team)
return -EINVAL; return -EINVAL;
err = team_nl_send_generic(info, team, team_nl_fill_options_get); err = team_nl_send_generic(info, team, team_nl_fill_options_get_all);
team_nl_team_put(team); team_nl_team_put(team);
...@@ -1365,10 +1405,10 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1365,10 +1405,10 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
return err; return err;
} }
static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, static int team_nl_fill_port_list_get(struct sk_buff *skb,
u32 pid, u32 seq, int flags, u32 pid, u32 seq, int flags,
struct team *team, struct team *team,
struct team_port *changed_port) bool fillall)
{ {
struct nlattr *port_list; struct nlattr *port_list;
void *hdr; void *hdr;
...@@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, ...@@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
list_for_each_entry(port, &team->port_list, list) { list_for_each_entry(port, &team->port_list, list) {
struct nlattr *port_item; struct nlattr *port_item;
/* Include only changed ports if fill all mode is not on */
if (!fillall && !port->changed)
continue;
port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
if (!port_item) if (!port_item)
goto nla_put_failure; goto nla_put_failure;
NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex); NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
if (port == changed_port) if (port->changed) {
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED); NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
port->changed = false;
}
if (port->removed)
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
if (port->linkup) if (port->linkup)
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP); NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed); NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
...@@ -1408,13 +1455,13 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, ...@@ -1408,13 +1455,13 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
return -EMSGSIZE; return -EMSGSIZE;
} }
static int team_nl_fill_port_list_get(struct sk_buff *skb, static int team_nl_fill_port_list_get_all(struct sk_buff *skb,
struct genl_info *info, int flags, struct genl_info *info, int flags,
struct team *team) struct team *team)
{ {
return team_nl_fill_port_list_get_changed(skb, info->snd_pid, return team_nl_fill_port_list_get(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK, info->snd_seq, NLM_F_ACK,
team, NULL); team, true);
} }
static int team_nl_cmd_port_list_get(struct sk_buff *skb, static int team_nl_cmd_port_list_get(struct sk_buff *skb,
...@@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb, ...@@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
if (!team) if (!team)
return -EINVAL; return -EINVAL;
err = team_nl_send_generic(info, team, team_nl_fill_port_list_get); err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all);
team_nl_team_put(team); team_nl_team_put(team);
...@@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = { ...@@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = {
.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
}; };
static int team_nl_send_event_options_get(struct team *team, static int team_nl_send_event_options_get(struct team *team)
struct team_option *changed_option)
{ {
struct sk_buff *skb; struct sk_buff *skb;
int err; int err;
...@@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team, ...@@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team,
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team, err = team_nl_fill_options_get(skb, 0, 0, 0, team, false);
changed_option);
if (err < 0) if (err < 0)
goto err_fill; goto err_fill;
...@@ -1489,18 +1534,17 @@ static int team_nl_send_event_options_get(struct team *team, ...@@ -1489,18 +1534,17 @@ static int team_nl_send_event_options_get(struct team *team,
return err; return err;
} }
static int team_nl_send_event_port_list_get(struct team_port *port) static int team_nl_send_event_port_list_get(struct team *team)
{ {
struct sk_buff *skb; struct sk_buff *skb;
int err; int err;
struct net *net = dev_net(port->team->dev); struct net *net = dev_net(team->dev);
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0, err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false);
port->team, port);
if (err < 0) if (err < 0)
goto err_fill; goto err_fill;
...@@ -1544,12 +1588,11 @@ static void team_nl_fini(void) ...@@ -1544,12 +1588,11 @@ static void team_nl_fini(void)
* Change checkers * Change checkers
******************/ ******************/
static void __team_options_change_check(struct team *team, static void __team_options_change_check(struct team *team)
struct team_option *changed_option)
{ {
int err; int err;
err = team_nl_send_event_options_get(team, changed_option); err = team_nl_send_event_options_get(team);
if (err) if (err)
netdev_warn(team->dev, "Failed to send options change via netlink\n"); netdev_warn(team->dev, "Failed to send options change via netlink\n");
} }
...@@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup) ...@@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
{ {
int err; int err;
if (port->linkup == linkup) if (!port->removed && port->linkup == linkup)
return; return;
port->changed = true;
port->linkup = linkup; port->linkup = linkup;
if (linkup) { if (linkup) {
struct ethtool_cmd ecmd; struct ethtool_cmd ecmd;
...@@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup) ...@@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
port->duplex = 0; port->duplex = 0;
send_event: send_event:
err = team_nl_send_event_port_list_get(port); err = team_nl_send_event_port_list_get(port->team);
if (err) if (err)
netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n", netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
port->dev->name); port->dev->name);
......
...@@ -46,6 +46,10 @@ struct team_port { ...@@ -46,6 +46,10 @@ struct team_port {
u32 speed; u32 speed;
u8 duplex; u8 duplex;
/* Custom gennetlink interface related flags */
bool changed;
bool removed;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -72,6 +76,10 @@ struct team_option { ...@@ -72,6 +76,10 @@ struct team_option {
enum team_option_type type; enum team_option_type type;
int (*getter)(struct team *team, void *arg); int (*getter)(struct team *team, void *arg);
int (*setter)(struct team *team, void *arg); int (*setter)(struct team *team, void *arg);
/* Custom gennetlink interface related flags */
bool changed;
bool removed;
}; };
struct team_mode { struct team_mode {
...@@ -207,6 +215,7 @@ enum { ...@@ -207,6 +215,7 @@ enum {
TEAM_ATTR_OPTION_CHANGED, /* flag */ TEAM_ATTR_OPTION_CHANGED, /* flag */
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_MAX, __TEAM_ATTR_OPTION_MAX,
TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
...@@ -227,6 +236,7 @@ enum { ...@@ -227,6 +236,7 @@ enum {
TEAM_ATTR_PORT_LINKUP, /* flag */ TEAM_ATTR_PORT_LINKUP, /* flag */
TEAM_ATTR_PORT_SPEED, /* u32 */ TEAM_ATTR_PORT_SPEED, /* u32 */
TEAM_ATTR_PORT_DUPLEX, /* u8 */ TEAM_ATTR_PORT_DUPLEX, /* u8 */
TEAM_ATTR_PORT_REMOVED, /* flag */
__TEAM_ATTR_PORT_MAX, __TEAM_ATTR_PORT_MAX,
TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1, TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_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