Commit d091ec97 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa-felix-psfp'



Xiaoliang Yang says:

====================
net: dsa: felix: psfp support on vsc9959

VSC9959 hardware supports Per-Stream Filtering and Policing(PSFP).
This patch series add PSFP support on tc flower offload of ocelot
driver. Use chain 30000 to distinguish PSFP from VCAP blocks. Add gate
and police set to support PSFP in VSC9959 driver.

v6-v7 changes:
 - Add a patch to restrict psfp rules on ingress port.
 - Using stats.drops to show the packet count discarded by the rule.

v5->v6 changes:
 - Modify ocelot_mact_lookup() parameters.
 - Use parameters ssid and sfid instead of streamdata in
   ocelot_mact_learn_streamdata() function.
 - Serialize STREAMDATA and MAC table write.

v4->v5 changes:
 - Add MAC table lock patch, and move stream data write in
   ocelot_mact_learn_streamdata().
 - Add two sections of VCAP policers to Seville platform.

v3->v4 changes:
 - Introduce vsc9959_psfp_sfi_table_get() function in patch where it is
   used to fix compile warning.

v2->v3 changes:
 - Reorder first two patches. Export struct ocelot_mact_entry, then add
   ocelot_mact_lookup() and ocelot_mact_write() functions.
 - Add PSFP list to struct ocelot, and init it by using
   ocelot->ops->psfp_init().

v1->v2 changes:
 - Use tc flower offload of ocelot driver to support PSFP add and delete.
 - Add PSFP tables add/del functions in felix_vsc9959.c.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f6ef47e5 a7e13edf
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -989,6 +989,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
	ocelot->num_stats	= felix->info->num_stats;
	ocelot->num_mact_rows	= felix->info->num_mact_rows;
	ocelot->vcap		= felix->info->vcap;
	ocelot->vcap_pol.base	= felix->info->vcap_pol_base;
	ocelot->vcap_pol.max	= felix->info->vcap_pol_max;
	ocelot->vcap_pol.base2	= felix->info->vcap_pol_base2;
	ocelot->vcap_pol.max2	= felix->info->vcap_pol_max2;
	ocelot->ops		= felix->info->ops;
	ocelot->npi_inj_prefix	= OCELOT_TAG_PREFIX_SHORT;
	ocelot->npi_xtr_prefix	= OCELOT_TAG_PREFIX_SHORT;
