Update team driver to 3.9-rc1.
Split patches available here: http://people.redhat.com/jpirko/f18_team_update_4/
Flavio Leitner (5): team: implement carrier change team: add ethtool support team: update master carrier state team: use strlcpy with ethtool_drvinfo fields team: allow userspace to take control over carrier
Jiri Pirko (5): rtnl: expose carrier value with possibility to set it net: add change_carrier netdev op team: handle sending port list in the same way option list is sent team: move netlink event notifiers after team_port_leave() team: ab: set active port option as changed when port is leaving
Documentation/networking/operstates.txt | 4 + drivers/net/team/team.c | 246 +++++++++++++++++++----------- drivers/net/team/team_mode_activebackup.c | 13 +- include/linux/if_team.h | 1 + include/linux/netdevice.h | 12 ++ include/uapi/linux/if_link.h | 1 + net/core/dev.c | 19 +++ net/core/rtnetlink.c | 10 ++ 8 files changed, 215 insertions(+), 91 deletions(-)
Signed-off-by: Jiri Pirko jpirko@redhat.com
diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt index 1a77a3c..9769457 100644 --- a/Documentation/networking/operstates.txt +++ b/Documentation/networking/operstates.txt @@ -88,6 +88,10 @@ set this flag. On netif_carrier_off(), the scheduler stops sending packets. The name 'carrier' and the inversion are historical, think of it as lower layer.
+Note that for certain kind of soft-devices, which are not managing any +real hardware, there is possible to set this bit from userpsace. +One should use TVL IFLA_CARRIER to do so. + netif_carrier_ok() can be used to query that bit.
__LINK_STATE_DORMANT, maps to IFF_DORMANT: diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ad86660..9e68014 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -28,6 +28,7 @@ #include <net/genetlink.h> #include <net/netlink.h> #include <net/sch_generic.h> +#include <generated/utsrelease.h> #include <linux/if_team.h>
#define DRV_NAME "team" @@ -507,6 +508,7 @@ static bool team_is_mode_set(struct team *team)
static void team_set_no_mode(struct team *team) { + team->user_carrier_enabled = false; team->mode = &__team_no_mode; }
@@ -1129,10 +1131,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev) return -ENOENT; }
- __team_option_inst_mark_removed_port(team, port); - __team_options_change_check(team); - __team_option_inst_del_port(team, port); - __team_port_change_port_removed(port); team_port_disable(team, port); list_del_rcu(&port->list); netdev_rx_handler_unregister(port_dev); @@ -1141,6 +1139,12 @@ static int team_port_del(struct team *team, struct net_device *port_dev) vlan_vids_del_by_dev(port_dev, dev); dev_close(port_dev); team_port_leave(team, port); + + __team_option_inst_mark_removed_port(team, port); + __team_options_change_check(team); + __team_option_inst_del_port(team, port); + __team_port_change_port_removed(port); + team_port_set_orig_dev_addr(port); dev_set_mtu(port_dev, port->orig.mtu); synchronize_rcu(); @@ -1399,13 +1403,11 @@ static void team_destructor(struct net_device *dev)
static int team_open(struct net_device *dev) { - netif_carrier_on(dev); return 0; }
static int team_close(struct net_device *dev) { - netif_carrier_off(dev); return 0; }
@@ -1707,6 +1709,19 @@ static netdev_features_t team_fix_features(struct net_device *dev, return features; }
+static int team_change_carrier(struct net_device *dev, bool new_carrier) +{ + struct team *team = netdev_priv(dev); + + team->user_carrier_enabled = true; + + if (new_carrier) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + static const struct net_device_ops team_netdev_ops = { .ndo_init = team_init, .ndo_uninit = team_uninit, @@ -1729,8 +1744,24 @@ static const struct net_device_ops team_netdev_ops = { .ndo_add_slave = team_add_slave, .ndo_del_slave = team_del_slave, .ndo_fix_features = team_fix_features, + .ndo_change_carrier = team_change_carrier, };
+/*********************** + * ethtool interface + ***********************/ + +static void team_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); +} + +static const struct ethtool_ops team_ethtool_ops = { + .get_drvinfo = team_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, +};
/*********************** * rt netlink interface @@ -1780,6 +1811,7 @@ static void team_setup(struct net_device *dev) ether_setup(dev);
dev->netdev_ops = &team_netdev_ops; + dev->ethtool_ops = &team_ethtool_ops; dev->destructor = team_destructor; dev->tx_queue_len = 0; dev->flags |= IFF_MULTICAST; @@ -1941,30 +1973,6 @@ static void team_nl_team_put(struct team *team) dev_put(team->dev); }
-static int team_nl_send_generic(struct genl_info *info, struct team *team, - int (*fill_func)(struct sk_buff *skb, - struct genl_info *info, - int flags, struct team *team)) -{ - struct sk_buff *skb; - int err; - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - err = fill_func(skb, info, NLM_F_ACK, team); - if (err < 0) - goto err_fill; - - err = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid); - return err; - -err_fill: - nlmsg_free(skb); - return err; -} - typedef int team_nl_send_func_t(struct sk_buff *skb, struct team *team, u32 portid);
@@ -2309,16 +2317,57 @@ team_put: return err; }
-static int team_nl_fill_port_list_get(struct sk_buff *skb, - u32 portid, u32 seq, int flags, - struct team *team, - bool fillall) +static int team_nl_fill_one_port_get(struct sk_buff *skb, + struct team_port *port) +{ + struct nlattr *port_item; + + port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); + if (!port_item) + goto nest_cancel; + if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) + goto nest_cancel; + if (port->changed) { + if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED)) + goto nest_cancel; + port->changed = false; + } + if ((port->removed && + nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || + (port->state.linkup && + nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || + nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || + nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) + goto nest_cancel; + nla_nest_end(skb, port_item); + return 0; + +nest_cancel: + nla_nest_cancel(skb, port_item); + return -EMSGSIZE; +} + +static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq, + int flags, team_nl_send_func_t *send_func, + struct team_port *one_port) { struct nlattr *port_list; + struct nlmsghdr *nlh; void *hdr; struct team_port *port; + int err; + struct sk_buff *skb = NULL; + bool incomplete; + int i; + + port = list_first_entry(&team->port_list, struct team_port, list);
- hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags, +start_again: + err = __send_and_alloc_skb(&skb, team, portid, send_func); + if (err) + return err; + + hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI, TEAM_CMD_PORT_LIST_GET); if (!hdr) return -EMSGSIZE; @@ -2329,47 +2378,54 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, if (!port_list) goto nla_put_failure;
- list_for_each_entry(port, &team->port_list, list) { - struct nlattr *port_item; + i = 0; + incomplete = false;
- /* 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; - if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) - goto nla_put_failure; - if (port->changed) { - if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED)) - goto nla_put_failure; - port->changed = false; + /* If one port is selected, called wants to send port list containing + * only this port. Otherwise go through all listed ports and send all + */ + if (one_port) { + err = team_nl_fill_one_port_get(skb, one_port); + if (err) + goto errout; + } else { + list_for_each_entry(port, &team->port_list, list) { + err = team_nl_fill_one_port_get(skb, port); + if (err) { + if (err == -EMSGSIZE) { + if (!i) + goto errout; + incomplete = true; + break; + } + goto errout; + } + i++; } - if ((port->removed && - nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || - (port->state.linkup && - nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || - nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || - nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) - goto nla_put_failure; - nla_nest_end(skb, port_item); }
nla_nest_end(skb, port_list); - return genlmsg_end(skb, hdr); + genlmsg_end(skb, hdr); + if (incomplete) + goto start_again; + +send_done: + nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); + if (!nlh) { + err = __send_and_alloc_skb(&skb, team, portid, send_func); + if (err) + goto errout; + goto send_done; + } + + return send_func(skb, team, portid);
nla_put_failure: + err = -EMSGSIZE; +errout: genlmsg_cancel(skb, hdr); - return -EMSGSIZE; -} - -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(skb, info->snd_portid, - info->snd_seq, NLM_F_ACK, - team, true); + nlmsg_free(skb); + return err; }
static int team_nl_cmd_port_list_get(struct sk_buff *skb, @@ -2382,7 +2438,8 @@ 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_all); + err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq, + NLM_F_ACK, team_nl_send_unicast, NULL);
team_nl_team_put(team);
@@ -2433,27 +2490,11 @@ static int team_nl_send_event_options_get(struct team *team, sel_opt_inst_list); }
-static int team_nl_send_event_port_list_get(struct team *team) +static int team_nl_send_event_port_get(struct team *team, + struct team_port *port) { - struct sk_buff *skb; - int err; - struct net *net = dev_net(team->dev); - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false); - if (err < 0) - goto err_fill; - - err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id, - GFP_KERNEL); - return err; - -err_fill: - nlmsg_free(skb); - return err; + return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast, + port); }
static int team_nl_init(void) @@ -2526,28 +2567,53 @@ static void __team_port_change_send(struct team_port *port, bool linkup) port->state.duplex = 0;
send_event: - err = team_nl_send_event_port_list_get(port->team); + err = team_nl_send_event_port_get(port->team, port); if (err && err != -ESRCH) netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n", port->dev->name, err);
}
+static void __team_carrier_check(struct team *team) +{ + struct team_port *port; + bool team_linkup; + + if (team->user_carrier_enabled) + return; + + team_linkup = false; + list_for_each_entry(port, &team->port_list, list) { + if (port->linkup) { + team_linkup = true; + break; + } + } + + if (team_linkup) + netif_carrier_on(team->dev); + else + netif_carrier_off(team->dev); +} + static void __team_port_change_check(struct team_port *port, bool linkup) { if (port->state.linkup != linkup) __team_port_change_send(port, linkup); + __team_carrier_check(port->team); }
static void __team_port_change_port_added(struct team_port *port, bool linkup) { __team_port_change_send(port, linkup); + __team_carrier_check(port->team); }
static void __team_port_change_port_removed(struct team_port *port) { port->removed = true; __team_port_change_send(port, false); + __team_carrier_check(port->team); }
static void team_port_change_check(struct team_port *port, bool linkup) diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index 6262b4d..40fd338 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -19,6 +19,7 @@
struct ab_priv { struct team_port __rcu *active_port; + struct team_option_inst_info *ap_opt_inst_info; };
static struct ab_priv *ab_priv(struct team *team) @@ -54,8 +55,17 @@ drop:
static void ab_port_leave(struct team *team, struct team_port *port) { - if (ab_priv(team)->active_port == port) + if (ab_priv(team)->active_port == port) { RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); + team_option_inst_set_change(ab_priv(team)->ap_opt_inst_info); + } +} + +static int ab_active_port_init(struct team *team, + struct team_option_inst_info *info) +{ + ab_priv(team)->ap_opt_inst_info = info; + return 0; }
static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) @@ -88,6 +98,7 @@ static const struct team_option ab_options[] = { { .name = "activeport", .type = TEAM_OPTION_TYPE_U32, + .init = ab_active_port_init, .getter = ab_active_port_get, .setter = ab_active_port_set, }, diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 0245def..4648d80 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -186,6 +186,7 @@ struct team {
const struct team_mode *mode; struct team_mode_ops ops; + bool user_carrier_enabled; bool queue_override_enabled; struct list_head *qom_lists; /* array of queue override mapping lists */ long mode_priv[TEAM_MODE_PRIV_LONGS]; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9ef07d0..7ebddc7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -894,6 +894,14 @@ struct netdev_fcoe_hbainfo { * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh) * int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq, * struct net_device *dev) + * + * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier); + * Called to change device carrier. Soft-devices (like dummy, team, etc) + * which do not represent real hardware may define this to allow their + * userspace components to manage their virtual carrier state. Devices + * that determine carrier state from physical hardware properties (eg + * network cables) or protocol-dependent mechanisms (eg + * USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1011,6 +1019,8 @@ struct net_device_ops { int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev); + int (*ndo_change_carrier)(struct net_device *dev, + bool new_carrier); };
/* @@ -2197,6 +2207,8 @@ extern int dev_set_mtu(struct net_device *, int); extern void dev_set_group(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); +extern int dev_change_carrier(struct net_device *, + bool new_carrier); extern int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 60f3b6b..c4edfe1 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -142,6 +142,7 @@ enum { #define IFLA_PROMISCUITY IFLA_PROMISCUITY IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, __IFLA_MAX };
diff --git a/net/core/dev.c b/net/core/dev.c index f64e439..e0045ba 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5027,6 +5027,25 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) } EXPORT_SYMBOL(dev_set_mac_address);
+/** + * dev_change_carrier - Change device carrier + * @dev: device + * @new_carries: new value + * + * Change device carrier + */ +int dev_change_carrier(struct net_device *dev, bool new_carrier) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_change_carrier) + return -EOPNOTSUPP; + if (!netif_device_present(dev)) + return -ENODEV; + return ops->ndo_change_carrier(dev, new_carrier); +} +EXPORT_SYMBOL(dev_change_carrier); + /* * Perform the SIOCxIFxxx calls, inside rcu_read_lock() */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1868625..2ef7a56 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -780,6 +780,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_MTU */ + nla_total_size(4) /* IFLA_LINK */ + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(1) /* IFLA_CARRIER */ + nla_total_size(4) /* IFLA_PROMISCUITY */ + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */ + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */ @@ -909,6 +910,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u32(skb, IFLA_LINK, dev->iflink)) || (dev->master && nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || (dev->ifalias && @@ -1108,6 +1110,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_MTU] = { .type = NLA_U32 }, [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, [IFLA_TXQLEN] = { .type = NLA_U32 }, [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, @@ -1438,6 +1441,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, modified = 1; }
+ if (tb[IFLA_CARRIER]) { + err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER])); + if (err) + goto errout; + modified = 1; + } + if (tb[IFLA_TXQLEN]) dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
Update team driver to latest net-next.
Please add following config option: CONFIG_NET_TEAM_MODE_RANDOM=m
Split patches available here: http://people.redhat.com/jpirko/f18_team_update_5/
Jiri Pirko (2): team: introduce two default team_modeop functions and use them in modes team: introduce random mode
drivers/net/team/Kconfig | 12 ++++++ drivers/net/team/Makefile | 1 + drivers/net/team/team.c | 19 +++++++-- drivers/net/team/team_mode_broadcast.c | 14 +------ drivers/net/team/team_mode_random.c | 71 +++++++++++++++++++++++++++++++++ drivers/net/team/team_mode_roundrobin.c | 36 ++--------------- include/linux/if_team.h | 25 +++++++++++- 7 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 drivers/net/team/team_mode_random.c
Signed-off-by: Jiri Pirko jpirko@redhat.com
diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig index 6b08bd4..a3e36e5 100644 --- a/drivers/net/team/Kconfig +++ b/drivers/net/team/Kconfig @@ -38,6 +38,18 @@ config NET_TEAM_MODE_ROUNDROBIN To compile this team mode as a module, choose M here: the module will be called team_mode_roundrobin.
+config NET_TEAM_MODE_RANDOM + tristate "Random mode support" + depends on NET_TEAM + ---help--- + Basic mode where port used for transmitting packets is selected + randomly. + + All added ports are setup to have team's device address. + + To compile this team mode as a module, choose M here: the module + will be called team_mode_random. + config NET_TEAM_MODE_ACTIVEBACKUP tristate "Active-backup mode support" depends on NET_TEAM diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile index 9757630..c57e858 100644 --- a/drivers/net/team/Makefile +++ b/drivers/net/team/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_NET_TEAM) += team.o obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o +obj-$(CONFIG_NET_TEAM_MODE_RANDOM) += team_mode_random.o obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 9e68014..6110a0c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -73,11 +73,24 @@ static int team_port_set_orig_dev_addr(struct team_port *port) return __set_port_dev_addr(port->dev, port->orig.dev_addr); }
-int team_port_set_team_dev_addr(struct team_port *port) +static int team_port_set_team_dev_addr(struct team *team, + struct team_port *port) +{ + return __set_port_dev_addr(port->dev, team->dev->dev_addr); +} + +int team_modeop_port_enter(struct team *team, struct team_port *port) +{ + return team_port_set_team_dev_addr(team, port); +} +EXPORT_SYMBOL(team_modeop_port_enter); + +void team_modeop_port_change_dev_addr(struct team *team, + struct team_port *port) { - return __set_port_dev_addr(port->dev, port->team->dev->dev_addr); + team_port_set_team_dev_addr(team, port); } -EXPORT_SYMBOL(team_port_set_team_dev_addr); +EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
static void team_refresh_port_linkup(struct team_port *port) { diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c index c5db428..c366cd2 100644 --- a/drivers/net/team/team_mode_broadcast.c +++ b/drivers/net/team/team_mode_broadcast.c @@ -46,20 +46,10 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb) return sum_ret; }
-static int bc_port_enter(struct team *team, struct team_port *port) -{ - return team_port_set_team_dev_addr(port); -} - -static void bc_port_change_dev_addr(struct team *team, struct team_port *port) -{ - team_port_set_team_dev_addr(port); -} - static const struct team_mode_ops bc_mode_ops = { .transmit = bc_transmit, - .port_enter = bc_port_enter, - .port_change_dev_addr = bc_port_change_dev_addr, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, };
static const struct team_mode bc_mode = { diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c new file mode 100644 index 0000000..9eabfaa --- /dev/null +++ b/drivers/net/team/team_mode_random.c @@ -0,0 +1,71 @@ +/* + * drivers/net/team/team_mode_random.c - Random mode for team + * Copyright (c) 2013 Jiri Pirko jiri@resnulli.us + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/skbuff.h> +#include <linux/reciprocal_div.h> +#include <linux/if_team.h> + +static u32 random_N(unsigned int N) +{ + return reciprocal_divide(random32(), N); +} + +static bool rnd_transmit(struct team *team, struct sk_buff *skb) +{ + struct team_port *port; + int port_index; + + port_index = random_N(team->en_port_count); + port = team_get_port_by_index_rcu(team, port_index); + port = team_get_first_port_txable_rcu(team, port); + if (unlikely(!port)) + goto drop; + if (team_dev_queue_xmit(team, port, skb)) + return false; + return true; + +drop: + dev_kfree_skb_any(skb); + return false; +} + +static const struct team_mode_ops rnd_mode_ops = { + .transmit = rnd_transmit, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, +}; + +static const struct team_mode rnd_mode = { + .kind = "random", + .owner = THIS_MODULE, + .ops = &rnd_mode_ops, +}; + +static int __init rnd_init_module(void) +{ + return team_mode_register(&rnd_mode); +} + +static void __exit rnd_cleanup_module(void) +{ + team_mode_unregister(&rnd_mode); +} + +module_init(rnd_init_module); +module_exit(rnd_cleanup_module); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jiri Pirko jiri@resnulli.us"); +MODULE_DESCRIPTION("Random mode for team"); +MODULE_ALIAS("team-mode-random"); diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 105135a..d268e4d 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -25,26 +25,6 @@ static struct rr_priv *rr_priv(struct team *team) return (struct rr_priv *) &team->mode_priv; }
-static struct team_port *__get_first_port_up(struct team *team, - struct team_port *port) -{ - struct team_port *cur; - - if (team_port_txable(port)) - return port; - cur = port; - list_for_each_entry_continue_rcu(cur, &team->port_list, list) - if (team_port_txable(port)) - return cur; - list_for_each_entry_rcu(cur, &team->port_list, list) { - if (cur == port) - break; - if (team_port_txable(port)) - return cur; - } - return NULL; -} - static bool rr_transmit(struct team *team, struct sk_buff *skb) { struct team_port *port; @@ -52,7 +32,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb)
port_index = rr_priv(team)->sent_packets++ % team->en_port_count; port = team_get_port_by_index_rcu(team, port_index); - port = __get_first_port_up(team, port); + port = team_get_first_port_txable_rcu(team, port); if (unlikely(!port)) goto drop; if (team_dev_queue_xmit(team, port, skb)) @@ -64,20 +44,10 @@ drop: return false; }
-static int rr_port_enter(struct team *team, struct team_port *port) -{ - return team_port_set_team_dev_addr(port); -} - -static void rr_port_change_dev_addr(struct team *team, struct team_port *port) -{ - team_port_set_team_dev_addr(port); -} - static const struct team_mode_ops rr_mode_ops = { .transmit = rr_transmit, - .port_enter = rr_port_enter, - .port_change_dev_addr = rr_port_change_dev_addr, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, };
static const struct team_mode rr_mode = { diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 4648d80..90fc919 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -112,6 +112,10 @@ struct team_mode_ops { void (*port_disabled)(struct team *team, struct team_port *port); };
+extern int team_modeop_port_enter(struct team *team, struct team_port *port); +extern void team_modeop_port_change_dev_addr(struct team *team, + struct team_port *port); + enum team_option_type { TEAM_OPTION_TYPE_U32, TEAM_OPTION_TYPE_STRING, @@ -238,7 +242,26 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team, return NULL; }
-extern int team_port_set_team_dev_addr(struct team_port *port); +static inline struct team_port * +team_get_first_port_txable_rcu(struct team *team, struct team_port *port) +{ + struct team_port *cur; + + if (likely(team_port_txable(port))) + return port; + cur = port; + list_for_each_entry_continue_rcu(cur, &team->port_list, list) + if (team_port_txable(port)) + return cur; + list_for_each_entry_rcu(cur, &team->port_list, list) { + if (cur == port) + break; + if (team_port_txable(port)) + return cur; + } + return NULL; +} + extern int team_options_register(struct team *team, const struct team_option *option, size_t option_count);
kernel@lists.fedoraproject.org