Commit 46a275a5 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-marvell-prestera-add-nexthop-routes-offloading'



Yevhen Orlov says:

====================
net: marvell: prestera: add nexthop routes offloading

Add support for nexthop routes for Marvell Prestera driver.
Subscribe on NEIGH_UPDATE events.

Add features:
 - Support connected route adding
   e.g.: "ip address add 1.1.1.1/24 dev sw1p1"
   e.g.: "ip route add 6.6.6/24 dev sw1p1"
 - Support nexthop route adding
   e.g.: "ip route add 5.5.5/24 via 1.1.1.2"
 - Support ECMP route adding
   e.g.: "ip route add 5.5.5/24 nexthop via 1.1.1.2 nexthop via 1.1.1.3"
 - Support "offload" and "trap" flags per each nexthop
 - Support "offload" flag for neighbours

Limitations:
 - Only "local" and "main" tables supported
 - Only generic interfaces supported for router (no bridges or vlans)

Flags meaning:
  ip route add 5.5.5/24 nexthop via 2.2.2.2 nexthop via 2.2.2.3
  ip route show
  ...
  5.5.5.0/24 rt_offload
        nexthop via 2.2.2.2 dev sw1p31 weight 1 trap
        nexthop via 2.2.2.3 dev sw1p31 weight 1 trap
  ...
  # When you just add route - lpm entry became occupied
  # in HW ("rt_offload" flag), but related to nexthops neighbours
  # still not resolved ("trap" flag).
  #
  # After some time...
  ip route show
  ...
  5.5.5.0/24 rt_offload
        nexthop via 2.2.2.2 dev sw1p31 weight 1 offload
        nexthop via 2.2.2.3 dev sw1p31 weight 1 offload
  ...
  # You will see, that appropriate neighbours was resolved and nexthop
  # entries occupied in HW too ("offload" flag)

Co-developed-by: default avatarTaras Chornyi <tchornyi@marvell.com>
Signed-off-by: default avatarTaras Chornyi <tchornyi@marvell.com>
Co-developed-by: default avatarOleksandr Mazur <oleksandr.mazur@plvision.eu>
Signed-off-by: default avatarOleksandr Mazur <oleksandr.mazur@plvision.eu>
Signed-off-by: default avatarYevhen Orlov <yevhen.orlov@plvision.eu>

Changes for v2:
* Add more reviewers in CC
* Check if route nexthop or direct with fib_nh_gw_family instead of fib_nh_scope
  This is needed after,
  747c1430 ("ip: fix dflt addr selection for connected nexthop"),
  because direct route is now with the same scope as nexthop (RT_SCOPE_LINK)

Changes for v3:
* Resolve "unused functions" warnings, after
  patch ("net: marvell: prestera: Add heplers to interact ... "), and before
  patch ("net: marvell: prestera: Add neighbour cache accounting")

Changes for v4:
* Rebase to the latest master to resolve patch applying issues

Changes for v5:
* Repack structures to prevent holes
* Remove unused variables
* Fix misspeling issues

Changes for v6:
* Rebase on top of master
* Fix smatch warnings

Changes for v7:
* Rebase on top of master
* Refactor: use "fib_lookup" instead of "fib_new_table"+"fib_table_lookup",
  according to Paolo Abeni suggestion
* Refactor: use "rhashtable_free_and_destroy" instead of rhashtable
  walk, according to Paolo Abeni suggestion
====================

Link: https://lore.kernel.org/r/20221001093417.22388-1-yevhen.orlov@plvision.eu


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 899b8cd0 ae15ed6e
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -306,17 +306,27 @@ struct prestera_switch {
	struct prestera_counter *counter;
	u8 lag_member_max;
	u8 lag_max;
	u32 size_tbl_router_nexthop;
};

struct prestera_router {
	struct prestera_switch *sw;
	struct list_head vr_list;
	struct list_head rif_entry_list;
	struct rhashtable nh_neigh_ht;
	struct rhashtable nexthop_group_ht;
	struct rhashtable fib_ht;
	struct rhashtable kern_neigh_cache_ht;
	struct rhashtable kern_fib_cache_ht;
	struct notifier_block inetaddr_nb;
	struct notifier_block inetaddr_valid_nb;
	struct notifier_block fib_nb;
	struct notifier_block netevent_nb;
	u8 *nhgrp_hw_state_cache; /* Bitmap cached hw state of nhs */
	unsigned long nhgrp_hw_cache_kick; /* jiffies */
	struct {
		struct delayed_work dw;
	} neighs_update;
};