+4 −0
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ struct felix_info {
	int				num_ports;
	int				num_tx_queues;
	struct vcap_props		*vcap;
	u16				vcap_pol_base;
	u16				vcap_pol_max;
	u16				vcap_pol_base2;
	u16				vcap_pol_max2;
	int				switch_pci_bar;
	int				imdio_pci_bar;
	const struct ptp_clock_info	*ptp_caps;
+808 −10
Original line number Diff line number Diff line
@@ -5,8 +5,10 @@
#include <linux/fsl/enetc_mdio.h>
#include <soc/mscc/ocelot_qsys.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot_sys.h>
#include <net/tc_act/tc_gate.h>
#include <soc/mscc/ocelot.h>
#include <linux/dsa/ocelot.h>
#include <linux/pcs-lynx.h>
@@ -17,6 +19,8 @@
#include "felix.h"

#define VSC9959_TAS_GCL_ENTRY_MAX	63
#define VSC9959_VCAP_POLICER_BASE	63
#define VSC9959_VCAP_POLICER_MAX	383

static const u32 vsc9959_ana_regmap[] = {
	REG(ANA_ADVLEARN,			0x0089a0),
@@ -292,7 +296,7 @@ static const u32 vsc9959_sys_regmap[] = {
	REG_RESERVED(SYS_MMGT_FAST),
	REG_RESERVED(SYS_EVENTS_DIF),
	REG_RESERVED(SYS_EVENTS_CORE),
	REG_RESERVED(SYS_CNT),
	REG(SYS_CNT,				0x000000),
	REG(SYS_PTP_STATUS,			0x000f14),
	REG(SYS_PTP_TXSTAMP,			0x000f18),
	REG(SYS_PTP_NXT,			0x000f1c),
@@ -1020,15 +1024,6 @@ static void vsc9959_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
	*maxuse = val & GENMASK(11, 0);
}

static const struct ocelot_ops vsc9959_ops = {
	.reset			= vsc9959_reset,
	.wm_enc			= vsc9959_wm_enc,
	.wm_dec			= vsc9959_wm_dec,
	.wm_stat		= vsc9959_wm_stat,
	.port_to_netdev		= felix_port_to_netdev,
	.netdev_to_port		= felix_netdev_to_port,
};

static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
{
	struct felix *felix = ocelot_to_felix(ocelot);
@@ -1344,6 +1339,805 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,
	}
}

#define VSC9959_PSFP_SFID_MAX			175
#define VSC9959_PSFP_GATE_ID_MAX		183
#define VSC9959_PSFP_POLICER_BASE		63
#define VSC9959_PSFP_POLICER_MAX		383
#define VSC9959_PSFP_GATE_LIST_NUM		4
#define VSC9959_PSFP_GATE_CYCLETIME_MIN		5000

struct felix_stream {
	struct list_head list;
	unsigned long id;
	bool dummy;
	int ports;
	int port;
	u8 dmac[ETH_ALEN];
	u16 vid;
	s8 prio;
	u8 sfid_valid;
	u8 ssid_valid;
	u32 sfid;
	u32 ssid;
};

struct felix_stream_filter {
	struct list_head list;
	refcount_t refcount;
	u32 index;
	u8 enable;
	int portmask;
	u8 sg_valid;
	u32 sgid;
	u8 fm_valid;
	u32 fmid;
	u8 prio_valid;
	u8 prio;
	u32 maxsdu;
};

struct felix_stream_filter_counters {
	u32 match;
	u32 not_pass_gate;
	u32 not_pass_sdu;
	u32 red;
};

struct felix_stream_gate {
	u32 index;
	u8 enable;
	u8 ipv_valid;
	u8 init_ipv;
	u64 basetime;
	u64 cycletime;
	u64 cycletime_ext;
	u32 num_entries;
	struct action_gate_entry entries[0];
};

struct felix_stream_gate_entry {
	struct list_head list;
	refcount_t refcount;
	u32 index;
};

static int vsc9959_stream_identify(struct flow_cls_offload *f,
				   struct felix_stream *stream)
{
	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
	struct flow_dissector *dissector = rule->match.dissector;

	if (dissector->used_keys &
	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS)))
		return -EOPNOTSUPP;

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
		struct flow_match_eth_addrs match;

		flow_rule_match_eth_addrs(rule, &match);
		ether_addr_copy(stream->dmac, match.key->dst);
		if (!is_zero_ether_addr(match.mask->src))
			return -EOPNOTSUPP;
	} else {
		return -EOPNOTSUPP;
	}

	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
		struct flow_match_vlan match;

		flow_rule_match_vlan(rule, &match);
		if (match.mask->vlan_priority)
			stream->prio = match.key->vlan_priority;
		else
			stream->prio = -1;

		if (!match.mask->vlan_id)
			return -EOPNOTSUPP;
		stream->vid = match.key->vlan_id;
	} else {
		return -EOPNOTSUPP;
	}

	stream->id = f->cookie;

	return 0;
}

static int vsc9959_mact_stream_set(struct ocelot *ocelot,
				   struct felix_stream *stream,
				   struct netlink_ext_ack *extack)
{
	enum macaccess_entry_type type;
	int ret, sfid, ssid;
	u32 vid, dst_idx;
	u8 mac[ETH_ALEN];

	ether_addr_copy(mac, stream->dmac);
	vid = stream->vid;

	/* Stream identification desn't support to add a stream with non
	 * existent MAC (The MAC entry has not been learned in MAC table).
	 */
	ret = ocelot_mact_lookup(ocelot, &dst_idx, mac, vid, &type);
	if (ret) {
		if (extack)
			NL_SET_ERR_MSG_MOD(extack, "Stream is not learned in MAC table");
		return -EOPNOTSUPP;
	}

	if ((stream->sfid_valid || stream->ssid_valid) &&
	    type == ENTRYTYPE_NORMAL)
		type = ENTRYTYPE_LOCKED;

	sfid = stream->sfid_valid ? stream->sfid : -1;
	ssid = stream->ssid_valid ? stream->ssid : -1;

	ret = ocelot_mact_learn_streamdata(ocelot, dst_idx, mac, vid, type,
					   sfid, ssid);

	return ret;
}

static struct felix_stream *
vsc9959_stream_table_lookup(struct list_head *stream_list,
			    struct felix_stream *stream)
{
	struct felix_stream *tmp;

	list_for_each_entry(tmp, stream_list, list)
		if (ether_addr_equal(tmp->dmac, stream->dmac) &&
		    tmp->vid == stream->vid)
			return tmp;

	return NULL;
}

static int vsc9959_stream_table_add(struct ocelot *ocelot,
				    struct list_head *stream_list,
				    struct felix_stream *stream,
				    struct netlink_ext_ack *extack)
{
	struct felix_stream *stream_entry;
	int ret;

	stream_entry = kzalloc(sizeof(*stream_entry), GFP_KERNEL);
	if (!stream_entry)
		return -ENOMEM;

	memcpy(stream_entry, stream, sizeof(*stream_entry));

	if (!stream->dummy) {
		ret = vsc9959_mact_stream_set(ocelot, stream_entry, extack);
		if (ret) {
			kfree(stream_entry);
			return ret;
		}
	}

	list_add_tail(&stream_entry->list, stream_list);

	return 0;
}

static struct felix_stream *
vsc9959_stream_table_get(struct list_head *stream_list, unsigned long id)
{
	struct felix_stream *tmp;

	list_for_each_entry(tmp, stream_list, list)
		if (tmp->id == id)
			return tmp;

	return NULL;
}

static void vsc9959_stream_table_del(struct ocelot *ocelot,
				     struct felix_stream *stream)
{
	if (!stream->dummy)
		vsc9959_mact_stream_set(ocelot, stream, NULL);

	list_del(&stream->list);
	kfree(stream);
}

static u32 vsc9959_sfi_access_status(struct ocelot *ocelot)
{
	return ocelot_read(ocelot, ANA_TABLES_SFIDACCESS);
}

static int vsc9959_psfp_sfi_set(struct ocelot *ocelot,
				struct felix_stream_filter *sfi)
{
	u32 val;

	if (sfi->index > VSC9959_PSFP_SFID_MAX)
		return -EINVAL;

	if (!sfi->enable) {
		ocelot_write(ocelot, ANA_TABLES_SFIDTIDX_SFID_INDEX(sfi->index),
			     ANA_TABLES_SFIDTIDX);

		val = ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE);
		ocelot_write(ocelot, val, ANA_TABLES_SFIDACCESS);

		return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val,
					  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)),
					  10, 100000);
	}

	if (sfi->sgid > VSC9959_PSFP_GATE_ID_MAX ||
	    sfi->fmid > VSC9959_PSFP_POLICER_MAX)
		return -EINVAL;

	ocelot_write(ocelot,
		     (sfi->sg_valid ? ANA_TABLES_SFIDTIDX_SGID_VALID : 0) |
		     ANA_TABLES_SFIDTIDX_SGID(sfi->sgid) |
		     (sfi->fm_valid ? ANA_TABLES_SFIDTIDX_POL_ENA : 0) |
		     ANA_TABLES_SFIDTIDX_POL_IDX(sfi->fmid) |
		     ANA_TABLES_SFIDTIDX_SFID_INDEX(sfi->index),
		     ANA_TABLES_SFIDTIDX);

	ocelot_write(ocelot,
		     (sfi->prio_valid ? ANA_TABLES_SFIDACCESS_IGR_PRIO_MATCH_ENA : 0) |
		     ANA_TABLES_SFIDACCESS_IGR_PRIO(sfi->prio) |
		     ANA_TABLES_SFIDACCESS_MAX_SDU_LEN(sfi->maxsdu) |
		     ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE),
		     ANA_TABLES_SFIDACCESS);

	return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val,
				  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)),
				  10, 100000);
}

static int vsc9959_psfp_sfidmask_set(struct ocelot *ocelot, u32 sfid, int ports)
{
	u32 val;

	ocelot_rmw(ocelot,
		   ANA_TABLES_SFIDTIDX_SFID_INDEX(sfid),
		   ANA_TABLES_SFIDTIDX_SFID_INDEX_M,
		   ANA_TABLES_SFIDTIDX);

	ocelot_write(ocelot,
		     ANA_TABLES_SFID_MASK_IGR_PORT_MASK(ports) |
		     ANA_TABLES_SFID_MASK_IGR_SRCPORT_MATCH_ENA,
		     ANA_TABLES_SFID_MASK);

	ocelot_rmw(ocelot,
		   ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE),
		   ANA_TABLES_SFIDACCESS_SFID_TBL_CMD_M,
		   ANA_TABLES_SFIDACCESS);

	return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val,
				  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)),
				  10, 100000);
}

static int vsc9959_psfp_sfi_list_add(struct ocelot *ocelot,
				     struct felix_stream_filter *sfi,
				     struct list_head *pos)
{
	struct felix_stream_filter *sfi_entry;
	int ret;

	sfi_entry = kzalloc(sizeof(*sfi_entry), GFP_KERNEL);
	if (!sfi_entry)
		return -ENOMEM;

	memcpy(sfi_entry, sfi, sizeof(*sfi_entry));
	refcount_set(&sfi_entry->refcount, 1);

	ret = vsc9959_psfp_sfi_set(ocelot, sfi_entry);
	if (ret) {
		kfree(sfi_entry);
		return ret;
	}

	vsc9959_psfp_sfidmask_set(ocelot, sfi->index, sfi->portmask);

	list_add(&sfi_entry->list, pos);

	return 0;
}

static int vsc9959_psfp_sfi_table_add(struct ocelot *ocelot,
				      struct felix_stream_filter *sfi)
{
	struct list_head *pos, *q, *last;
	struct felix_stream_filter *tmp;
	struct ocelot_psfp_list *psfp;
	u32 insert = 0;

	psfp = &ocelot->psfp;
	last = &psfp->sfi_list;

	list_for_each_safe(pos, q, &psfp->sfi_list) {
		tmp = list_entry(pos, struct felix_stream_filter, list);
		if (sfi->sg_valid == tmp->sg_valid &&
		    sfi->fm_valid == tmp->fm_valid &&
		    sfi->portmask == tmp->portmask &&
		    tmp->sgid == sfi->sgid &&
		    tmp->fmid == sfi->fmid) {
			sfi->index = tmp->index;
			refcount_inc(&tmp->refcount);
			return 0;
		}
		/* Make sure that the index is increasing in order. */
		if (tmp->index == insert) {
			last = pos;
			insert++;
		}
	}
	sfi->index = insert;

	return vsc9959_psfp_sfi_list_add(ocelot, sfi, last);
}

static int vsc9959_psfp_sfi_table_add2(struct ocelot *ocelot,
				       struct felix_stream_filter *sfi,
				       struct felix_stream_filter *sfi2)
{
	struct felix_stream_filter *tmp;
	struct list_head *pos, *q, *last;
	struct ocelot_psfp_list *psfp;
	u32 insert = 0;
	int ret;

	psfp = &ocelot->psfp;
	last = &psfp->sfi_list;

	list_for_each_safe(pos, q, &psfp->sfi_list) {
		tmp = list_entry(pos, struct felix_stream_filter, list);
		/* Make sure that the index is increasing in order. */
		if (tmp->index >= insert + 2)
			break;

		insert = tmp->index + 1;
		last = pos;
	}
	sfi->index = insert;

	ret = vsc9959_psfp_sfi_list_add(ocelot, sfi, last);
	if (ret)
		return ret;

	sfi2->index = insert + 1;

	return vsc9959_psfp_sfi_list_add(ocelot, sfi2, last->next);
}

static struct felix_stream_filter *
vsc9959_psfp_sfi_table_get(struct list_head *sfi_list, u32 index)
{
	struct felix_stream_filter *tmp;

	list_for_each_entry(tmp, sfi_list, list)
		if (tmp->index == index)
			return tmp;

	return NULL;
}

static void vsc9959_psfp_sfi_table_del(struct ocelot *ocelot, u32 index)
{
	struct felix_stream_filter *tmp, *n;
	struct ocelot_psfp_list *psfp;
	u8 z;

	psfp = &ocelot->psfp;

	list_for_each_entry_safe(tmp, n, &psfp->sfi_list, list)
		if (tmp->index == index) {
			z = refcount_dec_and_test(&tmp->refcount);
			if (z) {
				tmp->enable = 0;
				vsc9959_psfp_sfi_set(ocelot, tmp);
				list_del(&tmp->list);
				kfree(tmp);
			}
			break;
		}
}

