Skip to content

Commit

Permalink
Merge branch 'prestera-mdb-offload'
Browse files Browse the repository at this point in the history
Oleksandr Mazur says:

====================
net: marvell: prestera: add MDB offloading support

This patch series adds support for the MDB handling for the marvell
Prestera Driver. It's used to propagate IGMP groups registered within
the Kernel to the underlying HW (offload registered groups).

Features:
 - define (and implement) main internal MDB-related objects;
 - define (and implement) main HW APIs for MDB handling;
 - add MDB handling support for both regular ports as well as Bond
   interfaces;
 - Mirrored behavior of Bridge driver upon multicast router appearance
   (all traffic flooded when there's no mcast router; mcast router
    receives all mcast traffic, and only group-specific registered mcast
    traffic is being received by ports who've explicitly joined any group
    when there's a registered mcast router);
 - Reworked prestera driver bridge flags (especially multicast)
   setting - thus making it possible to react over mcast disabled messages
   properly by offloading this state to the underlying HW
   (disabling multicast flooding);

Limitations:
 - Not full (partial) IGMPv3 support (due to limited switchdev
   notification capabilities:
     MDB events are being propagated only with a single MAC entry,
     while IGMPv3 has Group-Specific requests and responses);
 - It's possible that multiple IP groups would receive traffic from
   other groups, as MDB events are being propagated with a single MAC
   entry, which makes it possible to map a few IPs over same MAC;

Co-developed-by: Yevhen Orlov <yevhen.orlov@plvision.eu>
Signed-off-by: Yevhen Orlov <yevhen.orlov@plvision.eu>
Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>

PATCH V5:
 - fix checkpatch errors (4/4).
 - remove function forward declarations, and move
   function implementations to match the removed
   forward declarations (4/4).
 - rebased changes on top of latest master.
PATCH V4:
 - fix clang warning - var uninitialized when used.
PATCH V3:
 - add missing function implementations to 2/4 (HW API implementation),
   only definitions were added int V1, V2, and implementation waas missed
   by mistake.

Reported-by: kernel test robot <lkp@intel.com>
 - fix compiletime warning (unused variable)

PATCH V2:
 - include all the patches of patch series (V1's been sent to
   closed net-next, also had not all patches included);
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 13, 2022
2 parents b09ab9c + deef0d6 commit dd51723
Show file tree
Hide file tree
Showing 5 changed files with 1,124 additions and 91 deletions.
47 changes: 47 additions & 0 deletions drivers/net/ethernet/marvell/prestera/prestera.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ struct prestera_fw_rev {
u16 sub;
};

struct prestera_flood_domain {
struct prestera_switch *sw;
struct list_head flood_domain_port_list;
u32 idx;
};

struct prestera_mdb_entry {
struct prestera_switch *sw;
struct prestera_flood_domain *flood_domain;
unsigned char addr[ETH_ALEN];
u16 vid;
};

struct prestera_flood_domain_port {
struct prestera_flood_domain *flood_domain;
struct net_device *dev;
struct list_head flood_domain_port_node;
u16 vid;
};

struct prestera_port_stats {
u64 good_octets_received;
u64 bad_octets_received;
Expand Down Expand Up @@ -321,6 +341,8 @@ void prestera_router_fini(struct prestera_switch *sw);

struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id);

struct prestera_switch *prestera_switch_get(struct net_device *dev);

int prestera_port_cfg_mac_read(struct prestera_port *port,
struct prestera_port_mac_config *cfg);

Expand All @@ -331,16 +353,41 @@ struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev);

void prestera_queue_work(struct work_struct *work);

int prestera_port_learning_set(struct prestera_port *port, bool learn_enable);
int prestera_port_uc_flood_set(struct prestera_port *port, bool flood);
int prestera_port_mc_flood_set(struct prestera_port *port, bool flood);

int prestera_port_pvid_set(struct prestera_port *port, u16 vid);

bool prestera_netdev_check(const struct net_device *dev);

int prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr);

bool prestera_port_is_lag_member(const struct prestera_port *port);
int prestera_lag_id(struct prestera_switch *sw,
struct net_device *lag_dev, u16 *lag_id);

struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id);

u16 prestera_port_lag_id(const struct prestera_port *port);

struct prestera_mdb_entry *
prestera_mdb_entry_create(struct prestera_switch *sw,
const unsigned char *addr, u16 vid);
void prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry);

struct prestera_flood_domain *
prestera_flood_domain_create(struct prestera_switch *sw);
void prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain);

int
prestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain,
struct net_device *dev,
u16 vid);
void
prestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port);
struct prestera_flood_domain_port *
prestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain,
struct net_device *dev, u16 vid);