struct prestera_rxtx_params {
@@ -362,6 +372,8 @@ int prestera_port_cfg_mac_write(struct prestera_port *port,
struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev);

void prestera_queue_work(struct work_struct *work);
void prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay);
void prestera_queue_drain(void);

int prestera_port_learning_set(struct prestera_port *port, bool learn_enable);
int prestera_port_uc_flood_set(struct prestera_port *port, bool flood);
+130 −0
Original line number Diff line number Diff line
@@ -10,11 +10,14 @@
#include "prestera_hw.h"
#include "prestera_acl.h"
#include "prestera_counter.h"
#include "prestera_router_hw.h"

#define PRESTERA_SWITCH_INIT_TIMEOUT_MS (30 * 1000)

#define PRESTERA_MIN_MTU 64

#define PRESTERA_MSG_CHUNK_SIZE 1024

enum prestera_cmd_type_t {
	PRESTERA_CMD_TYPE_SWITCH_INIT = 0x1,
	PRESTERA_CMD_TYPE_SWITCH_ATTR_SET = 0x2,
@@ -57,6 +60,10 @@ enum prestera_cmd_type_t {
	PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE = 0x601,
	PRESTERA_CMD_TYPE_ROUTER_LPM_ADD = 0x610,
	PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE = 0x611,
	PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET = 0x622,
	PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET = 0x645,
	PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD = 0x623,
	PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE = 0x624,
	PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630,
	PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631,

@@ -542,6 +549,14 @@ struct prestera_msg_ip_addr {
	u8 __pad[3];
};

struct prestera_msg_nh {
	struct prestera_msg_iface oif;
	__le32 hw_id;
	u8 mac[ETH_ALEN];
	u8 is_active;
	u8 pad;
};

struct prestera_msg_rif_req {
	struct prestera_msg_cmd cmd;
	struct prestera_msg_iface iif;
@@ -567,6 +582,34 @@ struct prestera_msg_lpm_req {
	u8 __pad[2];
};

struct prestera_msg_nh_req {
	struct prestera_msg_cmd cmd;
	struct prestera_msg_nh nh[PRESTERA_NHGR_SIZE_MAX];
	__le32 size;
	__le32 grp_id;
};

struct prestera_msg_nh_chunk_req {
	struct prestera_msg_cmd cmd;
	__le32 offset;
};

struct prestera_msg_nh_chunk_resp {
	struct prestera_msg_ret ret;
	u8 hw_state[PRESTERA_MSG_CHUNK_SIZE];
};

struct prestera_msg_nh_grp_req {
	struct prestera_msg_cmd cmd;
	__le32 grp_id;
	__le32 size;
};

struct prestera_msg_nh_grp_resp {
	struct prestera_msg_ret ret;
	__le32 grp_id;
};

struct prestera_msg_vr_req {
	struct prestera_msg_cmd cmd;
	__le16 vr_id;
@@ -729,11 +772,15 @@ static void prestera_hw_build_tests(void)
	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);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh_req) != 124);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_req) != 8);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_req) != 12);

	/*  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);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh) != 28);

	/* check responses */
	BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8);
@@ -750,6 +797,8 @@ static void prestera_hw_build_tests(void)
	BUILD_BUG_ON(sizeof(struct prestera_msg_vr_resp) != 12);
	BUILD_BUG_ON(sizeof(struct prestera_msg_policer_resp) != 12);
	BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_resp) != 12);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_resp) != 1032);
	BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_resp) != 12);

	/* check events */
	BUILD_BUG_ON(sizeof(struct prestera_msg_event_port) != 20);
@@ -1027,6 +1076,8 @@ int prestera_hw_switch_init(struct prestera_switch *sw)
	sw->id = resp.switch_id;
	sw->lag_member_max = resp.lag_member_max;
	sw->lag_max = resp.lag_max;
	sw->size_tbl_router_nexthop =
		__le32_to_cpu(resp.size_tbl_router_nexthop);

	return 0;
}
@@ -2037,6 +2088,85 @@ int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id,
			    sizeof(req));
}

