Commit 702e7014 authored by Maksym Glubokiy's avatar Maksym Glubokiy Committed by David S. Miller
Browse files

net: prestera: acl: add support for 'egress' rules



The following is now supported:

  $ tc qdisc add PORT clsact
  $ tc filter add dev PORT egress ...

Signed-off-by: default avatarMaksym Glubokiy <maksym.glubokiy@plvision.eu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 04cfbc1d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -107,7 +107,8 @@ struct prestera_port_phy_config {
struct prestera_port {
	struct net_device *dev;
	struct prestera_switch *sw;
	struct prestera_flow_block *flow_block;
	struct prestera_flow_block *ingress_flow_block;
	struct prestera_flow_block *egress_flow_block;
	struct devlink_port dl_port;
	struct list_head lag_member;
	struct prestera_lag *lag;
+35 −12
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ struct prestera_acl_ruleset {
	u32 index;
	u16 pcl_id;
	bool offload;
	bool ingress;
};

struct prestera_acl_vtcam {
@@ -70,6 +71,7 @@ struct prestera_acl_vtcam {
	u32 id;
	bool is_keymask_set;
	u8 lookup;
	u8 direction;
};

static const struct rhashtable_params prestera_acl_ruleset_ht_params = {
@@ -93,23 +95,36 @@ static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = {
	.automatic_shrinking = true,
};

int prestera_acl_chain_to_client(u32 chain_index, u32 *client)
int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client)
{
	static const u32 client_map[] = {
		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0,
		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1,
		PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2
	static const u32 ingress_client_map[] = {
		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0,
		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1,
		PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2
	};

	if (chain_index >= ARRAY_SIZE(client_map))
	if (!ingress) {
		/* prestera supports only one chain on egress */
		if (chain_index > 0)
			return -EINVAL;

	*client = client_map[chain_index];
		*client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP;
		return 0;
	}

static bool prestera_acl_chain_is_supported(u32 chain_index)
	if (chain_index >= ARRAY_SIZE(ingress_client_map))
		return -EINVAL;

	*client = ingress_client_map[chain_index];
	return 0;
}

static bool prestera_acl_chain_is_supported(u32 chain_index, bool ingress)
{
	if (!ingress)
		/* prestera supports only one chain on egress */
		return chain_index == 0;

	return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0;
}

@@ -122,7 +137,7 @@ prestera_acl_ruleset_create(struct prestera_acl *acl,
	u32 uid = 0;
	int err;

	if (!prestera_acl_chain_is_supported(chain_index))
	if (!prestera_acl_chain_is_supported(chain_index, block->ingress))
		return ERR_PTR(-EINVAL);

	ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
@@ -130,6 +145,7 @@ prestera_acl_ruleset_create(struct prestera_acl *acl,
		return ERR_PTR(-ENOMEM);

	ruleset->acl = acl;
	ruleset->ingress = block->ingress;
	ruleset->ht_key.block = block;
	ruleset->ht_key.chain_index = chain_index;
	refcount_set(&ruleset->refcount, 1);
@@ -172,13 +188,18 @@ int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset)
{
	struct prestera_acl_iface iface;
	u32 vtcam_id;
	int dir;
	int err;

	dir = ruleset->ingress ?
		PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS;

	if (ruleset->offload)
		return -EEXIST;

	err = prestera_acl_vtcam_id_get(ruleset->acl,
					ruleset->ht_key.chain_index,
					dir,
					ruleset->keymask, &vtcam_id);
	if (err)
		goto err_vtcam_create;
@@ -719,7 +740,7 @@ static int __prestera_acl_vtcam_id_try_fit(struct prestera_acl *acl, u8 lookup,
	return 0;
}

int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir,
			      void *keymask, u32 *vtcam_id)
{
	struct prestera_acl_vtcam *vtcam;
@@ -731,7 +752,8 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
	 * fine for now
	 */
	list_for_each_entry(vtcam, &acl->vtcam_list, list) {
		if (lookup != vtcam->lookup)
		if (lookup != vtcam->lookup ||
		    dir != vtcam->direction)
			continue;

		if (!keymask && !vtcam->is_keymask_set) {
@@ -752,7 +774,7 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
		return -ENOMEM;

	err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id,
				       PRESTERA_HW_VTCAM_DIR_INGRESS);
				       dir);
	if (err) {
		kfree(vtcam);

@@ -765,6 +787,7 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
		return 0;
	}

	vtcam->direction = dir;
	vtcam->id = new_vtcam_id;
	vtcam->lookup = lookup;
	if (keymask) {
+2 −2
Original line number Diff line number Diff line
@@ -199,9 +199,9 @@ void
prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule,
				     u16 pcl_id);

int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup,
int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir,
			      void *keymask, u32 *vtcam_id);
int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id);
int prestera_acl_chain_to_client(u32 chain_index, u32 *client);
int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client);

#endif /* _PRESTERA_ACL_H_ */
+37 −15
Original line number Diff line number Diff line
@@ -75,7 +75,9 @@ static void prestera_flow_block_destroy(void *cb_priv)
}

static struct prestera_flow_block *
prestera_flow_block_create(struct prestera_switch *sw, struct net *net)
prestera_flow_block_create(struct prestera_switch *sw,
			   struct net *net,
			   bool ingress)
{
	struct prestera_flow_block *block;

@@ -87,6 +89,7 @@ prestera_flow_block_create(struct prestera_switch *sw, struct net *net)
	INIT_LIST_HEAD(&block->template_list);
	block->net = net;
	block->sw = sw;
	block->ingress = ingress;

	return block;
}
@@ -165,7 +168,8 @@ static int prestera_flow_block_unbind(struct prestera_flow_block *block,
static struct prestera_flow_block *
prestera_flow_block_get(struct prestera_switch *sw,
			struct flow_block_offload *f,
			bool *register_block)
			bool *register_block,
			bool ingress)
{
	struct prestera_flow_block *block;
	struct flow_block_cb *block_cb;
@@ -173,7 +177,7 @@ prestera_flow_block_get(struct prestera_switch *sw,
	block_cb = flow_block_cb_lookup(f->block,
					prestera_flow_block_cb, sw);
	if (!block_cb) {
		block = prestera_flow_block_create(sw, f->net);
		block = prestera_flow_block_create(sw, f->net, ingress);
		if (!block)
			return ERR_PTR(-ENOMEM);

@@ -209,7 +213,7 @@ static void prestera_flow_block_put(struct prestera_flow_block *block)
}

static int prestera_setup_flow_block_bind(struct prestera_port *port,
					  struct flow_block_offload *f)
					  struct flow_block_offload *f, bool ingress)
{
	struct prestera_switch *sw = port->sw;
	struct prestera_flow_block *block;
@@ -217,7 +221,7 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port,
	bool register_block;
	int err;

	block = prestera_flow_block_get(sw, f, &register_block);
	block = prestera_flow_block_get(sw, f, &register_block, ingress);
	if (IS_ERR(block))
		return PTR_ERR(block);

@@ -232,7 +236,11 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port,
		list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
	}

	port->flow_block = block;
	if (ingress)
		port->ingress_flow_block = block;
	else
		port->egress_flow_block = block;

	return 0;

err_block_bind:
@@ -242,7 +250,7 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port,
}

static void prestera_setup_flow_block_unbind(struct prestera_port *port,
					     struct flow_block_offload *f)
					     struct flow_block_offload *f, bool ingress)
{
	struct prestera_switch *sw = port->sw;
	struct prestera_flow_block *block;
@@ -266,24 +274,38 @@ static void prestera_setup_flow_block_unbind(struct prestera_port *port,
		list_del(&block_cb->driver_list);
	}
error:
	port->flow_block = NULL;
	if (ingress)
		port->ingress_flow_block = NULL;
	else
		port->egress_flow_block = NULL;
}

int prestera_flow_block_setup(struct prestera_port *port,
			      struct flow_block_offload *f)
static int prestera_setup_flow_block_clsact(struct prestera_port *port,
					    struct flow_block_offload *f,
					    bool ingress)
{
	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
		return -EOPNOTSUPP;

	f->driver_block_list = &prestera_block_cb_list;

	switch (f->command) {
	case FLOW_BLOCK_BIND:
		return prestera_setup_flow_block_bind(port, f);
		return prestera_setup_flow_block_bind(port, f, ingress);
	case FLOW_BLOCK_UNBIND:
		prestera_setup_flow_block_unbind(port, f);
		prestera_setup_flow_block_unbind(port, f, ingress);
		return 0;
	default:
		return -EOPNOTSUPP;
	}
}

int prestera_flow_block_setup(struct prestera_port *port,
			      struct flow_block_offload *f)
{
	switch (f->binder_type) {
	case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
		return prestera_setup_flow_block_clsact(port, f, true);
	case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
		return prestera_setup_flow_block_clsact(port, f, false);
	default:
		return -EOPNOTSUPP;
	}
}
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ struct prestera_flow_block {
	struct flow_block_cb *block_cb;
	struct list_head template_list;
	unsigned int rule_count;
	bool ingress;
};

int prestera_flow_block_setup(struct prestera_port *port,
Loading