#endif /* _PRESTERA_H_ */
256 changes: 204 additions & 52 deletions drivers/net/ethernet/marvell/prestera/prestera_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ enum prestera_cmd_type_t {
PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630,
PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631,

PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE = 0x700,
PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY = 0x701,
PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET = 0x702,
PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET = 0x703,

PRESTERA_CMD_TYPE_MDB_CREATE = 0x704,
PRESTERA_CMD_TYPE_MDB_DESTROY = 0x705,

PRESTERA_CMD_TYPE_RXTX_INIT = 0x800,

PRESTERA_CMD_TYPE_LAG_MEMBER_ADD = 0x900,
Expand Down Expand Up @@ -185,6 +193,12 @@ struct prestera_fw_event_handler {
void *arg;
};

enum {
PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_REG_PORT = 0,
PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG = 1,
PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_MAX = 2,
};

struct prestera_msg_cmd {
__le32 type;
};
Expand Down Expand Up @@ -627,6 +641,57 @@ struct prestera_msg_event_fdb {
u8 dest_type;
};

struct prestera_msg_flood_domain_create_req {
struct prestera_msg_cmd cmd;
};

struct prestera_msg_flood_domain_create_resp {
struct prestera_msg_ret ret;
__le32 flood_domain_idx;
};

struct prestera_msg_flood_domain_destroy_req {
struct prestera_msg_cmd cmd;
__le32 flood_domain_idx;
};

struct prestera_msg_flood_domain_ports_set_req {
struct prestera_msg_cmd cmd;
__le32 flood_domain_idx;
__le32 ports_num;
};

struct prestera_msg_flood_domain_ports_reset_req {
struct prestera_msg_cmd cmd;
__le32 flood_domain_idx;
};

struct prestera_msg_flood_domain_port {
union {
struct {
__le32 port_num;
__le32 dev_num;
};
__le16 lag_id;
};
__le16 vid;
__le16 port_type;
};

struct prestera_msg_mdb_create_req {
struct prestera_msg_cmd cmd;
__le32 flood_domain_idx;
__le16 vid;
u8 mac[ETH_ALEN];
};

struct prestera_msg_mdb_destroy_req {
struct prestera_msg_cmd cmd;
__le32 flood_domain_idx;
__le16 vid;
u8 mac[ETH_ALEN];
};

static void prestera_hw_build_tests(void)
{
/* check requests */
Expand Down Expand Up @@ -654,10 +719,17 @@ static void prestera_hw_build_tests(void)
BUILD_BUG_ON(sizeof(struct prestera_msg_vr_req) != 8);
BUILD_BUG_ON(sizeof(struct prestera_msg_lpm_req) != 36);
BUILD_BUG_ON(sizeof(struct prestera_msg_policer_req) != 36);
BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_req) != 4);
BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_destroy_req) != 8);
BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_set_req) != 12);
BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_reset_req) != 8);
BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_create_req) != 16);
BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_destroy_req) != 16);

/* structure that are part of req/resp fw messages */
BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16);
BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20);
BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_port) != 12);

/* check responses */
BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8);
Expand Down Expand Up @@ -1531,7 +1603,7 @@ int prestera_hw_port_learning_set(struct prestera_port *port, bool enable)
&req.cmd, sizeof(req));
}

static int prestera_hw_port_uc_flood_set(struct prestera_port *port, bool flood)
int prestera_hw_port_uc_flood_set(const struct prestera_port *port, bool flood)
{
struct prestera_msg_port_attr_req req = {
.attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD),
Expand All @@ -1549,7 +1621,7 @@ static int prestera_hw_port_uc_flood_set(struct prestera_port *port, bool flood)
&req.cmd, sizeof(req));
}

static int prestera_hw_port_mc_flood_set(struct prestera_port *port, bool flood)
int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood)
{
struct prestera_msg_port_attr_req req = {
.attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD),
Expand All @@ -1567,56 +1639,6 @@ static int prestera_hw_port_mc_flood_set(struct prestera_port *port, bool flood)
&req.cmd, sizeof(req));
}

static int prestera_hw_port_flood_set_v2(struct prestera_port *port, bool flood)
{
struct prestera_msg_port_attr_req req = {
.attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD),
.port = __cpu_to_le32(port->hw_id),
.dev = __cpu_to_le32(port->dev_id),
.param = {
.flood = flood,
}
};

return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
&req.cmd, sizeof(req));
}