static void vsc9959_psfp_parse_gate(const struct flow_action_entry *entry,
				    struct felix_stream_gate *sgi)
{
	sgi->index = entry->gate.index;
	sgi->ipv_valid = (entry->gate.prio < 0) ? 0 : 1;
	sgi->init_ipv = (sgi->ipv_valid) ? entry->gate.prio : 0;
	sgi->basetime = entry->gate.basetime;
	sgi->cycletime = entry->gate.cycletime;
	sgi->num_entries = entry->gate.num_entries;
	sgi->enable = 1;

	memcpy(sgi->entries, entry->gate.entries,
	       entry->gate.num_entries * sizeof(struct action_gate_entry));
}

static u32 vsc9959_sgi_cfg_status(struct ocelot *ocelot)
{
	return ocelot_read(ocelot, ANA_SG_ACCESS_CTRL);
}

static int vsc9959_psfp_sgi_set(struct ocelot *ocelot,
				struct felix_stream_gate *sgi)
{
	struct action_gate_entry *e;
	struct timespec64 base_ts;
	u32 interval_sum = 0;
	u32 val;
	int i;

	if (sgi->index > VSC9959_PSFP_GATE_ID_MAX)
		return -EINVAL;

	ocelot_write(ocelot, ANA_SG_ACCESS_CTRL_SGID(sgi->index),
		     ANA_SG_ACCESS_CTRL);

	if (!sgi->enable) {
		ocelot_rmw(ocelot, ANA_SG_CONFIG_REG_3_INIT_GATE_STATE,
			   ANA_SG_CONFIG_REG_3_INIT_GATE_STATE |
			   ANA_SG_CONFIG_REG_3_GATE_ENABLE,
			   ANA_SG_CONFIG_REG_3);

		return 0;
	}

	if (sgi->cycletime < VSC9959_PSFP_GATE_CYCLETIME_MIN ||
	    sgi->cycletime > NSEC_PER_SEC)
		return -EINVAL;

	if (sgi->num_entries > VSC9959_PSFP_GATE_LIST_NUM)
		return -EINVAL;

	vsc9959_new_base_time(ocelot, sgi->basetime, sgi->cycletime, &base_ts);
	ocelot_write(ocelot, base_ts.tv_nsec, ANA_SG_CONFIG_REG_1);
	val = lower_32_bits(base_ts.tv_sec);
	ocelot_write(ocelot, val, ANA_SG_CONFIG_REG_2);

	val = upper_32_bits(base_ts.tv_sec);
	ocelot_write(ocelot,
		     (sgi->ipv_valid ? ANA_SG_CONFIG_REG_3_IPV_VALID : 0) |
		     ANA_SG_CONFIG_REG_3_INIT_IPV(sgi->init_ipv) |
		     ANA_SG_CONFIG_REG_3_GATE_ENABLE |
		     ANA_SG_CONFIG_REG_3_LIST_LENGTH(sgi->num_entries) |
		     ANA_SG_CONFIG_REG_3_INIT_GATE_STATE |
		     ANA_SG_CONFIG_REG_3_BASE_TIME_SEC_MSB(val),
		     ANA_SG_CONFIG_REG_3);

	ocelot_write(ocelot, sgi->cycletime, ANA_SG_CONFIG_REG_4);

	e = sgi->entries;
	for (i = 0; i < sgi->num_entries; i++) {
		u32 ips = (e[i].ipv < 0) ? 0 : (e[i].ipv + 8);

		ocelot_write_rix(ocelot, ANA_SG_GCL_GS_CONFIG_IPS(ips) |
				 (e[i].gate_state ?
				  ANA_SG_GCL_GS_CONFIG_GATE_STATE : 0),
				 ANA_SG_GCL_GS_CONFIG, i);

		interval_sum += e[i].interval;
		ocelot_write_rix(ocelot, interval_sum, ANA_SG_GCL_TI_CONFIG, i);
	}

