Skip to content

Commit

Permalink
team: send only changed options/ports via netlink
Browse files Browse the repository at this point in the history
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: Jiri Pirko <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Jiri Pirko authored and davem330 committed Jan 24, 2012
1 parent c11bf1c commit b82b918
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 46 deletions.
136 changes: 90 additions & 46 deletions drivers/net/team/team.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name)
return NULL;
}

int team_options_register(struct team *team,
const struct team_option *option,
size_t option_count)
int __team_options_register(struct team *team,
const struct team_option *option,
size_t option_count)
{
int i;
struct team_option **dst_opts;
Expand All @@ -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);
}

kfree(dst_opts);
return 0;
Expand All @@ -130,10 +133,22 @@ int team_options_register(struct team *team,
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,
struct team_option *changed_option);
del_opt = __team_find_option(team, option->name);
if (del_opt) {
del_opt->changed = true;
del_opt->removed = true;
}
}
}

static void __team_options_unregister(struct team *team,
const struct team_option *option,
Expand All @@ -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,
const struct team_option *option,
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_change_check(team, NULL);
}
EXPORT_SYMBOL(team_options_unregister);

Expand All @@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option,
if (err)
return err;

__team_options_change_check(team, option);
option->changed = true;
__team_options_change_check(team);
return err;
}

Expand Down Expand Up @@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
return -ENOENT;
}

port->removed = true;
__team_port_change_check(port, false);
team_port_list_del_port(team, port);
team_adjust_ops(team);
Expand Down Expand Up @@ -1200,10 +1234,9 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team,
return err;
}

static int team_nl_fill_options_get_changed(struct sk_buff *skb,
u32 pid, u32 seq, int flags,
struct team *team,
struct team_option *changed_option)
static int team_nl_fill_options_get(struct sk_buff *skb,
u32 pid, u32 seq, int flags,
struct team *team, bool fillall)
{
struct nlattr *option_list;
void *hdr;
Expand All @@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
struct nlattr *option_item;
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);
if (!option_item)
goto nla_put_failure;
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);
option->changed = false;
}
if (option->removed)
NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED);
switch (option->type) {
case TEAM_OPTION_TYPE_U32:
NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
Expand All @@ -1255,13 +1295,13 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb,
return -EMSGSIZE;
}

static int team_nl_fill_options_get(struct sk_buff *skb,
struct genl_info *info, int flags,
struct team *team)
static int team_nl_fill_options_get_all(struct sk_buff *skb,
struct genl_info *info, int flags,
struct team *team)
{
return team_nl_fill_options_get_changed(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK,
team, NULL);
return team_nl_fill_options_get(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK,
team, true);
}

static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
Expand All @@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
if (!team)
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);

Expand Down Expand Up @@ -1365,10 +1405,10 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
return err;
}

static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
u32 pid, u32 seq, int flags,
struct team *team,
struct team_port *changed_port)
static int team_nl_fill_port_list_get(struct sk_buff *skb,
u32 pid, u32 seq, int flags,
struct team *team,
bool fillall)
{
struct nlattr *port_list;
void *hdr;
Expand All @@ -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) {
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);
if (!port_item)
goto nla_put_failure;
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);
port->changed = false;
}
if (port->removed)
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED);
if (port->linkup)
NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
Expand All @@ -1408,13 +1455,13 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
return -EMSGSIZE;
}

static int team_nl_fill_port_list_get(struct sk_buff *skb,
struct genl_info *info, int flags,
struct team *team)
static int team_nl_fill_port_list_get_all(struct sk_buff *skb,
struct genl_info *info, int flags,
struct team *team)
{
return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK,
team, NULL);
return team_nl_fill_port_list_get(skb, info->snd_pid,
info->snd_seq, NLM_F_ACK,
team, true);
}

static int team_nl_cmd_port_list_get(struct sk_buff *skb,
Expand All @@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
if (!team)
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);

Expand Down Expand Up @@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = {
.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
};

static int team_nl_send_event_options_get(struct team *team,
struct team_option *changed_option)
static int team_nl_send_event_options_get(struct team *team)
{
struct sk_buff *skb;
int err;
Expand All @@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team,
if (!skb)
return -ENOMEM;

err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
changed_option);
err = team_nl_fill_options_get(skb, 0, 0, 0, team, false);
if (err < 0)
goto err_fill;

Expand All @@ -1489,18 +1534,17 @@ static int team_nl_send_event_options_get(struct team *team,
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;
int err;
struct net *net = dev_net(port->team->dev);
struct net *net = dev_net(team->dev);

skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;

err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
port->team, port);
err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false);
if (err < 0)
goto err_fill;

Expand Down Expand Up @@ -1544,12 +1588,11 @@ static void team_nl_fini(void)
* Change checkers
******************/

static void __team_options_change_check(struct team *team,
struct team_option *changed_option)
static void __team_options_change_check(struct team *team)
{
int err;

err = team_nl_send_event_options_get(team, changed_option);
err = team_nl_send_event_options_get(team);
if (err)
netdev_warn(team->dev, "Failed to send options change via netlink\n");
}
Expand All @@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
{
int err;

if (port->linkup == linkup)
if (!port->removed && port->linkup == linkup)
return;

port->changed = true;
port->linkup = linkup;
if (linkup) {
struct ethtool_cmd ecmd;
Expand All @@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup)
port->duplex = 0;

send_event:
err = team_nl_send_event_port_list_get(port);
err = team_nl_send_event_port_list_get(port->team);
if (err)
netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
port->dev->name);
Expand Down
10 changes: 10 additions & 0 deletions include/linux/if_team.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ struct team_port {
u32 speed;
u8 duplex;

/* Custom gennetlink interface related flags */
bool changed;
bool removed;

struct rcu_head rcu;
};

Expand All @@ -72,6 +76,10 @@ struct team_option {
enum team_option_type type;
int (*getter)(struct team *team, void *arg);
int (*setter)(struct team *team, void *arg);

/* Custom gennetlink interface related flags */
bool changed;
bool removed;
};

struct team_mode {
Expand Down Expand Up @@ -207,6 +215,7 @@ enum {
TEAM_ATTR_OPTION_CHANGED, /* flag */
TEAM_ATTR_OPTION_TYPE, /* u8 */
TEAM_ATTR_OPTION_DATA, /* dynamic */
TEAM_ATTR_OPTION_REMOVED, /* flag */

__TEAM_ATTR_OPTION_MAX,
TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
Expand All @@ -227,6 +236,7 @@ enum {
TEAM_ATTR_PORT_LINKUP, /* flag */
TEAM_ATTR_PORT_SPEED, /* u32 */
TEAM_ATTR_PORT_DUPLEX, /* u8 */
TEAM_ATTR_PORT_REMOVED, /* flag */

__TEAM_ATTR_PORT_MAX,
TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
Expand Down

0 comments on commit b82b918

Please sign in to comment.