diff options
Diffstat (limited to 'drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c')
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 120 |
1 files changed, 96 insertions, 24 deletions
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 43b0f8c57da7..1d39fca11810 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -440,36 +440,48 @@ int cxgb4_get_free_ftid(struct net_device *dev, int family) { struct adapter *adap = netdev2adap(dev); struct tid_info *t = &adap->tids; + bool found = false; + u8 i, n, cnt; int ftid; - spin_lock_bh(&t->ftid_lock); - if (family == PF_INET) { - ftid = find_first_zero_bit(t->ftid_bmap, t->nftids); - if (ftid >= t->nftids) - ftid = -1; - } else { - if (is_t6(adap->params.chip)) { - ftid = bitmap_find_free_region(t->ftid_bmap, - t->nftids, 1); - if (ftid < 0) - goto out_unlock; - - /* this is only a lookup, keep the found region - * unallocated - */ - bitmap_release_region(t->ftid_bmap, ftid, 1); - } else { - ftid = bitmap_find_free_region(t->ftid_bmap, - t->nftids, 2); - if (ftid < 0) - goto out_unlock; + /* IPv4 occupy 1 slot. IPv6 occupy 2 slots on T6 and 4 slots + * on T5. + */ + n = 1; + if (family == PF_INET6) { + n++; + if (CHELSIO_CHIP_VERSION(adap->params.chip) < CHELSIO_T6) + n += 2; + } + + if (n > t->nftids) + return -ENOMEM; - bitmap_release_region(t->ftid_bmap, ftid, 2); + /* Find free filter slots from the end of TCAM. Appropriate + * checks must be done by caller later to ensure the prio + * passed by TC doesn't conflict with prio saved by existing + * rules in the TCAM. + */ + spin_lock_bh(&t->ftid_lock); + ftid = t->nftids - 1; + while (ftid >= n - 1) { + cnt = 0; + for (i = 0; i < n; i++) { + if (test_bit(ftid - i, t->ftid_bmap)) + break; + cnt++; } + if (cnt == n) { + ftid &= ~(n - 1); + found = true; + break; + } + + ftid -= n; } -out_unlock: spin_unlock_bh(&t->ftid_lock); - return ftid; + + return found ? ftid : -ENOMEM; } static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family, @@ -510,6 +522,60 @@ static void cxgb4_clear_ftid(struct tid_info *t, int fidx, int family, spin_unlock_bh(&t->ftid_lock); } +bool cxgb4_filter_prio_in_range(struct net_device *dev, u32 idx, u32 prio) +{ + struct adapter *adap = netdev2adap(dev); + struct filter_entry *prev_fe, *next_fe; + struct tid_info *t = &adap->tids; + u32 prev_ftid, next_ftid; + bool valid = true; + + /* Only insert the rule if both of the following conditions + * are met: + * 1. The immediate previous rule has priority <= @prio. + * 2. The immediate next rule has priority >= @prio. + */ + spin_lock_bh(&t->ftid_lock); + /* Don't insert if there's a rule already present at @idx. */ + if (test_bit(idx, t->ftid_bmap)) { + valid = false; + goto out_unlock; + } + + next_ftid = find_next_bit(t->ftid_bmap, t->nftids, idx); + if (next_ftid >= t->nftids) + next_ftid = idx; + + next_fe = &adap->tids.ftid_tab[next_ftid]; + + prev_ftid = find_last_bit(t->ftid_bmap, idx); + if (prev_ftid >= idx) + prev_ftid = idx; + + /* See if the filter entry belongs to an IPv6 rule, which + * occupy 4 slots on T5 and 2 slots on T6. Adjust the + * reference to the previously inserted filter entry + * accordingly. + */ + if (CHELSIO_CHIP_VERSION(adap->params.chip) < CHELSIO_T6) { + prev_fe = &adap->tids.ftid_tab[prev_ftid & ~0x3]; + if (!prev_fe->fs.type) + prev_fe = &adap->tids.ftid_tab[prev_ftid]; + } else { + prev_fe = &adap->tids.ftid_tab[prev_ftid & ~0x1]; + if (!prev_fe->fs.type) + prev_fe = &adap->tids.ftid_tab[prev_ftid]; + } + + if ((prev_fe->valid && prio < prev_fe->fs.tc_prio) || + (next_fe->valid && prio > next_fe->fs.tc_prio)) + valid = false; + +out_unlock: + spin_unlock_bh(&t->ftid_lock); + return valid; +} + /* Delete the filter at a specified index. */ static int del_filter_wr(struct adapter *adapter, int fidx) { @@ -806,6 +872,12 @@ static void fill_default_mask(struct ch_filter_specification *fs) fs->mask.tos |= ~0; if (fs->val.proto && !fs->mask.proto) fs->mask.proto |= ~0; + if (fs->val.pfvf_vld && !fs->mask.pfvf_vld) + fs->mask.pfvf_vld |= ~0; + if (fs->val.pf && !fs->mask.pf) + fs->mask.pf |= ~0; + if (fs->val.vf && !fs->mask.vf) + fs->mask.vf |= ~0; for (i = 0; i < ARRAY_SIZE(fs->val.lip); i++) { lip |= fs->val.lip[i]; |