	ocelot_rmw(ocelot, ANA_SG_ACCESS_CTRL_CONFIG_CHANGE,
		   ANA_SG_ACCESS_CTRL_CONFIG_CHANGE,
		   ANA_SG_ACCESS_CTRL);

	return readx_poll_timeout(vsc9959_sgi_cfg_status, ocelot, val,
				  (!(ANA_SG_ACCESS_CTRL_CONFIG_CHANGE & val)),
				  10, 100000);
}

static int vsc9959_psfp_sgi_table_add(struct ocelot *ocelot,
				      struct felix_stream_gate *sgi)
{
	struct felix_stream_gate_entry *tmp;
	struct ocelot_psfp_list *psfp;
	int ret;

	psfp = &ocelot->psfp;

	list_for_each_entry(tmp, &psfp->sgi_list, list)
		if (tmp->index == sgi->index) {
			refcount_inc(&tmp->refcount);
			return 0;
		}

	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
	if (!tmp)
		return -ENOMEM;

	ret = vsc9959_psfp_sgi_set(ocelot, sgi);
	if (ret) {
		kfree(tmp);
		return ret;
	}

	tmp->index = sgi->index;
	refcount_set(&tmp->refcount, 1);
	list_add_tail(&tmp->list, &psfp->sgi_list);

	return 0;
}

static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot,
				       u32 index)
{
	struct felix_stream_gate_entry *tmp, *n;
	struct felix_stream_gate sgi = {0};
	struct ocelot_psfp_list *psfp;
	u8 z;

	psfp = &ocelot->psfp;

	list_for_each_entry_safe(tmp, n, &psfp->sgi_list, list)
		if (tmp->index == index) {
			z = refcount_dec_and_test(&tmp->refcount);
			if (z) {
				sgi.index = index;
				sgi.enable = 0;
				vsc9959_psfp_sgi_set(ocelot, &sgi);
				list_del(&tmp->list);
				kfree(tmp);
			}
			break;
		}
}

static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index,
				      struct felix_stream_filter_counters *counters)
{
	ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(index),
		   SYS_STAT_CFG_STAT_VIEW_M,
		   SYS_STAT_CFG);

	counters->match = ocelot_read_gix(ocelot, SYS_CNT, 0x200);
	counters->not_pass_gate = ocelot_read_gix(ocelot, SYS_CNT, 0x201);
	counters->not_pass_sdu = ocelot_read_gix(ocelot, SYS_CNT, 0x202);
	counters->red = ocelot_read_gix(ocelot, SYS_CNT, 0x203);

	/* Clear the PSFP counter. */
	ocelot_write(ocelot,
		     SYS_STAT_CFG_STAT_VIEW(index) |
		     SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10),
		     SYS_STAT_CFG);
}

static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port,
				   struct flow_cls_offload *f)
{
	struct netlink_ext_ack *extack = f->common.extack;
	struct felix_stream_filter old_sfi, *sfi_entry;
	struct felix_stream_filter sfi = {0};
	const struct flow_action_entry *a;
	struct felix_stream *stream_entry;
	struct felix_stream stream = {0};
	struct felix_stream_gate *sgi;
	struct ocelot_psfp_list *psfp;
	struct ocelot_policer pol;
	int ret, i, size;
	u64 rate, burst;
	u32 index;

	psfp = &ocelot->psfp;

	ret = vsc9959_stream_identify(f, &stream);
	if (ret) {
		NL_SET_ERR_MSG_MOD(extack, "Only can match on VID, PCP, and dest MAC");
		return ret;
	}

	flow_action_for_each(i, a, &f->rule->action) {
		switch (a->id) {
		case FLOW_ACTION_GATE:
			size = struct_size(sgi, entries, a->gate.num_entries);
			sgi = kzalloc(size, GFP_KERNEL);
			vsc9959_psfp_parse_gate(a, sgi);
			ret = vsc9959_psfp_sgi_table_add(ocelot, sgi);
			if (ret) {
				kfree(sgi);
				goto err;
			}
			sfi.sg_valid = 1;
			sfi.sgid = sgi->index;
			kfree(sgi);
			break;
		case FLOW_ACTION_POLICE:
			index = a->police.index + VSC9959_PSFP_POLICER_BASE;
			if (index > VSC9959_PSFP_POLICER_MAX) {
				ret = -EINVAL;
				goto err;
			}

			rate = a->police.rate_bytes_ps;
			burst = rate * PSCHED_NS2TICKS(a->police.burst);
			pol = (struct ocelot_policer) {
				.burst = div_u64(burst, PSCHED_TICKS_PER_SEC),
				.rate = div_u64(rate, 1000) * 8,
			};
			ret = ocelot_vcap_policer_add(ocelot, index, &pol);
			if (ret)
				goto err;

			sfi.fm_valid = 1;
			sfi.fmid = index;
			sfi.maxsdu = a->police.mtu;
			break;
		default:
			return -EOPNOTSUPP;
		}
	}

	stream.ports = BIT(port);
	stream.port = port;

	sfi.portmask = stream.ports;
	sfi.prio_valid = (stream.prio < 0 ? 0 : 1);
	sfi.prio = (sfi.prio_valid ? stream.prio : 0);
	sfi.enable = 1;

	/* Check if stream is set. */
	stream_entry = vsc9959_stream_table_lookup(&psfp->stream_list, &stream);
	if (stream_entry) {
		if (stream_entry->ports & BIT(port)) {
			NL_SET_ERR_MSG_MOD(extack,
					   "The stream is added on this port");
			ret = -EEXIST;
			goto err;
		}

		if (stream_entry->ports != BIT(stream_entry->port)) {
			NL_SET_ERR_MSG_MOD(extack,
					   "The stream is added on two ports");
			ret = -EEXIST;
			goto err;
		}

		stream_entry->ports |= BIT(port);
		stream.ports = stream_entry->ports;

		sfi_entry = vsc9959_psfp_sfi_table_get(&psfp->sfi_list,
						       stream_entry->sfid);
		memcpy(&old_sfi, sfi_entry, sizeof(old_sfi));

		vsc9959_psfp_sfi_table_del(ocelot, stream_entry->sfid);

		old_sfi.portmask = stream_entry->ports;
		sfi.portmask = stream.ports;

		if (stream_entry->port > port) {
			ret = vsc9959_psfp_sfi_table_add2(ocelot, &sfi,
							  &old_sfi);
			stream_entry->dummy = true;
		} else {
			ret = vsc9959_psfp_sfi_table_add2(ocelot, &old_sfi,
							  &sfi);
			stream.dummy = true;
		}
		if (ret)
			goto err;

		stream_entry->sfid = old_sfi.index;
	} else {
		ret = vsc9959_psfp_sfi_table_add(ocelot, &sfi);
		if (ret)
			goto err;
	}

	stream.sfid = sfi.index;
	stream.sfid_valid = 1;
	ret = vsc9959_stream_table_add(ocelot, &psfp->stream_list,
				       &stream, extack);
	if (ret) {
		vsc9959_psfp_sfi_table_del(ocelot, stream.sfid);
		goto err;
	}

	return 0;

err:
	if (sfi.sg_valid)
		vsc9959_psfp_sgi_table_del(ocelot, sfi.sgid);

	if (sfi.fm_valid)
		ocelot_vcap_policer_del(ocelot, sfi.fmid);

	return ret;
}

static int vsc9959_psfp_filter_del(struct ocelot *ocelot,
				   struct flow_cls_offload *f)
{
	struct felix_stream *stream, tmp, *stream_entry;
	static struct felix_stream_filter *sfi;
	struct ocelot_psfp_list *psfp;

	psfp = &ocelot->psfp;

	stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
	if (!stream)
		return -ENOMEM;

	sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid);
	if (!sfi)
		return -ENOMEM;

	if (sfi->sg_valid)
		vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid);

	if (sfi->fm_valid)
		ocelot_vcap_policer_del(ocelot, sfi->fmid);

	vsc9959_psfp_sfi_table_del(ocelot, stream->sfid);

	memcpy(&tmp, stream, sizeof(tmp));

	stream->sfid_valid = 0;
	vsc9959_stream_table_del(ocelot, stream);

	stream_entry = vsc9959_stream_table_lookup(&psfp->stream_list, &tmp);
	if (stream_entry) {
		stream_entry->ports = BIT(stream_entry->port);
		if (stream_entry->dummy) {
			stream_entry->dummy = false;
			vsc9959_mact_stream_set(ocelot, stream_entry, NULL);
		}
		vsc9959_psfp_sfidmask_set(ocelot, stream_entry->sfid,
					  stream_entry->ports);
	}

	return 0;
}

