Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5111f80c authored by Scott Feldman's avatar Scott Feldman Committed by David S. Miller
Browse files

rocker: add ndo_bridge_setlink/getlink support for learning policy



Rocker ports will use new "swdev" hwmode for bridge port offload policy.
Current supported policy settings are BR_LEARNING and BR_LEARNING_SYNC.
User can turn on/off device port FDB learning and syncing to bridge.

Signed-off-by: default avatarScott Feldman <sfeldma@gmail.com>
Signed-off-by: default avatarJiri Pirko <jiri@resnulli.us>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ce76ca68
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -203,6 +203,7 @@ struct rocker_port {
	u32 lport;
	__be16 internal_vlan_id;
	int stp_state;
	u32 brport_flags;
	bool ctrls[ROCKER_CTRL_MAX];
	unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN];
	struct napi_struct napi_tx;
@@ -1629,6 +1630,30 @@ rocker_cmd_set_port_settings_macaddr_prep(struct rocker *rocker,
	return 0;
}

static int
rocker_cmd_set_port_learning_prep(struct rocker *rocker,
				  struct rocker_port *rocker_port,
				  struct rocker_desc_info *desc_info,
				  void *priv)
{
	struct rocker_tlv *cmd_info;

	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
		return -EMSGSIZE;
	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
	if (!cmd_info)
		return -EMSGSIZE;
	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LPORT,
			       rocker_port->lport))
		return -EMSGSIZE;
	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
			      !!(rocker_port->brport_flags & BR_LEARNING)))
		return -EMSGSIZE;
	rocker_tlv_nest_end(desc_info, cmd_info);
	return 0;
}

static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
						struct ethtool_cmd *ecmd)
{
@@ -1663,6 +1688,13 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
			       macaddr, NULL, NULL, false);
}

static int rocker_port_set_learning(struct rocker_port *rocker_port)
{
	return rocker_cmd_exec(rocker_port->rocker, rocker_port,
			       rocker_cmd_set_port_learning_prep,
			       NULL, NULL, NULL, false);
}

static int rocker_cmd_flow_tbl_add_ig_port(struct rocker_desc_info *desc_info,
					   struct rocker_flow_tbl_entry *entry)
{
@@ -2995,6 +3027,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
	u32 out_lport = rocker_port->lport;
	u32 tunnel_id = 0;
	u32 group_id = ROCKER_GROUP_NONE;
	bool syncing = !!(rocker_port->brport_flags & BR_LEARNING_SYNC);
	bool copy_to_cpu = false;
	int err;

@@ -3009,6 +3042,9 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
			return err;
	}

	if (!syncing)
		return 0;

	if (!rocker_port_is_bridged(rocker_port))
		return 0;

@@ -3659,6 +3695,64 @@ static int rocker_port_fdb_dump(struct sk_buff *skb,
	return idx;
}

static int rocker_port_bridge_setlink(struct net_device *dev,
				      struct nlmsghdr *nlh)
{
	struct rocker_port *rocker_port = netdev_priv(dev);
	struct nlattr *protinfo;
	struct nlattr *afspec;
	struct nlattr *attr;
	u16 mode;
	int err;

	protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
				   IFLA_PROTINFO);
	afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);

	if (afspec) {
		attr = nla_find_nested(afspec, IFLA_BRIDGE_MODE);
		if (attr) {
			mode = nla_get_u16(attr);
			if (mode != BRIDGE_MODE_SWDEV)
				return -EINVAL;
		}
	}

	if (protinfo) {
		attr = nla_find_nested(protinfo, IFLA_BRPORT_LEARNING);
		if (attr) {
			if (nla_get_u8(attr))
				rocker_port->brport_flags |= BR_LEARNING;
			else
				rocker_port->brport_flags &= ~BR_LEARNING;
			err = rocker_port_set_learning(rocker_port);
			if (err)
				return err;
		}
		attr = nla_find_nested(protinfo, IFLA_BRPORT_LEARNING_SYNC);
		if (attr) {
			if (nla_get_u8(attr))
				rocker_port->brport_flags |= BR_LEARNING_SYNC;
			else
				rocker_port->brport_flags &= ~BR_LEARNING_SYNC;
		}
	}

	return 0;
}

static int rocker_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
				      struct net_device *dev,
				      u32 filter_mask)
{
	struct rocker_port *rocker_port = netdev_priv(dev);
	u16 mode = BRIDGE_MODE_SWDEV;
	u32 mask = BR_LEARNING | BR_LEARNING_SYNC;

	return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
				       rocker_port->brport_flags, mask);
}

static int rocker_port_switch_parent_id_get(struct net_device *dev,
					    struct netdev_phys_item_id *psid)
{
@@ -3687,6 +3781,8 @@ static const struct net_device_ops rocker_port_netdev_ops = {
	.ndo_fdb_add			= rocker_port_fdb_add,
	.ndo_fdb_del			= rocker_port_fdb_del,
	.ndo_fdb_dump			= rocker_port_fdb_dump,
	.ndo_bridge_setlink		= rocker_port_bridge_setlink,
	.ndo_bridge_getlink		= rocker_port_bridge_getlink,
	.ndo_switch_parent_id_get	= rocker_port_switch_parent_id_get,
	.ndo_switch_port_stp_update	= rocker_port_switch_port_stp_update,
};
@@ -3887,6 +3983,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
	rocker_port->rocker = rocker;
	rocker_port->port_number = port_number;
	rocker_port->lport = port_number + 1;
	rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC;

	rocker_port_dev_addr_init(rocker, rocker_port);
	dev->netdev_ops = &rocker_port_netdev_ops;
@@ -3906,6 +4003,8 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
	}
	rocker->ports[port_number] = rocker_port;

	rocker_port_set_learning(rocker_port);

	rocker_port->internal_vlan_id =
		rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex);
	err = rocker_port_ig_tbl(rocker_port, 0);
+1 −0
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ enum {
	ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG,		/* u8 */
	ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,		/* binary */
	ROCKER_TLV_CMD_PORT_SETTINGS_MODE,		/* u8 */
	ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,		/* u8 */

	__ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
	ROCKER_TLV_CMD_PORT_SETTINGS_MAX =