int prestera_hw_port_flood_set(struct prestera_port *port, unsigned long mask,
unsigned long val)
{
int err;

if (port->sw->dev->fw_rev.maj <= 2) {
if (!(mask & BR_FLOOD))
return 0;

return prestera_hw_port_flood_set_v2(port, val & BR_FLOOD);
}

if (mask & BR_FLOOD) {
err = prestera_hw_port_uc_flood_set(port, val & BR_FLOOD);
if (err)
goto err_uc_flood;
}

if (mask & BR_MCAST_FLOOD) {
err = prestera_hw_port_mc_flood_set(port, val & BR_MCAST_FLOOD);
if (err)
goto err_mc_flood;
}

return 0;

err_mc_flood:
prestera_hw_port_mc_flood_set(port, 0);
err_uc_flood:
if (mask & BR_FLOOD)
prestera_hw_port_uc_flood_set(port, 0);

return err;
}

int prestera_hw_vlan_create(struct prestera_switch *sw, u16 vid)
{
struct prestera_msg_vlan_req req = {
Expand Down Expand Up @@ -2244,3 +2266,133 @@ int prestera_hw_policer_sr_tcm_set(struct prestera_switch *sw,
return prestera_cmd(sw, PRESTERA_CMD_TYPE_POLICER_SET,
&req.cmd, sizeof(req));
}

int prestera_hw_flood_domain_create(struct prestera_flood_domain *domain)
{
struct prestera_msg_flood_domain_create_resp resp;
struct prestera_msg_flood_domain_create_req req;
int err;

err = prestera_cmd_ret(domain->sw,
PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE, &req.cmd,
sizeof(req), &resp.ret, sizeof(resp));
if (err)
return err;

domain->idx = __le32_to_cpu(resp.flood_domain_idx);

return 0;
}

int prestera_hw_flood_domain_destroy(struct prestera_flood_domain *domain)
{
struct prestera_msg_flood_domain_destroy_req req = {
.flood_domain_idx = __cpu_to_le32(domain->idx),
};

return prestera_cmd(domain->sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY,
&req.cmd, sizeof(req));
}

int prestera_hw_flood_domain_ports_set(struct prestera_flood_domain *domain)
{
struct prestera_flood_domain_port *flood_domain_port;
struct prestera_msg_flood_domain_ports_set_req *req;
struct prestera_msg_flood_domain_port *ports;
struct prestera_switch *sw = domain->sw;
struct prestera_port *port;
u32 ports_num = 0;
int buf_size;
void *buff;
u16 lag_id;
int err;

list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list,
flood_domain_port_node)
ports_num++;

if (!ports_num)
return -EINVAL;

buf_size = sizeof(*req) + sizeof(*ports) * ports_num;

buff = kmalloc(buf_size, GFP_KERNEL);
if (!buff)
return -ENOMEM;

req = buff;
ports = buff + sizeof(*req);

req->flood_domain_idx = __cpu_to_le32(domain->idx);
req->ports_num = __cpu_to_le32(ports_num);

list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list,
flood_domain_port_node) {
if (netif_is_lag_master(flood_domain_port->dev)) {
if (prestera_lag_id(sw, flood_domain_port->dev,
&lag_id)) {
kfree(buff);
return -EINVAL;
}

ports->port_type =
__cpu_to_le16(PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG);
ports->lag_id = __cpu_to_le16(lag_id);
} else {
port = prestera_port_dev_lower_find(flood_domain_port->dev);

ports->port_type =
__cpu_to_le16(PRESTERA_HW_FDB_ENTRY_TYPE_REG_PORT);
ports->dev_num = __cpu_to_le32(port->dev_id);
ports->port_num = __cpu_to_le32(port->hw_id);
}

ports->vid = __cpu_to_le16(flood_domain_port->vid);

ports++;
}

err = prestera_cmd(sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET,
&req->cmd, buf_size);

kfree(buff);

return err;
}

int prestera_hw_flood_domain_ports_reset(struct prestera_flood_domain *domain)
{
struct prestera_msg_flood_domain_ports_reset_req req = {
.flood_domain_idx = __cpu_to_le32(domain->idx),
};

return prestera_cmd(domain->sw,
PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET, &req.cmd,
sizeof(req));
}

int prestera_hw_mdb_create(struct prestera_mdb_entry *mdb)
{
struct prestera_msg_mdb_create_req req = {
.flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx),
.vid = __cpu_to_le16(mdb->vid),
};

memcpy(req.mac, mdb->addr, ETH_ALEN);

return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_CREATE, &req.cmd,
sizeof(req));
}

int prestera_hw_mdb_destroy(struct prestera_mdb_entry *mdb)
{
struct prestera_msg_mdb_destroy_req req = {
.flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx),
.vid = __cpu_to_le16(mdb->vid),
};

memcpy(req.mac, mdb->addr, ETH_ALEN);

return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_DESTROY, &req.cmd,
sizeof(req));
}
Loading

0 comments on commit dd51723

Please sign in to comment.