niu: Add TCAM classification configuration

Signed-off-by: Santwona Behera <santwona.behera@sun.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Santwona Behera 2009-02-20 00:58:45 -08:00 committed by David S. Miller
parent 59089d8d16
commit 2d96cf8cdf
2 changed files with 686 additions and 27 deletions

View File

@ -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;

View File

@ -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 {