diff options
Diffstat (limited to 'drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c')
| -rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 122 |
1 files changed, 87 insertions, 35 deletions
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index c7d2b4dc7568..8524246fd67e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -36,6 +36,7 @@ #include <net/tc_act/tc_mirred.h> #include "cxgb4.h" +#include "cxgb4_filter.h" #include "cxgb4_tc_u32_parse.h" #include "cxgb4_tc_u32.h" @@ -47,7 +48,7 @@ static int fill_match_fields(struct adapter *adap, bool next_header) { unsigned int i, j; - u32 val, mask; + __be32 val, mask; int off, err; bool found; @@ -148,14 +149,16 @@ static int fill_action_fields(struct adapter *adap, int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { const struct cxgb4_match_field *start, *link_start = NULL; + struct netlink_ext_ack *extack = cls->common.extack; struct adapter *adapter = netdev2adap(dev); __be16 protocol = cls->common.protocol; struct ch_filter_specification fs; struct cxgb4_tc_u32_table *t; struct cxgb4_link *link; - unsigned int filter_id; u32 uhtid, link_uhtid; bool is_ipv6 = false; + u8 inet_family; + int filter_id; int ret; if (!can_tc_u32_offload(dev)) @@ -164,14 +167,18 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6)) return -EOPNOTSUPP; - /* Fetch the location to insert the filter. */ - filter_id = cls->knode.handle & 0xFFFFF; + inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET; - if (filter_id > adapter->tids.nftids) { - dev_err(adapter->pdev_dev, - "Location %d out of range for insertion. Max: %d\n", - filter_id, adapter->tids.nftids); - return -ERANGE; + /* Get a free filter entry TID, where we can insert this new + * rule. Only insert rule if its prio doesn't conflict with + * existing rules. + */ + filter_id = cxgb4_get_free_ftid(dev, inet_family, false, + TC_U32_NODE(cls->knode.handle)); + if (filter_id < 0) { + NL_SET_ERR_MSG_MOD(extack, + "No free LETCAM index available"); + return -ENOMEM; } t = adapter->tc_u32; @@ -179,7 +186,7 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) link_uhtid = TC_U32_USERHTID(cls->knode.link_handle); /* Ensure that uhtid is either root u32 (i.e. 0x800) - * or a a valid linked bucket. + * or a valid linked bucket. */ if (uhtid != 0x800 && uhtid >= t->size) return -EINVAL; @@ -190,6 +197,11 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) memset(&fs, 0, sizeof(fs)); + if (filter_id < adapter->tids.nhpftids) + fs.prio = 1; + fs.tc_prio = cls->common.prio; + fs.tc_cookie = cls->knode.handle; + if (protocol == htons(ETH_P_IPV6)) { start = cxgb4_ipv6_fields; is_ipv6 = true; @@ -216,7 +228,7 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) const struct cxgb4_next_header *next; bool found = false; unsigned int i, j; - u32 val, mask; + __be32 val, mask; int off; if (t->table[link_uhtid - 1].link_handle) { @@ -230,10 +242,10 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) /* Try to find matches that allow jumps to next header. */ for (i = 0; next[i].jump; i++) { - if (next[i].offoff != cls->knode.sel->offoff || - next[i].shift != cls->knode.sel->offshift || - next[i].mask != cls->knode.sel->offmask || - next[i].offset != cls->knode.sel->off) + if (next[i].sel.offoff != cls->knode.sel->offoff || + next[i].sel.offshift != cls->knode.sel->offshift || + next[i].sel.offmask != cls->knode.sel->offmask || + next[i].sel.off != cls->knode.sel->off) continue; /* Found a possible candidate. Find a key that @@ -245,9 +257,9 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) val = cls->knode.sel->keys[j].val; mask = cls->knode.sel->keys[j].mask; - if (next[i].match_off == off && - next[i].match_val == val && - next[i].match_mask == mask) { + if (next[i].key.off == off && + next[i].key.val == val && + next[i].key.mask == mask) { found = true; break; } @@ -343,28 +355,74 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) unsigned int filter_id, max_tids, i, j; struct cxgb4_link *link = NULL; struct cxgb4_tc_u32_table *t; + struct filter_entry *f; + bool found = false; u32 handle, uhtid; + u8 nslots; int ret; if (!can_tc_u32_offload(dev)) return -EOPNOTSUPP; /* Fetch the location to delete the filter. */ - filter_id = cls->knode.handle & 0xFFFFF; + max_tids = adapter->tids.nhpftids + adapter->tids.nftids; + + spin_lock_bh(&adapter->tids.ftid_lock); + filter_id = 0; + while (filter_id < max_tids) { + if (filter_id < adapter->tids.nhpftids) { + i = filter_id; + f = &adapter->tids.hpftid_tab[i]; + if (f->valid && f->fs.tc_cookie == cls->knode.handle) { + found = true; + break; + } - if (filter_id > adapter->tids.nftids) { - dev_err(adapter->pdev_dev, - "Location %d out of range for deletion. Max: %d\n", - filter_id, adapter->tids.nftids); - return -ERANGE; + i = find_next_bit(adapter->tids.hpftid_bmap, + adapter->tids.nhpftids, i + 1); + if (i >= adapter->tids.nhpftids) { + filter_id = adapter->tids.nhpftids; + continue; + } + + filter_id = i; + } else { + i = filter_id - adapter->tids.nhpftids; + f = &adapter->tids.ftid_tab[i]; + if (f->valid && f->fs.tc_cookie == cls->knode.handle) { + found = true; + break; + } + + i = find_next_bit(adapter->tids.ftid_bmap, + adapter->tids.nftids, i + 1); + if (i >= adapter->tids.nftids) + break; + + filter_id = i + adapter->tids.nhpftids; + } + + nslots = 0; + if (f->fs.type) { + nslots++; + if (CHELSIO_CHIP_VERSION(adapter->params.chip) < + CHELSIO_T6) + nslots += 2; + } + + filter_id += nslots; } + spin_unlock_bh(&adapter->tids.ftid_lock); + + if (!found) + return -ERANGE; t = adapter->tc_u32; handle = cls->knode.handle; uhtid = TC_U32_USERHTID(cls->knode.handle); /* Ensure that uhtid is either root u32 (i.e. 0x800) - * or a a valid linked bucket. + * or a valid linked bucket. */ if (uhtid != 0x800 && uhtid >= t->size) return -EINVAL; @@ -389,7 +447,6 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) /* If a link is being deleted, then delete all filters * associated with the link. */ - max_tids = adapter->tids.nftids; for (i = 0; i < t->size; i++) { link = &t->table[i]; @@ -437,15 +494,14 @@ void cxgb4_cleanup_tc_u32(struct adapter *adap) struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap) { - unsigned int max_tids = adap->tids.nftids; + unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids; struct cxgb4_tc_u32_table *t; unsigned int i; if (!max_tids) return NULL; - t = kvzalloc(sizeof(*t) + - (max_tids * sizeof(struct cxgb4_link)), GFP_KERNEL); + t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL); if (!t) return NULL; @@ -468,13 +524,9 @@ struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap) out_no_mem: for (i = 0; i < t->size; i++) { struct cxgb4_link *link = &t->table[i]; - - if (link->tid_map) - kvfree(link->tid_map); + kvfree(link->tid_map); } - - if (t) - kvfree(t); + kvfree(t); return NULL; } |
