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

Commit 2d96cf8c authored by Santwona Behera's avatar Santwona Behera Committed by David S. Miller
Browse files

niu: Add TCAM classification configuration

parent 59089d8d
Loading
Loading
Loading
Loading
+675 −24
Original line number Diff line number Diff line
@@ -2981,7 +2981,6 @@ static int tcam_user_ip_class_enable(struct niu *np, unsigned long class,
	return 0;
}

#if 0
static int tcam_user_ip_class_set(struct niu *np, unsigned long class,
				  int ipv6, u64 protocol_id,
				  u64 tos_mask, u64 tos_val)
@@ -3009,7 +3008,6 @@ static int tcam_user_ip_class_set(struct niu *np, unsigned long class,

	return 0;
}
#endif

static int tcam_early_init(struct niu *np)
{
@@ -3276,6 +3274,27 @@ static int niu_set_tcam_key(struct niu *np, unsigned long class_code, u64 key)
	return 0;
}

/* Entries for the ports are interleaved in the TCAM */
static u16 tcam_get_index(struct niu *np, u16 idx)
{
	/* One entry reserved for IP fragment rule */
	if (idx >= (np->clas.tcam_sz - 1))
		idx = 0;
	return (np->clas.tcam_top + ((idx+1) * np->parent->num_ports));
}

static u16 tcam_get_size(struct niu *np)
{
	/* One entry reserved for IP fragment rule */
	return np->clas.tcam_sz - 1;
}

static u16 tcam_get_valid_entry_cnt(struct niu *np)
{
	/* One entry reserved for IP fragment rule */
	return np->clas.tcam_valid_entries - 1;
}

static void niu_rx_skb_append(struct sk_buff *skb, struct page *page,
			      u32 offset, u32 size)
{
@@ -4999,8 +5018,7 @@ static int niu_set_ip_frag_rule(struct niu *np)
	struct niu_tcam_entry *tp;
	int index, err;

	/* XXX fix this allocation scheme XXX */
	index = cp->tcam_index;
	index = cp->tcam_top;
	tp = &parent->tcam[index];

	/* Note that the noport bit is the same in both ipv4 and
@@ -5017,6 +5035,8 @@ static int niu_set_ip_frag_rule(struct niu *np)
	err = tcam_assoc_write(np, index, tp->assoc_data);
	if (err)
		return err;
	tp->valid = 1;
	cp->tcam_valid_entries++;

	return 0;
}
@@ -6907,6 +6927,75 @@ static int niu_get_eeprom(struct net_device *dev,
	return 0;
}

static void niu_ethflow_to_l3proto(int flow_type, u8 *pid)
{
	switch (flow_type) {
	case TCP_V4_FLOW:
	case TCP_V6_FLOW:
		*pid = IPPROTO_TCP;
		break;
	case UDP_V4_FLOW:
	case UDP_V6_FLOW:
		*pid = IPPROTO_UDP;
		break;
	case SCTP_V4_FLOW:
	case SCTP_V6_FLOW:
		*pid = IPPROTO_SCTP;
		break;
	case AH_V4_FLOW:
	case AH_V6_FLOW:
		*pid = IPPROTO_AH;
		break;
	case ESP_V4_FLOW:
	case ESP_V6_FLOW:
		*pid = IPPROTO_ESP;
		break;
	default:
		*pid = 0;
		break;
	}
}

static int niu_class_to_ethflow(u64 class, int *flow_type)
{
	switch (class) {
	case CLASS_CODE_TCP_IPV4:
		*flow_type = TCP_V4_FLOW;
		break;
	case CLASS_CODE_UDP_IPV4:
		*flow_type = UDP_V4_FLOW;
		break;
	case CLASS_CODE_AH_ESP_IPV4:
		*flow_type = AH_V4_FLOW;
		break;
	case CLASS_CODE_SCTP_IPV4:
		*flow_type = SCTP_V4_FLOW;
		break;
	case CLASS_CODE_TCP_IPV6:
		*flow_type = TCP_V6_FLOW;
		break;
	case CLASS_CODE_UDP_IPV6:
		*flow_type = UDP_V6_FLOW;
		break;
	case CLASS_CODE_AH_ESP_IPV6:
		*flow_type = AH_V6_FLOW;
		break;
	case CLASS_CODE_SCTP_IPV6:
		*flow_type = SCTP_V6_FLOW;
		break;
	case CLASS_CODE_USER_PROG1:
	case CLASS_CODE_USER_PROG2:
	case CLASS_CODE_USER_PROG3:
	case CLASS_CODE_USER_PROG4:
		*flow_type = IP_USER_FLOW;
		break;
	default:
		return 0;
	}

	return 1;
}

static int niu_ethflow_to_class(int flow_type, u64 *class)
{
	switch (flow_type) {
@@ -6916,7 +7005,8 @@ static int niu_ethflow_to_class(int flow_type, u64 *class)
	case UDP_V4_FLOW:
		*class = CLASS_CODE_UDP_IPV4;
		break;
	case AH_ESP_V4_FLOW:
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		*class = CLASS_CODE_AH_ESP_IPV4;
		break;
	case SCTP_V4_FLOW:
@@ -6928,7 +7018,8 @@ static int niu_ethflow_to_class(int flow_type, u64 *class)
	case UDP_V6_FLOW:
		*class = CLASS_CODE_UDP_IPV6;
		break;
	case AH_ESP_V6_FLOW:
	case AH_V6_FLOW:
	case ESP_V6_FLOW:
		*class = CLASS_CODE_AH_ESP_IPV6;
		break;
	case SCTP_V6_FLOW:
@@ -6945,8 +7036,6 @@ static u64 niu_flowkey_to_ethflow(u64 flow_key)
{
	u64 ethflow = 0;

	if (flow_key & FLOW_KEY_PORT)
		ethflow |= RXH_DEV_PORT;
	if (flow_key & FLOW_KEY_L2DA)
		ethflow |= RXH_L2DA;
	if (flow_key & FLOW_KEY_VLAN)
@@ -6970,8 +7059,6 @@ static int niu_ethflow_to_flowkey(u64 ethflow, u64 *flow_key)
{
	u64 key = 0;

	if (ethflow & RXH_DEV_PORT)
		key |= FLOW_KEY_PORT;
	if (ethflow & RXH_L2DA)
		key |= FLOW_KEY_L2DA;
	if (ethflow & RXH_VLAN)
@@ -6993,41 +7080,279 @@ static int niu_ethflow_to_flowkey(u64 ethflow, u64 *flow_key)

}

static int niu_get_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
static int niu_get_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc)
{
	struct niu *np = netdev_priv(dev);
	u64 class;

	cmd->data = 0;
	nfc->data = 0;

	if (!niu_ethflow_to_class(cmd->flow_type, &class))
	if (!niu_ethflow_to_class(nfc->flow_type, &class))
		return -EINVAL;

	if (np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] &
	    TCAM_KEY_DISC)
		cmd->data = RXH_DISCARD;
		nfc->data = RXH_DISCARD;
	else

		cmd->data = niu_flowkey_to_ethflow(np->parent->flow_key[class -
		nfc->data = niu_flowkey_to_ethflow(np->parent->flow_key[class -
						      CLASS_CODE_USER_PROG1]);
	return 0;
}

static int niu_set_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
static void niu_get_ip4fs_from_tcam_key(struct niu_tcam_entry *tp,
					struct ethtool_rx_flow_spec *fsp)
{

	fsp->h_u.tcp_ip4_spec.ip4src = (tp->key[3] & TCAM_V4KEY3_SADDR) >>
		TCAM_V4KEY3_SADDR_SHIFT;
	fsp->h_u.tcp_ip4_spec.ip4dst = (tp->key[3] & TCAM_V4KEY3_DADDR) >>
		TCAM_V4KEY3_DADDR_SHIFT;
	fsp->m_u.tcp_ip4_spec.ip4src = (tp->key_mask[3] & TCAM_V4KEY3_SADDR) >>
		TCAM_V4KEY3_SADDR_SHIFT;
	fsp->m_u.tcp_ip4_spec.ip4dst = (tp->key_mask[3] & TCAM_V4KEY3_DADDR) >>
		TCAM_V4KEY3_DADDR_SHIFT;

	fsp->h_u.tcp_ip4_spec.ip4src =
		cpu_to_be32(fsp->h_u.tcp_ip4_spec.ip4src);
	fsp->m_u.tcp_ip4_spec.ip4src =
		cpu_to_be32(fsp->m_u.tcp_ip4_spec.ip4src);
	fsp->h_u.tcp_ip4_spec.ip4dst =
		cpu_to_be32(fsp->h_u.tcp_ip4_spec.ip4dst);
	fsp->m_u.tcp_ip4_spec.ip4dst =
		cpu_to_be32(fsp->m_u.tcp_ip4_spec.ip4dst);

	fsp->h_u.tcp_ip4_spec.tos = (tp->key[2] & TCAM_V4KEY2_TOS) >>
		TCAM_V4KEY2_TOS_SHIFT;
	fsp->m_u.tcp_ip4_spec.tos = (tp->key_mask[2] & TCAM_V4KEY2_TOS) >>
		TCAM_V4KEY2_TOS_SHIFT;

	switch (fsp->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
		fsp->h_u.tcp_ip4_spec.psrc =
			((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
			 TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16;
		fsp->h_u.tcp_ip4_spec.pdst =
			((tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
			 TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff;
		fsp->m_u.tcp_ip4_spec.psrc =
			((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
			 TCAM_V4KEY2_PORT_SPI_SHIFT) >> 16;
		fsp->m_u.tcp_ip4_spec.pdst =
			((tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
			 TCAM_V4KEY2_PORT_SPI_SHIFT) & 0xffff;

		fsp->h_u.tcp_ip4_spec.psrc =
			cpu_to_be16(fsp->h_u.tcp_ip4_spec.psrc);
		fsp->h_u.tcp_ip4_spec.pdst =
			cpu_to_be16(fsp->h_u.tcp_ip4_spec.pdst);
		fsp->m_u.tcp_ip4_spec.psrc =
			cpu_to_be16(fsp->m_u.tcp_ip4_spec.psrc);
		fsp->m_u.tcp_ip4_spec.pdst =
			cpu_to_be16(fsp->m_u.tcp_ip4_spec.pdst);
		break;
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		fsp->h_u.ah_ip4_spec.spi =
			(tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
			TCAM_V4KEY2_PORT_SPI_SHIFT;
		fsp->m_u.ah_ip4_spec.spi =
			(tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
			TCAM_V4KEY2_PORT_SPI_SHIFT;

		fsp->h_u.ah_ip4_spec.spi =
			cpu_to_be32(fsp->h_u.ah_ip4_spec.spi);
		fsp->m_u.ah_ip4_spec.spi =
			cpu_to_be32(fsp->m_u.ah_ip4_spec.spi);
		break;
	case IP_USER_FLOW:
		fsp->h_u.usr_ip4_spec.l4_4_bytes =
			(tp->key[2] & TCAM_V4KEY2_PORT_SPI) >>
			TCAM_V4KEY2_PORT_SPI_SHIFT;
		fsp->m_u.usr_ip4_spec.l4_4_bytes =
			(tp->key_mask[2] & TCAM_V4KEY2_PORT_SPI) >>
			TCAM_V4KEY2_PORT_SPI_SHIFT;

		fsp->h_u.usr_ip4_spec.l4_4_bytes =
			cpu_to_be32(fsp->h_u.usr_ip4_spec.l4_4_bytes);
		fsp->m_u.usr_ip4_spec.l4_4_bytes =
			cpu_to_be32(fsp->m_u.usr_ip4_spec.l4_4_bytes);

		fsp->h_u.usr_ip4_spec.proto =
			(tp->key[2] & TCAM_V4KEY2_PROTO) >>
			TCAM_V4KEY2_PROTO_SHIFT;
		fsp->m_u.usr_ip4_spec.proto =
			(tp->key_mask[2] & TCAM_V4KEY2_PROTO) >>
			TCAM_V4KEY2_PROTO_SHIFT;

		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
		break;
	default:
		break;
	}
}

static int niu_get_ethtool_tcam_entry(struct niu *np,
				      struct ethtool_rxnfc *nfc)
{
	struct niu_parent *parent = np->parent;
	struct niu_tcam_entry *tp;
	struct ethtool_rx_flow_spec *fsp = &nfc->fs;
	u16 idx;
	u64 class;
	int ret = 0;

	idx = tcam_get_index(np, (u16)nfc->fs.location);

	tp = &parent->tcam[idx];
	if (!tp->valid) {
		pr_info(PFX "niu%d: %s entry [%d] invalid for idx[%d]\n",
		parent->index, np->dev->name, (u16)nfc->fs.location, idx);
		return -EINVAL;
	}

	/* fill the flow spec entry */
	class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >>
		TCAM_V4KEY0_CLASS_CODE_SHIFT;
	ret = niu_class_to_ethflow(class, &fsp->flow_type);

	if (ret < 0) {
		pr_info(PFX "niu%d: %s niu_class_to_ethflow failed\n",
		parent->index, np->dev->name);
		ret = -EINVAL;
		goto out;
	}

	if (fsp->flow_type == AH_V4_FLOW || fsp->flow_type == AH_V6_FLOW) {
		u32 proto = (tp->key[2] & TCAM_V4KEY2_PROTO) >>
			TCAM_V4KEY2_PROTO_SHIFT;
		if (proto == IPPROTO_ESP) {
			if (fsp->flow_type == AH_V4_FLOW)
				fsp->flow_type = ESP_V4_FLOW;
			else
				fsp->flow_type = ESP_V6_FLOW;
		}
	}

	switch (fsp->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		niu_get_ip4fs_from_tcam_key(tp, fsp);
		break;
	case TCP_V6_FLOW:
	case UDP_V6_FLOW:
	case SCTP_V6_FLOW:
	case AH_V6_FLOW:
	case ESP_V6_FLOW:
		/* Not yet implemented */
		ret = -EINVAL;
		break;
	case IP_USER_FLOW:
		niu_get_ip4fs_from_tcam_key(tp, fsp);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	if (ret < 0)
		goto out;

	if (tp->assoc_data & TCAM_ASSOCDATA_DISC)
		fsp->ring_cookie = RX_CLS_FLOW_DISC;
	else
		fsp->ring_cookie = (tp->assoc_data & TCAM_ASSOCDATA_OFFSET) >>
			TCAM_ASSOCDATA_OFFSET_SHIFT;

	/* put the tcam size here */
	nfc->data = tcam_get_size(np);
out:
	return ret;
}

static int niu_get_ethtool_tcam_all(struct niu *np,
				    struct ethtool_rxnfc *nfc,
				    u32 *rule_locs)
{
	struct niu_parent *parent = np->parent;
	struct niu_tcam_entry *tp;
	int i, idx, cnt;
	u16 n_entries;
	unsigned long flags;


	/* put the tcam size here */
	nfc->data = tcam_get_size(np);

	niu_lock_parent(np, flags);
	n_entries = nfc->rule_cnt;
	for (cnt = 0, i = 0; i < nfc->data; i++) {
		idx = tcam_get_index(np, i);
		tp = &parent->tcam[idx];
		if (!tp->valid)
			continue;
		rule_locs[cnt] = i;
		cnt++;
	}
	niu_unlock_parent(np, flags);

	if (n_entries != cnt) {
		/* print warning, this should not happen */
		pr_info(PFX "niu%d: %s In niu_get_ethtool_tcam_all, "
			"n_entries[%d] != cnt[%d]!!!\n\n",
			np->parent->index, np->dev->name, n_entries, cnt);
	}

	return 0;
}

static int niu_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
		       void *rule_locs)
{
	struct niu *np = netdev_priv(dev);
	int ret = 0;

	switch (cmd->cmd) {
	case ETHTOOL_GRXFH:
		ret = niu_get_hash_opts(np, cmd);
		break;
	case ETHTOOL_GRXRINGS:
		cmd->data = np->num_rx_rings;
		break;
	case ETHTOOL_GRXCLSRLCNT:
		cmd->rule_cnt = tcam_get_valid_entry_cnt(np);
		break;
	case ETHTOOL_GRXCLSRULE:
		ret = niu_get_ethtool_tcam_entry(np, cmd);
		break;
	case ETHTOOL_GRXCLSRLALL:
		ret = niu_get_ethtool_tcam_all(np, cmd, (u32 *)rule_locs);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int niu_set_hash_opts(struct niu *np, struct ethtool_rxnfc *nfc)
{
	u64 class;
	u64 flow_key = 0;
	unsigned long flags;

	if (!niu_ethflow_to_class(cmd->flow_type, &class))
	if (!niu_ethflow_to_class(nfc->flow_type, &class))
		return -EINVAL;

	if (class < CLASS_CODE_USER_PROG1 ||
	    class > CLASS_CODE_SCTP_IPV6)
		return -EINVAL;

	if (cmd->data & RXH_DISCARD) {
	if (nfc->data & RXH_DISCARD) {
		niu_lock_parent(np, flags);
		flow_key = np->parent->tcam_key[class -
					       CLASS_CODE_USER_PROG1];
@@ -7052,7 +7377,7 @@ static int niu_set_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
		}
	}

	if (!niu_ethflow_to_flowkey(cmd->data, &flow_key))
	if (!niu_ethflow_to_flowkey(nfc->data, &flow_key))
		return -EINVAL;

	niu_lock_parent(np, flags);
@@ -7063,6 +7388,331 @@ static int niu_set_hash_opts(struct net_device *dev, struct ethtool_rxnfc *cmd)
	return 0;
}

static void niu_get_tcamkey_from_ip4fs(struct ethtool_rx_flow_spec *fsp,
				       struct niu_tcam_entry *tp,
				       int l2_rdc_tab, u64 class)
{
	u8 pid = 0;
	u32 sip, dip, sipm, dipm, spi, spim;
	u16 sport, dport, spm, dpm;

	sip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4src);
	sipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4src);
	dip = be32_to_cpu(fsp->h_u.tcp_ip4_spec.ip4dst);
	dipm = be32_to_cpu(fsp->m_u.tcp_ip4_spec.ip4dst);

	tp->key[0] = class << TCAM_V4KEY0_CLASS_CODE_SHIFT;
	tp->key_mask[0] = TCAM_V4KEY0_CLASS_CODE;
	tp->key[1] = (u64)l2_rdc_tab << TCAM_V4KEY1_L2RDCNUM_SHIFT;
	tp->key_mask[1] = TCAM_V4KEY1_L2RDCNUM;

	tp->key[3] = (u64)sip << TCAM_V4KEY3_SADDR_SHIFT;
	tp->key[3] |= dip;

	tp->key_mask[3] = (u64)sipm << TCAM_V4KEY3_SADDR_SHIFT;
	tp->key_mask[3] |= dipm;

	tp->key[2] |= ((u64)fsp->h_u.tcp_ip4_spec.tos <<
		       TCAM_V4KEY2_TOS_SHIFT);
	tp->key_mask[2] |= ((u64)fsp->m_u.tcp_ip4_spec.tos <<
			    TCAM_V4KEY2_TOS_SHIFT);
	switch (fsp->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
		sport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.psrc);
		spm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.psrc);
		dport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.pdst);
		dpm = be16_to_cpu(fsp->m_u.tcp_ip4_spec.pdst);

		tp->key[2] |= (((u64)sport << 16) | dport);
		tp->key_mask[2] |= (((u64)spm << 16) | dpm);
		niu_ethflow_to_l3proto(fsp->flow_type, &pid);
		break;
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		spi = be32_to_cpu(fsp->h_u.ah_ip4_spec.spi);
		spim = be32_to_cpu(fsp->m_u.ah_ip4_spec.spi);

		tp->key[2] |= spi;
		tp->key_mask[2] |= spim;
		niu_ethflow_to_l3proto(fsp->flow_type, &pid);
		break;
	case IP_USER_FLOW:
		spi = be32_to_cpu(fsp->h_u.usr_ip4_spec.l4_4_bytes);
		spim = be32_to_cpu(fsp->m_u.usr_ip4_spec.l4_4_bytes);

		tp->key[2] |= spi;
		tp->key_mask[2] |= spim;
		pid = fsp->h_u.usr_ip4_spec.proto;
		break;
	default:
		break;
	}

	tp->key[2] |= ((u64)pid << TCAM_V4KEY2_PROTO_SHIFT);
	if (pid) {
		tp->key_mask[2] |= TCAM_V4KEY2_PROTO;
	}
}

static int niu_add_ethtool_tcam_entry(struct niu *np,
				      struct ethtool_rxnfc *nfc)
{
	struct niu_parent *parent = np->parent;
	struct niu_tcam_entry *tp;
	struct ethtool_rx_flow_spec *fsp = &nfc->fs;
	struct niu_rdc_tables *rdc_table = &parent->rdc_group_cfg[np->port];
	int l2_rdc_table = rdc_table->first_table_num;
	u16 idx;
	u64 class;
	unsigned long flags;
	int err, ret;

	ret = 0;

	idx = nfc->fs.location;
	if (idx >= tcam_get_size(np))
		return -EINVAL;

	if (fsp->flow_type == IP_USER_FLOW) {
		int i;
		int add_usr_cls = 0;
		int ipv6 = 0;
		struct ethtool_usrip4_spec *uspec = &fsp->h_u.usr_ip4_spec;
		struct ethtool_usrip4_spec *umask = &fsp->m_u.usr_ip4_spec;

		niu_lock_parent(np, flags);

		for (i = 0; i < NIU_L3_PROG_CLS; i++) {
			if (parent->l3_cls[i]) {
				if (uspec->proto == parent->l3_cls_pid[i]) {
					class = parent->l3_cls[i];
					parent->l3_cls_refcnt[i]++;
					add_usr_cls = 1;
					break;
				}
			} else {
				/* Program new user IP class */
				switch (i) {
				case 0:
					class = CLASS_CODE_USER_PROG1;
					break;
				case 1:
					class = CLASS_CODE_USER_PROG2;
					break;
				case 2:
					class = CLASS_CODE_USER_PROG3;
					break;
				case 3:
					class = CLASS_CODE_USER_PROG4;
					break;
				default:
					break;
				}
				if (uspec->ip_ver == ETH_RX_NFC_IP6)
					ipv6 = 1;
				ret = tcam_user_ip_class_set(np, class, ipv6,
							     uspec->proto,
							     uspec->tos,
							     umask->tos);
				if (ret)
					goto out;

				ret = tcam_user_ip_class_enable(np, class, 1);
				if (ret)
					goto out;
				parent->l3_cls[i] = class;
				parent->l3_cls_pid[i] = uspec->proto;
				parent->l3_cls_refcnt[i]++;
				add_usr_cls = 1;
				break;
			}
		}
		if (!add_usr_cls) {
			pr_info(PFX "niu%d: %s niu_add_ethtool_tcam_entry: "
				"Could not find/insert class for pid %d\n",
				parent->index, np->dev->name, uspec->proto);
			ret = -EINVAL;
			goto out;
		}
		niu_unlock_parent(np, flags);
	} else {
		if (!niu_ethflow_to_class(fsp->flow_type, &class)) {
			return -EINVAL;
		}
	}

	niu_lock_parent(np, flags);

	idx = tcam_get_index(np, idx);
	tp = &parent->tcam[idx];

	memset(tp, 0, sizeof(*tp));

	/* fill in the tcam key and mask */
	switch (fsp->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW:
	case SCTP_V4_FLOW:
	case AH_V4_FLOW:
	case ESP_V4_FLOW:
		niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table, class);
		break;
	case TCP_V6_FLOW:
	case UDP_V6_FLOW:
	case SCTP_V6_FLOW:
	case AH_V6_FLOW:
	case ESP_V6_FLOW:
		/* Not yet implemented */
		pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
			"flow %d for IPv6 not implemented\n\n",
			parent->index, np->dev->name, fsp->flow_type);
		ret = -EINVAL;
		goto out;
	case IP_USER_FLOW:
		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4) {
			niu_get_tcamkey_from_ip4fs(fsp, tp, l2_rdc_table,
						   class);
		} else {
			/* Not yet implemented */
			pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
			"usr flow for IPv6 not implemented\n\n",
			parent->index, np->dev->name);
			ret = -EINVAL;
			goto out;
		}
		break;
	default:
		pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
			"Unknown flow type %d\n\n",
			parent->index, np->dev->name, fsp->flow_type);
		ret = -EINVAL;
		goto out;
	}

	/* fill in the assoc data */
	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
		tp->assoc_data = TCAM_ASSOCDATA_DISC;
	} else {
		if (fsp->ring_cookie >= np->num_rx_rings) {
			pr_info(PFX "niu%d: %s In niu_add_ethtool_tcam_entry: "
				"Invalid RX ring %lld\n\n",
				parent->index, np->dev->name,
				(long long) fsp->ring_cookie);
			ret = -EINVAL;
			goto out;
		}
		tp->assoc_data = (TCAM_ASSOCDATA_TRES_USE_OFFSET |
				  (fsp->ring_cookie <<
				   TCAM_ASSOCDATA_OFFSET_SHIFT));
	}

	err = tcam_write(np, idx, tp->key, tp->key_mask);
	if (err) {
		ret = -EINVAL;
		goto out;
	}
	err = tcam_assoc_write(np, idx, tp->assoc_data);
	if (err) {
		ret = -EINVAL;
		goto out;
	}

	/* validate the entry */
	tp->valid = 1;
	np->clas.tcam_valid_entries++;
out:
	niu_unlock_parent(np, flags);

	return ret;
}

static int niu_del_ethtool_tcam_entry(struct niu *np, u32 loc)
{
	struct niu_parent *parent = np->parent;
	struct niu_tcam_entry *tp;
	u16 idx;
	unsigned long flags;
	u64 class;
	int ret = 0;

	if (loc >= tcam_get_size(np))
		return -EINVAL;

	niu_lock_parent(np, flags);

	idx = tcam_get_index(np, loc);
	tp = &parent->tcam[idx];

	/* if the entry is of a user defined class, then update*/
	class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >>
		TCAM_V4KEY0_CLASS_CODE_SHIFT;

	if (class >= CLASS_CODE_USER_PROG1 && class <= CLASS_CODE_USER_PROG4) {
		int i;
		for (i = 0; i < NIU_L3_PROG_CLS; i++) {
			if (parent->l3_cls[i] == class) {
				parent->l3_cls_refcnt[i]--;
				if (!parent->l3_cls_refcnt[i]) {
					/* disable class */
					ret = tcam_user_ip_class_enable(np,
									class,
									0);
					if (ret)
						goto out;
					parent->l3_cls[i] = 0;
					parent->l3_cls_pid[i] = 0;
				}
				break;
			}
		}
		if (i == NIU_L3_PROG_CLS) {
			pr_info(PFX "niu%d: %s In niu_del_ethtool_tcam_entry,"
				"Usr class 0x%llx not found \n",
				parent->index, np->dev->name,
				(unsigned long long) class);
			ret = -EINVAL;
			goto out;
		}
	}

	ret = tcam_flush(np, idx);
	if (ret)
		goto out;

	/* invalidate the entry */
	tp->valid = 0;
	np->clas.tcam_valid_entries--;
out:
	niu_unlock_parent(np, flags);

	return ret;
}

static int niu_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
	struct niu *np = netdev_priv(dev);
	int ret = 0;

	switch (cmd->cmd) {
	case ETHTOOL_SRXFH:
		ret = niu_set_hash_opts(np, cmd);
		break;
	case ETHTOOL_SRXCLSRLINS:
		ret = niu_add_ethtool_tcam_entry(np, cmd);
		break;
	case ETHTOOL_SRXCLSRLDEL:
		ret = niu_del_ethtool_tcam_entry(np, cmd->fs.location);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static const struct {
	const char string[ETH_GSTRING_LEN];
} niu_xmac_stat_keys[] = {
@@ -7296,8 +7946,8 @@ static const struct ethtool_ops niu_ethtool_ops = {
	.get_stats_count	= niu_get_stats_count,
	.get_ethtool_stats	= niu_get_ethtool_stats,
	.phys_id		= niu_phys_id,
	.get_rxhash		= niu_get_hash_opts,
	.set_rxhash		= niu_set_hash_opts,
	.get_rxnfc		= niu_get_nfc,
	.set_rxnfc		= niu_set_nfc,
};

static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,
@@ -8367,7 +9017,8 @@ static int __devinit niu_classifier_swstate_init(struct niu *np)
	niudbg(PROBE, "niu_classifier_swstate_init: num_tcam(%d)\n",
	       np->parent->tcam_num_entries);

	cp->tcam_index = (u16) np->port;
	cp->tcam_top = (u16) np->port;
	cp->tcam_sz = np->parent->tcam_num_entries / np->parent->num_ports;
	cp->h1_init = 0xffffffff;
	cp->h2_init = 0xffff;

+11 −3
Original line number Diff line number Diff line
@@ -3004,7 +3004,9 @@ struct niu_classifier {
	struct niu_altmac_rdc	alt_mac_mappings[16];
	struct niu_vlan_rdc	vlan_mappings[ENET_VLAN_TBL_NUM_ENTRIES];

	u16			tcam_index;
	u16			tcam_top;
	u16			tcam_sz;
	u16			tcam_valid_entries;
	u16			num_alt_mac_mappings;

	u32			h1_init;
@@ -3040,6 +3042,7 @@ struct phy_probe_info {
};

struct niu_tcam_entry {
	u8			valid;
	u64			key[4];
	u64			key_mask[4];
	u64			assoc_data;
@@ -3107,10 +3110,15 @@ struct niu_parent {
	struct phy_probe_info	phy_probe_info;

	struct niu_tcam_entry	tcam[NIU_TCAM_ENTRIES_MAX];
	u64			l2_cls[2];
	u64			l3_cls[4];

#define	NIU_L2_PROG_CLS		2
#define	NIU_L3_PROG_CLS		4
	u64			l2_cls[NIU_L2_PROG_CLS];
	u64			l3_cls[NIU_L3_PROG_CLS];
	u64			tcam_key[12];
	u64			flow_key[12];
	u16			l3_cls_refcnt[NIU_L3_PROG_CLS];
	u8			l3_cls_pid[NIU_L3_PROG_CLS];
};

struct niu_ops {