static int vsc9959_psfp_stats_get(struct ocelot *ocelot,
				  struct flow_cls_offload *f,
				  struct flow_stats *stats)
{
	struct felix_stream_filter_counters counters;
	struct ocelot_psfp_list *psfp;
	struct felix_stream *stream;

	psfp = &ocelot->psfp;
	stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie);
	if (!stream)
		return -ENOMEM;

	vsc9959_psfp_counters_get(ocelot, stream->sfid, &counters);

	stats->pkts = counters.match;
	stats->drops = counters.not_pass_gate + counters.not_pass_sdu +
		       counters.red;

	return 0;
}

static void vsc9959_psfp_init(struct ocelot *ocelot)
{
	struct ocelot_psfp_list *psfp = &ocelot->psfp;

	INIT_LIST_HEAD(&psfp->stream_list);
	INIT_LIST_HEAD(&psfp->sfi_list);
	INIT_LIST_HEAD(&psfp->sgi_list);
}

static const struct ocelot_ops vsc9959_ops = {
	.reset			= vsc9959_reset,
	.wm_enc			= vsc9959_wm_enc,
	.wm_dec			= vsc9959_wm_dec,
	.wm_stat		= vsc9959_wm_stat,
	.port_to_netdev		= felix_port_to_netdev,
	.netdev_to_port		= felix_netdev_to_port,
	.psfp_init		= vsc9959_psfp_init,
	.psfp_filter_add	= vsc9959_psfp_filter_add,
	.psfp_filter_del	= vsc9959_psfp_filter_del,
	.psfp_stats_get		= vsc9959_psfp_stats_get,
};

static const struct felix_info felix_info_vsc9959 = {
	.target_io_res		= vsc9959_target_io_res,
	.port_io_res		= vsc9959_port_io_res,
@@ -1354,6 +2148,10 @@ static const struct felix_info felix_info_vsc9959 = {
	.stats_layout		= vsc9959_stats_layout,
	.num_stats		= ARRAY_SIZE(vsc9959_stats_layout),
	.vcap			= vsc9959_vcap_props,
	.vcap_pol_base		= VSC9959_VCAP_POLICER_BASE,
	.vcap_pol_max		= VSC9959_VCAP_POLICER_MAX,
	.vcap_pol_base2		= 0,
	.vcap_pol_max2		= 0,
	.num_mact_rows		= 2048,
	.num_ports		= 6,
	.num_tx_queues		= OCELOT_NUM_TC,
+8 −0
Original line number Diff line number Diff line
@@ -18,6 +18,10 @@
#define MSCC_MIIM_CMD_REGAD_SHIFT		20
#define MSCC_MIIM_CMD_PHYAD_SHIFT		25
#define MSCC_MIIM_CMD_VLD			BIT(31)
#define VSC9953_VCAP_POLICER_BASE		11
#define VSC9953_VCAP_POLICER_MAX		31
#define VSC9953_VCAP_POLICER_BASE2		120
#define VSC9953_VCAP_POLICER_MAX2		161

static const u32 vsc9953_ana_regmap[] = {
	REG(ANA_ADVLEARN,			0x00b500),
@@ -1172,6 +1176,10 @@ static const struct felix_info seville_info_vsc9953 = {
	.stats_layout		= vsc9953_stats_layout,
	.num_stats		= ARRAY_SIZE(vsc9953_stats_layout),
	.vcap			= vsc9953_vcap_props,
	.vcap_pol_base		= VSC9953_VCAP_POLICER_BASE,
	.vcap_pol_max		= VSC9953_VCAP_POLICER_MAX,
	.vcap_pol_base2		= VSC9953_VCAP_POLICER_BASE2,
	.vcap_pol_max2		= VSC9953_VCAP_POLICER_MAX2,
	.num_mact_rows		= 2048,
	.num_ports		= 10,
	.num_tx_queues		= OCELOT_NUM_TC,
+78 −6

File changed.

Preview size limit exceeded, changes collapsed.

Loading