int prestera_hw_nh_entries_set(struct prestera_switch *sw, int count,
			       struct prestera_neigh_info *nhs, u32 grp_id)
{
	struct prestera_msg_nh_req req = { .size = __cpu_to_le32((u32)count),
			.grp_id = __cpu_to_le32(grp_id) };
	int i, err;

	for (i = 0; i < count; i++) {
		req.nh[i].is_active = nhs[i].connected;
		memcpy(&req.nh[i].mac, nhs[i].ha, ETH_ALEN);
		err = prestera_iface_to_msg(&nhs[i].iface, &req.nh[i].oif);
		if (err)
			return err;
	}

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET, &req.cmd,
			    sizeof(req));
}

int prestera_hw_nhgrp_blk_get(struct prestera_switch *sw,
			      u8 *hw_state, u32 buf_size /* Buffer in bytes */)
{
	static struct prestera_msg_nh_chunk_resp resp;
	struct prestera_msg_nh_chunk_req req;
	u32 buf_offset;
	int err;

	memset(&hw_state[0], 0, buf_size);
	buf_offset = 0;
	while (1) {
		if (buf_offset >= buf_size)
			break;

		memset(&req, 0, sizeof(req));
		req.offset = __cpu_to_le32(buf_offset * 8); /* 8 bits in u8 */
		err = prestera_cmd_ret(sw,
				       PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET,
				       &req.cmd, sizeof(req), &resp.ret,
				       sizeof(resp));
		if (err)
			return err;

		memcpy(&hw_state[buf_offset], &resp.hw_state[0],
		       buf_offset + PRESTERA_MSG_CHUNK_SIZE > buf_size ?
			buf_size - buf_offset : PRESTERA_MSG_CHUNK_SIZE);
		buf_offset += PRESTERA_MSG_CHUNK_SIZE;
	}

	return 0;
}

int prestera_hw_nh_group_create(struct prestera_switch *sw, u16 nh_count,
				u32 *grp_id)
{
	struct prestera_msg_nh_grp_req req = { .size = __cpu_to_le32((u32)nh_count) };
	struct prestera_msg_nh_grp_resp resp;
	int err;

	err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD,
			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
	if (err)
		return err;

	*grp_id = __le32_to_cpu(resp.grp_id);
	return err;
}

int prestera_hw_nh_group_delete(struct prestera_switch *sw, u16 nh_count,
				u32 grp_id)
{
	struct prestera_msg_nh_grp_req req = {
	    .grp_id = __cpu_to_le32(grp_id),
	    .size = __cpu_to_le32(nh_count)
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_rxtx_init(struct prestera_switch *sw,
			  struct prestera_rxtx_params *params)
{
+11 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ struct prestera_counter_stats;
struct prestera_iface;
struct prestera_flood_domain;
struct prestera_mdb_entry;
struct prestera_neigh_info;

/* Switch API */
int prestera_hw_switch_init(struct prestera_switch *sw);
@@ -266,6 +267,16 @@ int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id,
int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id,
			__be32 dst, u32 dst_len);

/* NH API */
int prestera_hw_nh_entries_set(struct prestera_switch *sw, int count,
			       struct prestera_neigh_info *nhs, u32 grp_id);
int prestera_hw_nhgrp_blk_get(struct prestera_switch *sw,
			      u8 *hw_state, u32 buf_size /* Buffer in bytes */);
int prestera_hw_nh_group_create(struct prestera_switch *sw, u16 nh_count,
				u32 *grp_id);
int prestera_hw_nh_group_delete(struct prestera_switch *sw, u16 nh_count,
				u32 grp_id);

/* Event handlers */
int prestera_hw_event_handler_register(struct prestera_switch *sw,
				       enum prestera_event_type type,
+11 −0
Original line number Diff line number Diff line
@@ -36,6 +36,17 @@ void prestera_queue_work(struct work_struct *work)
	queue_work(prestera_owq, work);
}

void prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay)
{
	queue_delayed_work(prestera_wq, work, delay);
}

void prestera_queue_drain(void)
{
	drain_workqueue(prestera_wq);
	drain_workqueue(prestera_owq);
}

int prestera_port_learning_set(struct prestera_port *port, bool learn)
{
	return prestera_hw_port_learning_set(port, learn);
+1083 −36

File changed.

Preview size limit exceeded, changes collapsed.

Loading