gianfar: Basic Support for programming hash rules

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

Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Sandeep Gopalpet 2009-11-02 07:03:40 +00:00 committed by David S. Miller
parent 46ceb60ca8
commit 7a8b3372e2
4 changed files with 404 additions and 0 deletions

View file

@ -431,6 +431,9 @@ static const struct net_device_ops gfar_netdev_ops = {
#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)
{
int i = 0x0;
@ -766,6 +769,73 @@ static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
}
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,
* and anything else we need before we start */
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';
}
/* Initialize the filer table */
gfar_init_filer_table(priv);
/* Create all the sysfs files */
gfar_init_sysfs(dev);

View file

@ -381,6 +381,84 @@ extern const char gfar_driver_version[];
#define BD_LFLAG(flags) ((flags) << 16)
#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 */
#define TXBD_READY 0x8000
#define TXBD_PADCRC 0x4000
@ -959,6 +1037,8 @@ struct gfar_private {
unsigned int rx_stash_size;
unsigned int rx_stash_index;
u32 cur_filer_idx;
struct sk_buff_head rx_recycle;
struct vlan_group *vlgrp;
@ -1002,6 +1082,9 @@ struct gfar_private {
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)
{
u32 val;
@ -1014,6 +1097,16 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 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_tx_qs(struct gfar_private *priv);
extern void unlock_rx_qs(struct gfar_private *priv);

View file

@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
#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 = {
.get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_wol = gfar_get_wol,
.set_wol = gfar_set_wol,
#endif
.set_rxnfc = gfar_set_nfc,
};

View file

@ -674,6 +674,8 @@ struct ethtool_ops {
#define AH_V6_FLOW 0x0b
#define ESP_V6_FLOW 0x0c
#define IP_USER_FLOW 0x0d
#define IPV4_FLOW 0x10
#define IPV6_FLOW 0x11
/* L3-L4 network traffic flow hash options */
#define RXH_L2DA (1 << 1)