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

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

niu: Add support for rx flow hash configuration.



Implemented ethtool callback functions for configuring receive flow
hashing in the niu driver.

Signed-off-by: default avatarSantwona Behera <santwona.behera@sun.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0853ad66
Loading
Loading
Loading
Loading
+158 −0
Original line number Original line Diff line number Diff line
@@ -6385,6 +6385,162 @@ static int niu_get_eeprom(struct net_device *dev,
	return 0;
	return 0;
}
}


static int niu_ethflow_to_class(int flow_type, u64 *class)
{
	switch (flow_type) {
	case TCP_V4_FLOW:
		*class = CLASS_CODE_TCP_IPV4;
		break;
	case UDP_V4_FLOW:
		*class = CLASS_CODE_UDP_IPV4;
		break;
	case AH_ESP_V4_FLOW:
		*class = CLASS_CODE_AH_ESP_IPV4;
		break;
	case SCTP_V4_FLOW:
		*class = CLASS_CODE_SCTP_IPV4;
		break;
	case TCP_V6_FLOW:
		*class = CLASS_CODE_TCP_IPV6;
		break;
	case UDP_V6_FLOW:
		*class = CLASS_CODE_UDP_IPV6;
		break;
	case AH_ESP_V6_FLOW:
		*class = CLASS_CODE_AH_ESP_IPV6;
		break;
	case SCTP_V6_FLOW:
		*class = CLASS_CODE_SCTP_IPV6;
		break;
	default:
		return -1;
	}

	return 1;
}

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)
		ethflow |= RXH_VLAN;
	if (flow_key & FLOW_KEY_IPSA)
		ethflow |= RXH_IP_SRC;
	if (flow_key & FLOW_KEY_IPDA)
		ethflow |= RXH_IP_DST;
	if (flow_key & FLOW_KEY_PROTO)
		ethflow |= RXH_L3_PROTO;
	if (flow_key & (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_0_SHIFT))
		ethflow |= RXH_L4_B_0_1;
	if (flow_key & (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_1_SHIFT))
		ethflow |= RXH_L4_B_2_3;

	return ethflow;

}

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)
		key |= FLOW_KEY_VLAN;
	if (ethflow & RXH_IP_SRC)
		key |= FLOW_KEY_IPSA;
	if (ethflow & RXH_IP_DST)
		key |= FLOW_KEY_IPDA;
	if (ethflow & RXH_L3_PROTO)
		key |= FLOW_KEY_PROTO;
	if (ethflow & RXH_L4_B_0_1)
		key |= (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_0_SHIFT);
	if (ethflow & RXH_L4_B_2_3)
		key |= (FLOW_KEY_L4_BYTE12 << FLOW_KEY_L4_1_SHIFT);

	*flow_key = key;

	return 1;

}

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

	cmd->data = 0;

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

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

		cmd->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)
{
	struct niu *np = netdev_priv(dev);
	u64 class;
	u64 flow_key = 0;
	unsigned long flags;

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

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

	if (cmd->data & RXH_DISCARD) {
		niu_lock_parent(np, flags);
		flow_key = np->parent->tcam_key[class -
					       CLASS_CODE_USER_PROG1];
		flow_key |= TCAM_KEY_DISC;
		nw64(TCAM_KEY(class - CLASS_CODE_USER_PROG1), flow_key);
		np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] = flow_key;
		niu_unlock_parent(np, flags);
		return 0;
	} else {
		/* Discard was set before, but is not set now */
		if (np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] &
		    TCAM_KEY_DISC) {
			niu_lock_parent(np, flags);
			flow_key = np->parent->tcam_key[class -
					       CLASS_CODE_USER_PROG1];
			flow_key &= ~TCAM_KEY_DISC;
			nw64(TCAM_KEY(class - CLASS_CODE_USER_PROG1),
			     flow_key);
			np->parent->tcam_key[class - CLASS_CODE_USER_PROG1] =
				flow_key;
			niu_unlock_parent(np, flags);
		}
	}

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

	niu_lock_parent(np, flags);
	nw64(FLOW_KEY(class - CLASS_CODE_USER_PROG1), flow_key);
	np->parent->flow_key[class - CLASS_CODE_USER_PROG1] = flow_key;
	niu_unlock_parent(np, flags);

	return 0;
}

static const struct {
static const struct {
	const char string[ETH_GSTRING_LEN];
	const char string[ETH_GSTRING_LEN];
} niu_xmac_stat_keys[] = {
} niu_xmac_stat_keys[] = {
@@ -6615,6 +6771,8 @@ static const struct ethtool_ops niu_ethtool_ops = {
	.get_stats_count	= niu_get_stats_count,
	.get_stats_count	= niu_get_stats_count,
	.get_ethtool_stats	= niu_get_ethtool_stats,
	.get_ethtool_stats	= niu_get_ethtool_stats,
	.phys_id		= niu_phys_id,
	.phys_id		= niu_phys_id,
	.get_rxhash		= niu_get_hash_opts,
	.set_rxhash		= niu_set_hash_opts,
};
};


static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,
static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent,