mirror of https://gitee.com/openkylin/linux.git
Merge branch 'cxgb4-add-TC-MATCHALL-classifier-offload'
Rahul Lakkireddy says: ==================== cxgb4: add TC-MATCHALL classifier offload This series of patches add support to offload TC-MATCHALL classifier to hardware to classify all outgoing and incoming traffic on the underlying port. Only 1 egress and 1 ingress rule each can be offloaded on the underlying port. Patch 1 adds support for TC-MATCHALL classifier offload on the egress side. TC-POLICE is the only action that can be offloaded on the egress side and is used to rate limit all outgoing traffic to specified max rate. Patch 2 adds logic to reject the current rule offload if its priority conflicts with existing rules in the TCAM. Patch 3 adds support for TC-MATCHALL classifier offload on the ingress side. The same set of actions supported by existing TC-FLOWER classifier offload can be applied on all the incoming traffic. v5: - Fixed commit message and comment to include comparison for equal priority in patch 2. v4: - Removed check in patch 1 to reject police offload if prio is not 1. - Moved TC_SETUP_BLOCK code to separate function in patch 1. - Added logic to ensure the prio passed by TC doesn't conflict with other rules in TCAM in patch 2. - Higher index has lower priority than lower index in TCAM. So, rework cxgb4_get_free_ftid() to search free index from end of TCAM in descending order in patch 2. - Added check to ensure the matchall rule's prio doesn't conflict with other rules in TCAM in patch 3. - Added logic to fill default mask for VIID, if none has been provided, to prevent conflict with duplicate VIID rules in patch 3. - Used existing variables in private structure to fill VIID info, instead of extracting the info manually in patch 3. v3: - Added check in patch 1 to reject police offload if prio is not 1. - Assign block_shared variable only for TC_SETUP_BLOCK in patch 1. v2: - Added check to reject flow block sharing for policers in patch 1. - Removed logic to fetch free index from end of TCAM in patch 2. Must maintain the same ordering as in kernel. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
07def46382
|
@ -8,7 +8,8 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
|
|||
cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o \
|
||||
cxgb4_uld.o srq.o sched.o cxgb4_filter.o cxgb4_tc_u32.o \
|
||||
cxgb4_ptp.o cxgb4_tc_flower.o cxgb4_cudbg.o cxgb4_mps.o \
|
||||
cudbg_common.o cudbg_lib.o cudbg_zlib.o cxgb4_tc_mqprio.o
|
||||
cudbg_common.o cudbg_lib.o cudbg_zlib.o cxgb4_tc_mqprio.o \
|
||||
cxgb4_tc_matchall.o
|
||||
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
|
||||
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
|
||||
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
|
||||
|
|
|
@ -603,6 +603,8 @@ struct port_info {
|
|||
u8 vivld;
|
||||
u8 smt_idx;
|
||||
u8 rx_cchan;
|
||||
|
||||
bool tc_block_shared;
|
||||
};
|
||||
|
||||
struct dentry;
|
||||
|
@ -1101,6 +1103,9 @@ struct adapter {
|
|||
|
||||
/* TC MQPRIO offload */
|
||||
struct cxgb4_tc_mqprio *tc_mqprio;
|
||||
|
||||
/* TC MATCHALL classifier offload */
|
||||
struct cxgb4_tc_matchall *tc_matchall;
|
||||
};
|
||||
|
||||
/* Support for "sched-class" command to allow a TX Scheduling Class to be
|
||||
|
@ -1130,6 +1135,7 @@ enum {
|
|||
|
||||
enum {
|
||||
SCHED_CLASS_LEVEL_CL_RL = 0, /* class rate limiter */
|
||||
SCHED_CLASS_LEVEL_CH_RL = 2, /* channel rate limiter */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1280,8 +1286,11 @@ struct ch_filter_specification {
|
|||
u16 nat_lport; /* local port to use after NAT'ing */
|
||||
u16 nat_fport; /* foreign port to use after NAT'ing */
|
||||
|
||||
u32 tc_prio; /* TC's filter priority index */
|
||||
u64 tc_cookie; /* Unique cookie identifying TC rules */
|
||||
|
||||
/* reservation for future additions */
|
||||
u8 rsvd[24];
|
||||
u8 rsvd[12];
|
||||
|
||||
/* Filter rule value/mask pairs.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
bitmap_release_region(t->ftid_bmap, ftid, 2);
|
||||
}
|
||||
/* 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;
|
||||
|
||||
/* 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];
|
||||
|
|
|
@ -53,4 +53,5 @@ void clear_all_filters(struct adapter *adapter);
|
|||
void init_hash_filter(struct adapter *adap);
|
||||
bool is_filter_exact_match(struct adapter *adap,
|
||||
struct ch_filter_specification *fs);
|
||||
bool cxgb4_filter_prio_in_range(struct net_device *dev, u32 idx, u32 prio);
|
||||
#endif /* __CXGB4_FILTER_H */
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#include "cxgb4_tc_u32.h"
|
||||
#include "cxgb4_tc_flower.h"
|
||||
#include "cxgb4_tc_mqprio.h"
|
||||
#include "cxgb4_tc_matchall.h"
|
||||
#include "cxgb4_ptp.h"
|
||||
#include "cxgb4_cudbg.h"
|
||||
|
||||
|
@ -3234,8 +3235,33 @@ static int cxgb_setup_tc_cls_u32(struct net_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int cxgb_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
|
||||
void *cb_priv)
|
||||
static int cxgb_setup_tc_matchall(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall,
|
||||
bool ingress)
|
||||
{
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
|
||||
if (!adap->tc_matchall)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (cls_matchall->command) {
|
||||
case TC_CLSMATCHALL_REPLACE:
|
||||
return cxgb4_tc_matchall_replace(dev, cls_matchall, ingress);
|
||||
case TC_CLSMATCHALL_DESTROY:
|
||||
return cxgb4_tc_matchall_destroy(dev, cls_matchall, ingress);
|
||||
case TC_CLSMATCHALL_STATS:
|
||||
if (ingress)
|
||||
return cxgb4_tc_matchall_stats(dev, cls_matchall);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int cxgb_setup_tc_block_ingress_cb(enum tc_setup_type type,
|
||||
void *type_data, void *cb_priv)
|
||||
{
|
||||
struct net_device *dev = cb_priv;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
|
@ -3256,11 +3282,40 @@ static int cxgb_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
|
|||
return cxgb_setup_tc_cls_u32(dev, type_data);
|
||||
case TC_SETUP_CLSFLOWER:
|
||||
return cxgb_setup_tc_flower(dev, type_data);
|
||||
case TC_SETUP_CLSMATCHALL:
|
||||
return cxgb_setup_tc_matchall(dev, type_data, true);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int cxgb_setup_tc_block_egress_cb(enum tc_setup_type type,
|
||||
void *type_data, void *cb_priv)
|
||||
{
|
||||
struct net_device *dev = cb_priv;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
|
||||
if (!(adap->flags & CXGB4_FULL_INIT_DONE)) {
|
||||
dev_err(adap->pdev_dev,
|
||||
"Failed to setup tc on port %d. Link Down?\n",
|
||||
pi->port_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!tc_cls_can_offload_and_chain0(dev, type_data))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case TC_SETUP_CLSMATCHALL:
|
||||
return cxgb_setup_tc_matchall(dev, type_data, false);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int cxgb_setup_tc_mqprio(struct net_device *dev,
|
||||
struct tc_mqprio_qopt_offload *mqprio)
|
||||
{
|
||||
|
@ -3274,19 +3329,34 @@ static int cxgb_setup_tc_mqprio(struct net_device *dev,
|
|||
|
||||
static LIST_HEAD(cxgb_block_cb_list);
|
||||
|
||||
static int cxgb_setup_tc_block(struct net_device *dev,
|
||||
struct flow_block_offload *f)
|
||||
{
|
||||
struct port_info *pi = netdev_priv(dev);
|
||||
flow_setup_cb_t *cb;
|
||||
bool ingress_only;
|
||||
|
||||
pi->tc_block_shared = f->block_shared;
|
||||
if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
|
||||
cb = cxgb_setup_tc_block_egress_cb;
|
||||
ingress_only = false;
|
||||
} else {
|
||||
cb = cxgb_setup_tc_block_ingress_cb;
|
||||
ingress_only = true;
|
||||
}
|
||||
|
||||
return flow_block_cb_setup_simple(f, &cxgb_block_cb_list,
|
||||
cb, pi, dev, ingress_only);
|
||||
}
|
||||
|
||||
static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
||||
void *type_data)
|
||||
{
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
|
||||
switch (type) {
|
||||
case TC_SETUP_QDISC_MQPRIO:
|
||||
return cxgb_setup_tc_mqprio(dev, type_data);
|
||||
case TC_SETUP_BLOCK:
|
||||
return flow_block_cb_setup_simple(type_data,
|
||||
&cxgb_block_cb_list,
|
||||
cxgb_setup_tc_block_cb,
|
||||
pi, dev, true);
|
||||
return cxgb_setup_tc_block(dev, type_data);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -5741,6 +5811,7 @@ static void free_some_resources(struct adapter *adapter)
|
|||
kvfree(adapter->srq);
|
||||
t4_cleanup_sched(adapter);
|
||||
kvfree(adapter->tids.tid_tab);
|
||||
cxgb4_cleanup_tc_matchall(adapter);
|
||||
cxgb4_cleanup_tc_mqprio(adapter);
|
||||
cxgb4_cleanup_tc_flower(adapter);
|
||||
cxgb4_cleanup_tc_u32(adapter);
|
||||
|
@ -6315,6 +6386,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
if (cxgb4_init_tc_mqprio(adapter))
|
||||
dev_warn(&pdev->dev,
|
||||
"could not offload tc mqprio, continuing\n");
|
||||
|
||||
if (cxgb4_init_tc_matchall(adapter))
|
||||
dev_warn(&pdev->dev,
|
||||
"could not offload tc matchall, continuing\n");
|
||||
}
|
||||
|
||||
if (is_offload(adapter) || is_hashfilter(adapter)) {
|
||||
|
|
|
@ -378,15 +378,14 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
|
|||
}
|
||||
}
|
||||
|
||||
static void cxgb4_process_flow_actions(struct net_device *in,
|
||||
struct flow_cls_offload *cls,
|
||||
struct ch_filter_specification *fs)
|
||||
void cxgb4_process_flow_actions(struct net_device *in,
|
||||
struct flow_action *actions,
|
||||
struct ch_filter_specification *fs)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
|
||||
struct flow_action_entry *act;
|
||||
int i;
|
||||
|
||||
flow_action_for_each(i, act, &rule->action) {
|
||||
flow_action_for_each(i, act, actions) {
|
||||
switch (act->id) {
|
||||
case FLOW_ACTION_ACCEPT:
|
||||
fs->action = FILTER_PASS;
|
||||
|
@ -544,17 +543,16 @@ static bool valid_pedit_action(struct net_device *dev,
|
|||
return true;
|
||||
}
|
||||
|
||||
static int cxgb4_validate_flow_actions(struct net_device *dev,
|
||||
struct flow_cls_offload *cls)
|
||||
int cxgb4_validate_flow_actions(struct net_device *dev,
|
||||
struct flow_action *actions)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
|
||||
struct flow_action_entry *act;
|
||||
bool act_redir = false;
|
||||
bool act_pedit = false;
|
||||
bool act_vlan = false;
|
||||
int i;
|
||||
|
||||
flow_action_for_each(i, act, &rule->action) {
|
||||
flow_action_for_each(i, act, actions) {
|
||||
switch (act->id) {
|
||||
case FLOW_ACTION_ACCEPT:
|
||||
case FLOW_ACTION_DROP:
|
||||
|
@ -636,14 +634,15 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
|
|||
int cxgb4_tc_flower_replace(struct net_device *dev,
|
||||
struct flow_cls_offload *cls)
|
||||
{
|
||||
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
|
||||
struct netlink_ext_ack *extack = cls->common.extack;
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
struct ch_tc_flower_entry *ch_flower;
|
||||
struct ch_filter_specification *fs;
|
||||
struct filter_ctx ctx;
|
||||
int fidx;
|
||||
int ret;
|
||||
int fidx, ret;
|
||||
|
||||
if (cxgb4_validate_flow_actions(dev, cls))
|
||||
if (cxgb4_validate_flow_actions(dev, &rule->action))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (cxgb4_validate_flow_match(dev, cls))
|
||||
|
@ -658,20 +657,41 @@ int cxgb4_tc_flower_replace(struct net_device *dev,
|
|||
fs = &ch_flower->fs;
|
||||
fs->hitcnts = 1;
|
||||
cxgb4_process_flow_match(dev, cls, fs);
|
||||
cxgb4_process_flow_actions(dev, cls, fs);
|
||||
cxgb4_process_flow_actions(dev, &rule->action, fs);
|
||||
|
||||
fs->hash = is_filter_exact_match(adap, fs);
|
||||
if (fs->hash) {
|
||||
fidx = 0;
|
||||
} else {
|
||||
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
|
||||
if (fidx < 0) {
|
||||
netdev_err(dev, "%s: No fidx for offload.\n", __func__);
|
||||
u8 inet_family;
|
||||
|
||||
inet_family = fs->type ? PF_INET6 : PF_INET;
|
||||
|
||||
/* Note that TC uses prio 0 to indicate stack to
|
||||
* generate automatic prio and hence doesn't pass prio
|
||||
* 0 to driver. However, the hardware TCAM index
|
||||
* starts from 0. Hence, the -1 here.
|
||||
*/
|
||||
if (cls->common.prio <= adap->tids.nftids)
|
||||
fidx = cls->common.prio - 1;
|
||||
else
|
||||
fidx = cxgb4_get_free_ftid(dev, inet_family);
|
||||
|
||||
/* Only insert FLOWER rule if its priority doesn't
|
||||
* conflict with existing rules in the LETCAM.
|
||||
*/
|
||||
if (fidx < 0 ||
|
||||
!cxgb4_filter_prio_in_range(dev, fidx, cls->common.prio)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"No free LETCAM index available");
|
||||
ret = -ENOMEM;
|
||||
goto free_entry;
|
||||
}
|
||||
}
|
||||
|
||||
fs->tc_prio = cls->common.prio;
|
||||
fs->tc_cookie = cls->cookie;
|
||||
|
||||
init_completion(&ctx.completion);
|
||||
ret = __cxgb4_set_filter(dev, fidx, fs, &ctx);
|
||||
if (ret) {
|
||||
|
|
|
@ -108,6 +108,12 @@ struct ch_tc_pedit_fields {
|
|||
#define PEDIT_TCP_SPORT_DPORT 0x0
|
||||
#define PEDIT_UDP_SPORT_DPORT 0x0
|
||||
|
||||
void cxgb4_process_flow_actions(struct net_device *in,
|
||||
struct flow_action *actions,
|
||||
struct ch_filter_specification *fs);
|
||||
int cxgb4_validate_flow_actions(struct net_device *dev,
|
||||
struct flow_action *actions);
|
||||
|
||||
int cxgb4_tc_flower_replace(struct net_device *dev,
|
||||
struct flow_cls_offload *cls);
|
||||
int cxgb4_tc_flower_destroy(struct net_device *dev,
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
|
||||
|
||||
#include "cxgb4.h"
|
||||
#include "cxgb4_tc_matchall.h"
|
||||
#include "sched.h"
|
||||
#include "cxgb4_uld.h"
|
||||
#include "cxgb4_filter.h"
|
||||
#include "cxgb4_tc_flower.h"
|
||||
|
||||
static int cxgb4_matchall_egress_validate(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls)
|
||||
{
|
||||
struct netlink_ext_ack *extack = cls->common.extack;
|
||||
struct flow_action *actions = &cls->rule->action;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct flow_action_entry *entry;
|
||||
u64 max_link_rate;
|
||||
u32 i, speed;
|
||||
int ret;
|
||||
|
||||
if (!flow_action_has_entries(actions)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Egress MATCHALL offload needs at least 1 policing action");
|
||||
return -EINVAL;
|
||||
} else if (!flow_offload_has_one_action(actions)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Egress MATCHALL offload only supports 1 policing action");
|
||||
return -EINVAL;
|
||||
} else if (pi->tc_block_shared) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Egress MATCHALL offload not supported with shared blocks");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = t4_get_link_params(pi, NULL, &speed, NULL);
|
||||
if (ret) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Failed to get max speed supported by the link");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Convert from Mbps to bps */
|
||||
max_link_rate = (u64)speed * 1000 * 1000;
|
||||
|
||||
flow_action_for_each(i, entry, actions) {
|
||||
switch (entry->id) {
|
||||
case FLOW_ACTION_POLICE:
|
||||
/* Convert bytes per second to bits per second */
|
||||
if (entry->police.rate_bytes_ps * 8 > max_link_rate) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Specified policing max rate is larger than underlying link speed");
|
||||
return -ERANGE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Only policing action supported with Egress MATCHALL offload");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxgb4_matchall_alloc_tc(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls)
|
||||
{
|
||||
struct ch_sched_params p = {
|
||||
.type = SCHED_CLASS_TYPE_PACKET,
|
||||
.u.params.level = SCHED_CLASS_LEVEL_CH_RL,
|
||||
.u.params.mode = SCHED_CLASS_MODE_CLASS,
|
||||
.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS,
|
||||
.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS,
|
||||
.u.params.class = SCHED_CLS_NONE,
|
||||
.u.params.minrate = 0,
|
||||
.u.params.weight = 0,
|
||||
.u.params.pktsize = dev->mtu,
|
||||
};
|
||||
struct netlink_ext_ack *extack = cls->common.extack;
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
struct flow_action_entry *entry;
|
||||
struct sched_class *e;
|
||||
u32 i;
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
|
||||
flow_action_for_each(i, entry, &cls->rule->action)
|
||||
if (entry->id == FLOW_ACTION_POLICE)
|
||||
break;
|
||||
|
||||
/* Convert from bytes per second to Kbps */
|
||||
p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000);
|
||||
p.u.params.channel = pi->tx_chan;
|
||||
e = cxgb4_sched_class_alloc(dev, &p);
|
||||
if (!e) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"No free traffic class available for policing action");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tc_port_matchall->egress.hwtc = e->idx;
|
||||
tc_port_matchall->egress.cookie = cls->cookie;
|
||||
tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cxgb4_matchall_free_tc(struct net_device *dev)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc);
|
||||
|
||||
tc_port_matchall->egress.hwtc = SCHED_CLS_NONE;
|
||||
tc_port_matchall->egress.cookie = 0;
|
||||
tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED;
|
||||
}
|
||||
|
||||
static int cxgb4_matchall_alloc_filter(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls)
|
||||
{
|
||||
struct netlink_ext_ack *extack = cls->common.extack;
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
struct ch_filter_specification *fs;
|
||||
int ret, fidx;
|
||||
|
||||
/* Note that TC uses prio 0 to indicate stack to generate
|
||||
* automatic prio and hence doesn't pass prio 0 to driver.
|
||||
* However, the hardware TCAM index starts from 0. Hence, the
|
||||
* -1 here. 1 slot is enough to create a wildcard matchall
|
||||
* VIID rule.
|
||||
*/
|
||||
if (cls->common.prio <= adap->tids.nftids)
|
||||
fidx = cls->common.prio - 1;
|
||||
else
|
||||
fidx = cxgb4_get_free_ftid(dev, PF_INET);
|
||||
|
||||
/* Only insert MATCHALL rule if its priority doesn't conflict
|
||||
* with existing rules in the LETCAM.
|
||||
*/
|
||||
if (fidx < 0 ||
|
||||
!cxgb4_filter_prio_in_range(dev, fidx, cls->common.prio)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"No free LETCAM index available");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
fs = &tc_port_matchall->ingress.fs;
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
|
||||
fs->tc_prio = cls->common.prio;
|
||||
fs->tc_cookie = cls->cookie;
|
||||
fs->hitcnts = 1;
|
||||
|
||||
fs->val.pfvf_vld = 1;
|
||||
fs->val.pf = adap->pf;
|
||||
fs->val.vf = pi->vin;
|
||||
|
||||
cxgb4_process_flow_actions(dev, &cls->rule->action, fs);
|
||||
|
||||
ret = cxgb4_set_filter(dev, fidx, fs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tc_port_matchall->ingress.tid = fidx;
|
||||
tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_ENABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxgb4_matchall_free_filter(struct net_device *dev)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
int ret;
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
|
||||
ret = cxgb4_del_filter(dev, tc_port_matchall->ingress.tid,
|
||||
&tc_port_matchall->ingress.fs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tc_port_matchall->ingress.packets = 0;
|
||||
tc_port_matchall->ingress.bytes = 0;
|
||||
tc_port_matchall->ingress.last_used = 0;
|
||||
tc_port_matchall->ingress.tid = 0;
|
||||
tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cxgb4_tc_matchall_replace(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall,
|
||||
bool ingress)
|
||||
{
|
||||
struct netlink_ext_ack *extack = cls_matchall->common.extack;
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
int ret;
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
if (ingress) {
|
||||
if (tc_port_matchall->ingress.state ==
|
||||
CXGB4_MATCHALL_STATE_ENABLED) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Only 1 Ingress MATCHALL can be offloaded");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = cxgb4_validate_flow_actions(dev,
|
||||
&cls_matchall->rule->action);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return cxgb4_matchall_alloc_filter(dev, cls_matchall);
|
||||
}
|
||||
|
||||
if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Only 1 Egress MATCHALL can be offloaded");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = cxgb4_matchall_egress_validate(dev, cls_matchall);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return cxgb4_matchall_alloc_tc(dev, cls_matchall);
|
||||
}
|
||||
|
||||
int cxgb4_tc_matchall_destroy(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall,
|
||||
bool ingress)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
if (ingress) {
|
||||
if (cls_matchall->cookie !=
|
||||
tc_port_matchall->ingress.fs.tc_cookie)
|
||||
return -ENOENT;
|
||||
|
||||
return cxgb4_matchall_free_filter(dev);
|
||||
}
|
||||
|
||||
if (cls_matchall->cookie != tc_port_matchall->egress.cookie)
|
||||
return -ENOENT;
|
||||
|
||||
cxgb4_matchall_free_tc(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cxgb4_tc_matchall_stats(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
u64 packets, bytes;
|
||||
int ret;
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_DISABLED)
|
||||
return -ENOENT;
|
||||
|
||||
ret = cxgb4_get_filter_counters(dev, tc_port_matchall->ingress.tid,
|
||||
&packets, &bytes,
|
||||
tc_port_matchall->ingress.fs.hash);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tc_port_matchall->ingress.packets != packets) {
|
||||
flow_stats_update(&cls_matchall->stats,
|
||||
bytes - tc_port_matchall->ingress.bytes,
|
||||
packets - tc_port_matchall->ingress.packets,
|
||||
tc_port_matchall->ingress.last_used);
|
||||
|
||||
tc_port_matchall->ingress.packets = packets;
|
||||
tc_port_matchall->ingress.bytes = bytes;
|
||||
tc_port_matchall->ingress.last_used = jiffies;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cxgb4_matchall_disable_offload(struct net_device *dev)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
|
||||
tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
|
||||
if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED)
|
||||
cxgb4_matchall_free_tc(dev);
|
||||
|
||||
if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_ENABLED)
|
||||
cxgb4_matchall_free_filter(dev);
|
||||
}
|
||||
|
||||
int cxgb4_init_tc_matchall(struct adapter *adap)
|
||||
{
|
||||
struct cxgb4_tc_port_matchall *tc_port_matchall;
|
||||
struct cxgb4_tc_matchall *tc_matchall;
|
||||
int ret;
|
||||
|
||||
tc_matchall = kzalloc(sizeof(*tc_matchall), GFP_KERNEL);
|
||||
if (!tc_matchall)
|
||||
return -ENOMEM;
|
||||
|
||||
tc_port_matchall = kcalloc(adap->params.nports,
|
||||
sizeof(*tc_port_matchall),
|
||||
GFP_KERNEL);
|
||||
if (!tc_port_matchall) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_matchall;
|
||||
}
|
||||
|
||||
tc_matchall->port_matchall = tc_port_matchall;
|
||||
adap->tc_matchall = tc_matchall;
|
||||
return 0;
|
||||
|
||||
out_free_matchall:
|
||||
kfree(tc_matchall);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cxgb4_cleanup_tc_matchall(struct adapter *adap)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
if (adap->tc_matchall) {
|
||||
if (adap->tc_matchall->port_matchall) {
|
||||
for (i = 0; i < adap->params.nports; i++) {
|
||||
struct net_device *dev = adap->port[i];
|
||||
|
||||
if (dev)
|
||||
cxgb4_matchall_disable_offload(dev);
|
||||
}
|
||||
kfree(adap->tc_matchall->port_matchall);
|
||||
}
|
||||
kfree(adap->tc_matchall);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2019 Chelsio Communications. All rights reserved. */
|
||||
|
||||
#ifndef __CXGB4_TC_MATCHALL_H__
|
||||
#define __CXGB4_TC_MATCHALL_H__
|
||||
|
||||
#include <net/pkt_cls.h>
|
||||
|
||||
enum cxgb4_matchall_state {
|
||||
CXGB4_MATCHALL_STATE_DISABLED = 0,
|
||||
CXGB4_MATCHALL_STATE_ENABLED,
|
||||
};
|
||||
|
||||
struct cxgb4_matchall_egress_entry {
|
||||
enum cxgb4_matchall_state state; /* Current MATCHALL offload state */
|
||||
u8 hwtc; /* Traffic class bound to port */
|
||||
u64 cookie; /* Used to identify the MATCHALL rule offloaded */
|
||||
};
|
||||
|
||||
struct cxgb4_matchall_ingress_entry {
|
||||
enum cxgb4_matchall_state state; /* Current MATCHALL offload state */
|
||||
u32 tid; /* Index to hardware filter entry */
|
||||
struct ch_filter_specification fs; /* Filter entry */
|
||||
u64 bytes; /* # of bytes hitting the filter */
|
||||
u64 packets; /* # of packets hitting the filter */
|
||||
u64 last_used; /* Last updated jiffies time */
|
||||
};
|
||||
|
||||
struct cxgb4_tc_port_matchall {
|
||||
struct cxgb4_matchall_egress_entry egress; /* Egress offload info */
|
||||
struct cxgb4_matchall_ingress_entry ingress; /* Ingress offload info */
|
||||
};
|
||||
|
||||
struct cxgb4_tc_matchall {
|
||||
struct cxgb4_tc_port_matchall *port_matchall; /* Per port entry */
|
||||
};
|
||||
|
||||
int cxgb4_tc_matchall_replace(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall,
|
||||
bool ingress);
|
||||
int cxgb4_tc_matchall_destroy(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall,
|
||||
bool ingress);
|
||||
int cxgb4_tc_matchall_stats(struct net_device *dev,
|
||||
struct tc_cls_matchall_offload *cls_matchall);
|
||||
|
||||
int cxgb4_init_tc_matchall(struct adapter *adap);
|
||||
void cxgb4_cleanup_tc_matchall(struct adapter *adap);
|
||||
#endif /* __CXGB4_TC_MATCHALL_H__ */
|
|
@ -11,8 +11,7 @@ static int cxgb4_mqprio_validate(struct net_device *dev,
|
|||
u64 min_rate = 0, max_rate = 0, max_link_rate;
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct adapter *adap = netdev2adap(dev);
|
||||
u32 qcount = 0, qoffset = 0;
|
||||
u32 link_ok, speed, mtu;
|
||||
u32 speed, qcount = 0, qoffset = 0;
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
|
@ -35,7 +34,7 @@ static int cxgb4_mqprio_validate(struct net_device *dev,
|
|||
return -ERANGE;
|
||||
}
|
||||
|
||||
ret = t4_get_link_params(pi, &link_ok, &speed, &mtu);
|
||||
ret = t4_get_link_params(pi, NULL, &speed, NULL);
|
||||
if (ret) {
|
||||
netdev_err(dev, "Failed to get link speed, ret: %d\n", ret);
|
||||
return -EINVAL;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -148,6 +149,7 @@ 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;
|
||||
|
@ -164,14 +166,21 @@ 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;
|
||||
/* Note that TC uses prio 0 to indicate stack to generate
|
||||
* automatic prio and hence doesn't pass prio 0 to driver.
|
||||
* However, the hardware TCAM index starts from 0. Hence, the
|
||||
* -1 here.
|
||||
*/
|
||||
filter_id = TC_U32_NODE(cls->knode.handle) - 1;
|
||||
|
||||
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;
|
||||
/* Only insert U32 rule if its priority doesn't conflict with
|
||||
* existing rules in the LETCAM.
|
||||
*/
|
||||
if (filter_id >= adapter->tids.nftids ||
|
||||
!cxgb4_filter_prio_in_range(dev, filter_id, cls->common.prio)) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"No free LETCAM index available");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
t = adapter->tc_u32;
|
||||
|
@ -190,6 +199,9 @@ int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
|
|||
|
||||
memset(&fs, 0, sizeof(fs));
|
||||
|
||||
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;
|
||||
|
@ -350,14 +362,10 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
|
|||
return -EOPNOTSUPP;
|
||||
|
||||
/* Fetch the location to delete the filter. */
|
||||
filter_id = cls->knode.handle & 0xFFFFF;
|
||||
|
||||
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);
|
||||
filter_id = TC_U32_NODE(cls->knode.handle) - 1;
|
||||
if (filter_id >= adapter->tids.nftids ||
|
||||
cls->knode.handle != adapter->tids.ftid_tab[filter_id].fs.tc_cookie)
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
t = adapter->tc_u32;
|
||||
handle = cls->knode.handle;
|
||||
|
|
|
@ -50,6 +50,7 @@ static int t4_sched_class_fw_cmd(struct port_info *pi,
|
|||
e = &s->tab[p->u.params.class];
|
||||
switch (op) {
|
||||
case SCHED_FW_OP_ADD:
|
||||
case SCHED_FW_OP_DEL:
|
||||
err = t4_sched_params(adap, p->type,
|
||||
p->u.params.level, p->u.params.mode,
|
||||
p->u.params.rateunit,
|
||||
|
@ -188,10 +189,8 @@ static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p)
|
|||
e = &pi->sched_tbl->tab[qe->param.class];
|
||||
list_del(&qe->list);
|
||||
kvfree(qe);
|
||||
if (atomic_dec_and_test(&e->refcnt)) {
|
||||
e->state = SCHED_STATE_UNUSED;
|
||||
memset(&e->info, 0, sizeof(e->info));
|
||||
}
|
||||
if (atomic_dec_and_test(&e->refcnt))
|
||||
cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -261,10 +260,8 @@ static int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p)
|
|||
e = &pi->sched_tbl->tab[fe->param.class];
|
||||
list_del(&fe->list);
|
||||
kvfree(fe);
|
||||
if (atomic_dec_and_test(&e->refcnt)) {
|
||||
e->state = SCHED_STATE_UNUSED;
|
||||
memset(&e->info, 0, sizeof(e->info));
|
||||
}
|
||||
if (atomic_dec_and_test(&e->refcnt))
|
||||
cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -469,10 +466,7 @@ static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
|
|||
struct sched_class *found = NULL;
|
||||
struct sched_class *e, *end;
|
||||
|
||||
/* Only allow tc to be shared among SCHED_FLOWC types. For
|
||||
* other types, always allocate a new tc.
|
||||
*/
|
||||
if (!p || p->u.params.mode != SCHED_CLASS_MODE_FLOW) {
|
||||
if (!p) {
|
||||
/* Get any available unused class */
|
||||
end = &s->tab[s->sched_size];
|
||||
for (e = &s->tab[0]; e != end; ++e) {
|
||||
|
@ -514,7 +508,7 @@ static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
|
|||
static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
|
||||
struct ch_sched_params *p)
|
||||
{
|
||||
struct sched_class *e;
|
||||
struct sched_class *e = NULL;
|
||||
u8 class_id;
|
||||
int err;
|
||||
|
||||
|
@ -529,10 +523,13 @@ static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
|
|||
if (class_id != SCHED_CLS_NONE)
|
||||
return NULL;
|
||||
|
||||
/* See if there's an exisiting class with same
|
||||
* requested sched params
|
||||
/* See if there's an exisiting class with same requested sched
|
||||
* params. Classes can only be shared among FLOWC types. For
|
||||
* other types, always request a new class.
|
||||
*/
|
||||
e = t4_sched_class_lookup(pi, p);
|
||||
if (p->u.params.mode == SCHED_CLASS_MODE_FLOW)
|
||||
e = t4_sched_class_lookup(pi, p);
|
||||
|
||||
if (!e) {
|
||||
struct ch_sched_params np;
|
||||
|
||||
|
@ -592,10 +589,35 @@ void cxgb4_sched_class_free(struct net_device *dev, u8 classid)
|
|||
{
|
||||
struct port_info *pi = netdev2pinfo(dev);
|
||||
struct sched_table *s = pi->sched_tbl;
|
||||
struct ch_sched_params p;
|
||||
struct sched_class *e;
|
||||
u32 speed;
|
||||
int ret;
|
||||
|
||||
e = &s->tab[classid];
|
||||
if (!atomic_read(&e->refcnt)) {
|
||||
if (!atomic_read(&e->refcnt) && e->state != SCHED_STATE_UNUSED) {
|
||||
/* Port based rate limiting needs explicit reset back
|
||||
* to max rate. But, we'll do explicit reset for all
|
||||
* types, instead of just port based type, to be on
|
||||
* the safer side.
|
||||
*/
|
||||
memcpy(&p, &e->info, sizeof(p));
|
||||
/* Always reset mode to 0. Otherwise, FLOWC mode will
|
||||
* still be enabled even after resetting the traffic
|
||||
* class.
|
||||
*/
|
||||
p.u.params.mode = 0;
|
||||
p.u.params.minrate = 0;
|
||||
p.u.params.pktsize = 0;
|
||||
|
||||
ret = t4_get_link_params(pi, NULL, &speed, NULL);
|
||||
if (!ret)
|
||||
p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */
|
||||
else
|
||||
p.u.params.maxrate = SCHED_MAX_RATE_KBPS;
|
||||
|
||||
t4_sched_class_fw_cmd(pi, &p, SCHED_FW_OP_DEL);
|
||||
|
||||
e->state = SCHED_STATE_UNUSED;
|
||||
memset(&e->info, 0, sizeof(e->info));
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ enum {
|
|||
|
||||
enum sched_fw_ops {
|
||||
SCHED_FW_OP_ADD,
|
||||
SCHED_FW_OP_DEL,
|
||||
};
|
||||
|
||||
enum sched_bind_type {
|
||||
|
|
|
@ -8777,8 +8777,8 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
|
|||
unsigned int *speedp, unsigned int *mtup)
|
||||
{
|
||||
unsigned int fw_caps = pi->adapter->params.fw_caps_support;
|
||||
struct fw_port_cmd port_cmd;
|
||||
unsigned int action, link_ok, mtu;
|
||||
struct fw_port_cmd port_cmd;
|
||||
fw_port_cap32_t linkattr;
|
||||
int ret;
|
||||
|
||||
|
@ -8813,9 +8813,12 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
|
|||
be32_to_cpu(port_cmd.u.info32.auxlinfo32_mtu32));
|
||||
}
|
||||
|
||||
*link_okp = link_ok;
|
||||
*speedp = fwcap_to_speed(linkattr);
|
||||
*mtup = mtu;
|
||||
if (link_okp)
|
||||
*link_okp = link_ok;
|
||||
if (speedp)
|
||||
*speedp = fwcap_to_speed(linkattr);
|
||||
if (mtup)
|
||||
*mtup = mtu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue