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

Commit 7a8b3372 authored by Sandeep Gopalpet's avatar Sandeep Gopalpet Committed by David S. Miller
Browse files

gianfar: Basic Support for programming hash rules



This patch provides basic hash rules programming via the ethtool
interface.

Signed-off-by: default avatarSandeep Gopalpet <Sandeep.Kumar@freescale.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 46ceb60c
Loading
Loading
Loading
Loading
+73 −0
Original line number Original line Diff line number Diff line
@@ -431,6 +431,9 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
#endif
};
};


unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];

void lock_rx_qs(struct gfar_private *priv)
void lock_rx_qs(struct gfar_private *priv)
{
{
	int i = 0x0;
	int i = 0x0;
@@ -766,6 +769,73 @@ static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
	}
	}
	return new_bit_map;
	return new_bit_map;
}
}

u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar, u32 class)
{
	u32 rqfpr = FPR_FILER_MASK;
	u32 rqfcr = 0x0;

	rqfar--;
	rqfcr = RQFCR_CLE | RQFCR_PID_MASK | RQFCR_CMP_EXACT;
	ftp_rqfpr[rqfar] = rqfpr;
	ftp_rqfcr[rqfar] = rqfcr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	rqfar--;
	rqfcr = RQFCR_CMP_NOMATCH;
	ftp_rqfpr[rqfar] = rqfpr;
	ftp_rqfcr[rqfar] = rqfcr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	rqfar--;
	rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND;
	rqfpr = class;
	ftp_rqfcr[rqfar] = rqfcr;
	ftp_rqfpr[rqfar] = rqfpr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	rqfar--;
	rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_MASK | RQFCR_AND;
	rqfpr = class;
	ftp_rqfcr[rqfar] = rqfcr;
	ftp_rqfpr[rqfar] = rqfpr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	return rqfar;
}

static void gfar_init_filer_table(struct gfar_private *priv)
{
	int i = 0x0;
	u32 rqfar = MAX_FILER_IDX;
	u32 rqfcr = 0x0;
	u32 rqfpr = FPR_FILER_MASK;

	/* Default rule */
	rqfcr = RQFCR_CMP_MATCH;
	ftp_rqfcr[rqfar] = rqfcr;
	ftp_rqfpr[rqfar] = rqfpr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP);

	/* cur_filer_idx indicated the fisrt non-masked rule */
	priv->cur_filer_idx = rqfar;

	/* Rest are masked rules */
	rqfcr = RQFCR_CMP_NOMATCH;
	for (i = 0; i < rqfar; i++) {
		ftp_rqfcr[i] = rqfcr;
		ftp_rqfpr[i] = rqfpr;
		gfar_write_filer(priv, i, rqfcr, rqfpr);
	}
}

/* Set up the ethernet device structure, private data,
/* Set up the ethernet device structure, private data,
 * and anything else we need before we start */
 * and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
static int gfar_probe(struct of_device *ofdev,
@@ -1005,6 +1075,9 @@ static int gfar_probe(struct of_device *ofdev,
			priv->gfargrp[i].int_name_tx[len_devname] = '\0';
			priv->gfargrp[i].int_name_tx[len_devname] = '\0';
	}
	}


	/* Initialize the filer table */
	gfar_init_filer_table(priv);

	/* Create all the sysfs files */
	/* Create all the sysfs files */
	gfar_init_sysfs(dev);
	gfar_init_sysfs(dev);


+93 −0
Original line number Original line Diff line number Diff line
@@ -381,6 +381,84 @@ extern const char gfar_driver_version[];
#define BD_LFLAG(flags) ((flags) << 16)
#define BD_LFLAG(flags) ((flags) << 16)
#define BD_LENGTH_MASK		0x0000ffff
#define BD_LENGTH_MASK		0x0000ffff


#define CLASS_CODE_UNRECOG		0x00
#define CLASS_CODE_DUMMY1		0x01
#define CLASS_CODE_ETHERTYPE1		0x02
#define CLASS_CODE_ETHERTYPE2		0x03
#define CLASS_CODE_USER_PROG1		0x04
#define CLASS_CODE_USER_PROG2		0x05
#define CLASS_CODE_USER_PROG3		0x06
#define CLASS_CODE_USER_PROG4		0x07
#define CLASS_CODE_TCP_IPV4		0x08
#define CLASS_CODE_UDP_IPV4		0x09
#define CLASS_CODE_AH_ESP_IPV4		0x0a
#define CLASS_CODE_SCTP_IPV4		0x0b
#define CLASS_CODE_TCP_IPV6		0x0c
#define CLASS_CODE_UDP_IPV6		0x0d
#define CLASS_CODE_AH_ESP_IPV6		0x0e
#define CLASS_CODE_SCTP_IPV6		0x0f

#define FPR_FILER_MASK	0xFFFFFFFF
#define MAX_FILER_IDX	0xFF

/* RQFCR register bits */
#define RQFCR_GPI		0x80000000
#define RQFCR_HASHTBL_Q		0x00000000
#define RQFCR_HASHTBL_0		0x00020000
#define RQFCR_HASHTBL_1		0x00040000
#define RQFCR_HASHTBL_2		0x00060000
#define RQFCR_HASHTBL_3		0x00080000
#define RQFCR_HASH		0x00010000
#define RQFCR_CLE		0x00000200
#define RQFCR_RJE		0x00000100
#define RQFCR_AND		0x00000080
#define RQFCR_CMP_EXACT		0x00000000
#define RQFCR_CMP_MATCH		0x00000020
#define RQFCR_CMP_NOEXACT	0x00000040
#define RQFCR_CMP_NOMATCH	0x00000060

/* RQFCR PID values */
#define	RQFCR_PID_MASK		0x00000000
#define	RQFCR_PID_PARSE		0x00000001
#define	RQFCR_PID_ARB		0x00000002
#define	RQFCR_PID_DAH		0x00000003
#define	RQFCR_PID_DAL		0x00000004
#define	RQFCR_PID_SAH		0x00000005
#define	RQFCR_PID_SAL		0x00000006
#define	RQFCR_PID_ETY		0x00000007
#define	RQFCR_PID_VID		0x00000008
#define	RQFCR_PID_PRI		0x00000009
#define	RQFCR_PID_TOS		0x0000000A
#define	RQFCR_PID_L4P		0x0000000B
#define	RQFCR_PID_DIA		0x0000000C
#define	RQFCR_PID_SIA		0x0000000D
#define	RQFCR_PID_DPT		0x0000000E
#define	RQFCR_PID_SPT		0x0000000F

/* RQFPR when PID is 0x0001 */
#define RQFPR_HDR_GE_512	0x00200000
#define RQFPR_LERR		0x00100000
#define RQFPR_RAR		0x00080000
#define RQFPR_RARQ		0x00040000
#define RQFPR_AR		0x00020000
#define RQFPR_ARQ		0x00010000
#define RQFPR_EBC		0x00008000
#define RQFPR_VLN		0x00004000
#define RQFPR_CFI		0x00002000
#define RQFPR_JUM		0x00001000
#define RQFPR_IPF		0x00000800
#define RQFPR_FIF		0x00000400
#define RQFPR_IPV4		0x00000200
#define RQFPR_IPV6		0x00000100
#define RQFPR_ICC		0x00000080
#define RQFPR_ICV		0x00000040
#define RQFPR_TCP		0x00000020
#define RQFPR_UDP		0x00000010
#define RQFPR_TUC		0x00000008
#define RQFPR_TUV		0x00000004
#define RQFPR_PER		0x00000002
#define RQFPR_EER		0x00000001

/* TxBD status field bits */
/* TxBD status field bits */
#define TXBD_READY		0x8000
#define TXBD_READY		0x8000
#define TXBD_PADCRC		0x4000
#define TXBD_PADCRC		0x4000
@@ -959,6 +1037,8 @@ struct gfar_private {
	unsigned int rx_stash_size;
	unsigned int rx_stash_size;
	unsigned int rx_stash_index;
	unsigned int rx_stash_index;


	u32 cur_filer_idx;

	struct sk_buff_head rx_recycle;
	struct sk_buff_head rx_recycle;


	struct vlan_group *vlgrp;
	struct vlan_group *vlgrp;
@@ -1002,6 +1082,9 @@ struct gfar_private {
	struct gfar_extra_stats extra_stats;
	struct gfar_extra_stats extra_stats;
};
};


extern unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
extern unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];

static inline u32 gfar_read(volatile unsigned __iomem *addr)
static inline u32 gfar_read(volatile unsigned __iomem *addr)
{
{
	u32 val;
	u32 val;
@@ -1014,6 +1097,16 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
	out_be32(addr, val);
	out_be32(addr, val);
}
}


static inline void gfar_write_filer(struct gfar_private *priv,
		unsigned int far, unsigned int fcr, unsigned int fpr)
{
	struct gfar __iomem *regs = priv->gfargrp[0].regs;

	gfar_write(&regs->rqfar, far);
	gfar_write(&regs->rqfcr, fcr);
	gfar_write(&regs->rqfpr, fpr);
}

extern void lock_rx_qs(struct gfar_private *priv);
extern void lock_rx_qs(struct gfar_private *priv);
extern void lock_tx_qs(struct gfar_private *priv);
extern void lock_tx_qs(struct gfar_private *priv);
extern void unlock_rx_qs(struct gfar_private *priv);
extern void unlock_rx_qs(struct gfar_private *priv);
+236 −0
Original line number Original line Diff line number Diff line
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
}
#endif
#endif


static int gfar_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_V4_FLOW:
	case 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_V6_FLOW:
	case ESP_V6_FLOW:
		*class = CLASS_CODE_AH_ESP_IPV6;
		break;
	case SCTP_V6_FLOW:
		*class = CLASS_CODE_SCTP_IPV6;
		break;
	default:
		return 0;
	}

	return 1;
}

static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
{
	u32 fcr = 0x0, fpr = FPR_FILER_MASK;

	if (ethflow & RXH_L2DA) {
		fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
			RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;

		fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
				RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & RXH_VLAN) {
		fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
				RQFCR_AND | RQFCR_HASHTBL_0;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & RXH_IP_SRC) {
		fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
			RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & (RXH_IP_DST)) {
		fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
			RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & RXH_L3_PROTO) {
		fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
			RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & RXH_L4_B_0_1) {
		fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
			RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	if (ethflow & RXH_L4_B_2_3) {
		fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
			RQFCR_AND | RQFCR_HASHTBL_0;
		ftp_rqfpr[priv->cur_filer_idx] = fpr;
		ftp_rqfcr[priv->cur_filer_idx] = fcr;
		gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}
}

static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
{
	unsigned int last_rule_idx = priv->cur_filer_idx;
	unsigned int cmp_rqfpr;
	unsigned int local_rqfpr[MAX_FILER_IDX + 1];
	unsigned int local_rqfcr[MAX_FILER_IDX + 1];
	int i = 0x0, k = 0x0;
	int j = MAX_FILER_IDX, l = 0x0;

	switch (class) {
	case TCP_V4_FLOW:
		cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
		break;
	case UDP_V4_FLOW:
		cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
		break;
	case TCP_V6_FLOW:
		cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
		break;
	case UDP_V6_FLOW:
		cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
		break;
	case IPV4_FLOW:
		cmp_rqfpr = RQFPR_IPV4;
	case IPV6_FLOW:
		cmp_rqfpr = RQFPR_IPV6;
		break;
	default:
		printk(KERN_ERR "Right now this class is not supported\n");
		return 0;
	}

	for (i = 0; i < MAX_FILER_IDX + 1; i++) {
		local_rqfpr[j] = ftp_rqfpr[i];
		local_rqfcr[j] = ftp_rqfcr[i];
		j--;
		if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
			RQFCR_CLE |RQFCR_AND)) &&
			(ftp_rqfpr[i] == cmp_rqfpr))
			break;
	}

	if (i == MAX_FILER_IDX + 1) {
		printk(KERN_ERR "No parse rule found, ");
		printk(KERN_ERR "can't create hash rules\n");
		return 0;
	}

	/* If a match was found, then it begins the starting of a cluster rule
	 * if it was already programmed, we need to overwrite these rules
	 */
	for (l = i+1; l < MAX_FILER_IDX; l++) {
		if ((ftp_rqfcr[l] & RQFCR_CLE) &&
			!(ftp_rqfcr[l] & RQFCR_AND)) {
			ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
				RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
			ftp_rqfpr[l] = FPR_FILER_MASK;
			gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
			break;
		}

		if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
			continue;
		else {
			local_rqfpr[j] = ftp_rqfpr[l];
			local_rqfcr[j] = ftp_rqfcr[l];
			j--;
		}
	}

	priv->cur_filer_idx = l - 1;
	last_rule_idx = l;

	/* hash rules */
	ethflow_to_filer_rules(priv, ethflow);

	/* Write back the popped out rules again */
	for (k = j+1; k < MAX_FILER_IDX; k++) {
		ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
		ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
		gfar_write_filer(priv, priv->cur_filer_idx,
				local_rqfcr[k], local_rqfpr[k]);
		if (!priv->cur_filer_idx)
			break;
		priv->cur_filer_idx = priv->cur_filer_idx - 1;
	}

	return 1;
}

static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
{
	u64 class;

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

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

	/* write the filer rules here */
	if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
		return -1;

	return 0;
}

static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
	struct gfar_private *priv = netdev_priv(dev);
	int ret = 0;

	switch(cmd->cmd) {
	case ETHTOOL_SRXFH:
		ret = gfar_set_hash_opts(priv, cmd);
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}

const struct ethtool_ops gfar_ethtool_ops = {
const struct ethtool_ops gfar_ethtool_ops = {
	.get_settings = gfar_gsettings,
	.get_settings = gfar_gsettings,
	.set_settings = gfar_ssettings,
	.set_settings = gfar_ssettings,
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
	.get_wol = gfar_get_wol,
	.get_wol = gfar_get_wol,
	.set_wol = gfar_set_wol,
	.set_wol = gfar_set_wol,
#endif
#endif
	.set_rxnfc = gfar_set_nfc,
};
};
+2 −0
Original line number Original line Diff line number Diff line
@@ -674,6 +674,8 @@ struct ethtool_ops {
#define	AH_V6_FLOW	0x0b
#define	AH_V6_FLOW	0x0b
#define	ESP_V6_FLOW	0x0c
#define	ESP_V6_FLOW	0x0c
#define	IP_USER_FLOW	0x0d
#define	IP_USER_FLOW	0x0d
#define IPV4_FLOW       0x10
#define IPV6_FLOW       0x11


/* L3-L4 network traffic flow hash options */
/* L3-L4 network traffic flow hash options */
#define	RXH_L2DA	(1 << 1)
#define	RXH_L2DA	(1 << 1)