Merge branch 'net-sched-convert-cls-ndo_setup_tc-offload-calls-to-per-block-callbacks'

Jiri Pirko says:

====================
net: sched: convert cls ndo_setup_tc offload calls to per-block callbacks

This patchset is a bit bigger, but most of the patches are doing the
same changes in multiple classifiers and drivers. I could do some
squashes, but I think it is better split.

This is another dependency on the way to shared block implementation.
The goal is to remove use of tp->q in classifiers code.

Also, this provides drivers possibility to track binding of blocks to
qdiscs. Legacy drivers which do not support shared block offloading.
register one callback per binding. That maintains the current
functionality we have with ndo_setup_tc. Drivers which support block
sharing offload register one callback per block which safes overhead.

Patches 1-4 introduce the binding notifications and per-block callbacks
Patches 5-8 add block callbacks calls to classifiers
Patches 9-17 do convert from ndo_setup_tc calls to block callbacks for
             classifier offloads in drivers
Patches 18-20 do cleanup

v1->v2:
- patch1:
  - move new enum value to the end
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-10-21 03:04:09 +01:00
commit 471abeabb4
22 changed files with 872 additions and 250 deletions

View File

@ -7295,23 +7295,48 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc)
return 0;
}
static int bnxt_setup_flower(struct net_device *dev,
struct tc_cls_flower_offload *cls_flower)
static int bnxt_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt *bp = cb_priv;
if (BNXT_VF(bp))
return -EOPNOTSUPP;
return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, cls_flower);
switch (type) {
case TC_SETUP_CLSFLOWER:
return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, type_data);
default:
return -EOPNOTSUPP;
}
}
static int bnxt_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct bnxt *bp = netdev_priv(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, bnxt_setup_tc_block_cb,
bp, bp);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, bnxt_setup_tc_block_cb, bp);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_CLSFLOWER:
return bnxt_setup_flower(dev, type_data);
case TC_SETUP_BLOCK:
return bnxt_setup_tc_block(dev, type_data);
case TC_SETUP_MQPRIO: {
struct tc_mqprio_qopt *mqprio = type_data;

View File

@ -748,8 +748,7 @@ int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid,
{
int rc = 0;
if (!is_classid_clsact_ingress(cls_flower->common.classid) ||
cls_flower->common.chain_index)
if (cls_flower->common.chain_index)
return -EOPNOTSUPP;
switch (cls_flower->command) {

View File

@ -115,10 +115,11 @@ bnxt_vf_rep_get_stats64(struct net_device *dev,
stats->tx_bytes = vf_rep->tx_stats.bytes;
}
static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
static int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
struct bnxt_vf_rep *vf_rep = cb_priv;
struct bnxt *bp = vf_rep->bp;
int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid;
@ -130,6 +131,40 @@ static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
}
}
static int bnxt_vf_rep_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block,
bnxt_vf_rep_setup_tc_block_cb,
vf_rep, vf_rep);
return 0;
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block,
bnxt_vf_rep_setup_tc_block_cb, vf_rep);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_BLOCK:
return bnxt_vf_rep_setup_tc_block(dev, type_data);
default:
return -EOPNOTSUPP;
}
}
struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code)
{
u16 vf_idx;

View File

@ -2889,8 +2889,7 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
static int cxgb_setup_tc_flower(struct net_device *dev,
struct tc_cls_flower_offload *cls_flower)
{
if (!is_classid_clsact_ingress(cls_flower->common.classid) ||
cls_flower->common.chain_index)
if (cls_flower->common.chain_index)
return -EOPNOTSUPP;
switch (cls_flower->command) {
@ -2908,8 +2907,7 @@ static int cxgb_setup_tc_flower(struct net_device *dev,
static int cxgb_setup_tc_cls_u32(struct net_device *dev,
struct tc_cls_u32_offload *cls_u32)
{
if (!is_classid_clsact_ingress(cls_u32->common.classid) ||
cls_u32->common.chain_index)
if (cls_u32->common.chain_index)
return -EOPNOTSUPP;
switch (cls_u32->command) {
@ -2923,9 +2921,10 @@ static int cxgb_setup_tc_cls_u32(struct net_device *dev,
}
}
static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
static int cxgb_setup_tc_block_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);
@ -2946,6 +2945,37 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
}
}
static int cxgb_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct port_info *pi = netdev2pinfo(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, cxgb_setup_tc_block_cb,
pi, dev);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, cxgb_setup_tc_block_cb, pi);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_BLOCK:
return cxgb_setup_tc_block(dev, type_data);
default:
return -EOPNOTSUPP;
}
}
static netdev_features_t cxgb_fix_features(struct net_device *dev,
netdev_features_t features)
{

View File

@ -9365,13 +9365,10 @@ free_jump:
return err;
}
static int ixgbe_setup_tc_cls_u32(struct net_device *dev,
static int ixgbe_setup_tc_cls_u32(struct ixgbe_adapter *adapter,
struct tc_cls_u32_offload *cls_u32)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
if (!is_classid_clsact_ingress(cls_u32->common.classid) ||
cls_u32->common.chain_index)
if (cls_u32->common.chain_index)
return -EOPNOTSUPP;
switch (cls_u32->command) {
@ -9390,6 +9387,40 @@ static int ixgbe_setup_tc_cls_u32(struct net_device *dev,
}
}
static int ixgbe_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct ixgbe_adapter *adapter = cb_priv;
switch (type) {
case TC_SETUP_CLSU32:
return ixgbe_setup_tc_cls_u32(adapter, type_data);
default:
return -EOPNOTSUPP;
}
}
static int ixgbe_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, ixgbe_setup_tc_block_cb,
adapter, adapter);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, ixgbe_setup_tc_block_cb,
adapter);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int ixgbe_setup_tc_mqprio(struct net_device *dev,
struct tc_mqprio_qopt *mqprio)
{
@ -9401,8 +9432,8 @@ static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_CLSU32:
return ixgbe_setup_tc_cls_u32(dev, type_data);
case TC_SETUP_BLOCK:
return ixgbe_setup_tc_block(dev, type_data);
case TC_SETUP_MQPRIO:
return ixgbe_setup_tc_mqprio(dev, type_data);
default:

View File

@ -1056,8 +1056,8 @@ int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv,
int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv,
struct ethtool_flash *flash);
int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data);
int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv);
/* mlx5e generic netdev management API */
struct net_device*

View File

@ -3083,13 +3083,10 @@ out:
}
#ifdef CONFIG_MLX5_ESWITCH
static int mlx5e_setup_tc_cls_flower(struct net_device *dev,
static int mlx5e_setup_tc_cls_flower(struct mlx5e_priv *priv,
struct tc_cls_flower_offload *cls_flower)
{
struct mlx5e_priv *priv = netdev_priv(dev);
if (!is_classid_clsact_ingress(cls_flower->common.classid) ||
cls_flower->common.chain_index)
if (cls_flower->common.chain_index)
return -EOPNOTSUPP;
switch (cls_flower->command) {
@ -3103,6 +3100,40 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev,
return -EOPNOTSUPP;
}
}
int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct mlx5e_priv *priv = cb_priv;
switch (type) {
case TC_SETUP_CLSFLOWER:
return mlx5e_setup_tc_cls_flower(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static int mlx5e_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct mlx5e_priv *priv = netdev_priv(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, mlx5e_setup_tc_block_cb,
priv, priv);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, mlx5e_setup_tc_block_cb,
priv);
return 0;
default:
return -EOPNOTSUPP;
}
}
#endif
int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
@ -3110,8 +3141,8 @@ int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
{
switch (type) {
#ifdef CONFIG_MLX5_ESWITCH
case TC_SETUP_CLSFLOWER:
return mlx5e_setup_tc_cls_flower(dev, type_data);
case TC_SETUP_BLOCK:
return mlx5e_setup_tc_block(dev, type_data);
#endif
case TC_SETUP_MQPRIO:
return mlx5e_setup_tc_mqprio(dev, type_data);

View File

@ -659,13 +659,10 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
}
static int
mlx5e_rep_setup_tc_cls_flower(struct net_device *dev,
mlx5e_rep_setup_tc_cls_flower(struct mlx5e_priv *priv,
struct tc_cls_flower_offload *cls_flower)
{
struct mlx5e_priv *priv = netdev_priv(dev);
if (!is_classid_clsact_ingress(cls_flower->common.classid) ||
cls_flower->common.chain_index)
if (cls_flower->common.chain_index)
return -EOPNOTSUPP;
switch (cls_flower->command) {
@ -680,23 +677,48 @@ mlx5e_rep_setup_tc_cls_flower(struct net_device *dev,
}
}
static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
static int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct mlx5e_priv *priv = cb_priv;
switch (type) {
case TC_SETUP_CLSFLOWER:
return mlx5e_rep_setup_tc_cls_flower(dev, type_data);
return mlx5e_rep_setup_tc_cls_flower(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
static int mlx5e_rep_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
struct net_device *dev = cb_priv;
struct mlx5e_priv *priv = netdev_priv(dev);
return mlx5e_setup_tc(dev, type, type_data);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, mlx5e_rep_setup_tc_cb,
priv, priv);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, mlx5e_rep_setup_tc_cb, priv);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_BLOCK:
return mlx5e_rep_setup_tc_block(dev, type_data);
default:
return -EOPNOTSUPP;
}
}
bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv)
@ -987,6 +1009,7 @@ mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
{
struct mlx5e_rep_priv *rpriv;
struct net_device *netdev;
struct mlx5e_priv *upriv;
int err;
rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL);
@ -1018,8 +1041,9 @@ mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
goto err_detach_netdev;
}
err = tc_setup_cb_egdev_register(netdev, mlx5e_rep_setup_tc_cb,
mlx5_eswitch_get_uplink_netdev(esw));
upriv = netdev_priv(mlx5_eswitch_get_uplink_netdev(esw));
err = tc_setup_cb_egdev_register(netdev, mlx5e_setup_tc_block_cb,
upriv);
if (err)
goto err_neigh_cleanup;
@ -1033,8 +1057,8 @@ mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
return 0;
err_egdev_cleanup:
tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb,
mlx5_eswitch_get_uplink_netdev(esw));
tc_setup_cb_egdev_unregister(netdev, mlx5e_setup_tc_block_cb,
upriv);
err_neigh_cleanup:
mlx5e_rep_neigh_cleanup(rpriv);
@ -1055,10 +1079,12 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep)
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5e_rep_priv *rpriv = priv->ppriv;
void *ppriv = priv->ppriv;
struct mlx5e_priv *upriv;
unregister_netdev(rep->netdev);
tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb,
mlx5_eswitch_get_uplink_netdev(esw));
upriv = netdev_priv(mlx5_eswitch_get_uplink_netdev(esw));
tc_setup_cb_egdev_unregister(netdev, mlx5e_setup_tc_block_cb,
upriv);
mlx5e_rep_neigh_cleanup(rpriv);
mlx5e_detach_netdev(priv);
mlx5e_destroy_netdev(priv);

View File

@ -1697,17 +1697,9 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
}
static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_cls_matchall_offload *f)
struct tc_cls_matchall_offload *f,
bool ingress)
{
bool ingress;
if (is_classid_clsact_ingress(f->common.classid))
ingress = true;
else if (is_classid_clsact_egress(f->common.classid))
ingress = false;
else
return -EOPNOTSUPP;
if (f->common.chain_index)
return -EOPNOTSUPP;
@ -1725,17 +1717,9 @@ static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
static int
mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_cls_flower_offload *f)
struct tc_cls_flower_offload *f,
bool ingress)
{
bool ingress;
if (is_classid_clsact_ingress(f->common.classid))
ingress = true;
else if (is_classid_clsact_egress(f->common.classid))
ingress = false;
else
return -EOPNOTSUPP;
switch (f->command) {
case TC_CLSFLOWER_REPLACE:
return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, f);
@ -1749,16 +1733,67 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port,
}
}
static int mlxsw_sp_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv, bool ingress)
{
struct mlxsw_sp_port *mlxsw_sp_port = cb_priv;
switch (type) {
case TC_SETUP_CLSMATCHALL:
return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data,
ingress);
case TC_SETUP_CLSFLOWER:
return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, type_data,
ingress);
default:
return -EOPNOTSUPP;
}
}
static int mlxsw_sp_setup_tc_block_cb_ig(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
return mlxsw_sp_setup_tc_block_cb(type, type_data, cb_priv, true);
}
static int mlxsw_sp_setup_tc_block_cb_eg(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
return mlxsw_sp_setup_tc_block_cb(type, type_data, cb_priv, false);
}
static int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_block_offload *f)
{
tc_setup_cb_t *cb;
if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
cb = mlxsw_sp_setup_tc_block_cb_ig;
else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
cb = mlxsw_sp_setup_tc_block_cb_eg;
else
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, cb, mlxsw_sp_port,
mlxsw_sp_port);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, cb, mlxsw_sp_port);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
switch (type) {
case TC_SETUP_CLSMATCHALL:
return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data);
case TC_SETUP_CLSFLOWER:
return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, type_data);
case TC_SETUP_BLOCK:
return mlxsw_sp_setup_tc_block(mlxsw_sp_port, type_data);
default:
return -EOPNOTSUPP;
}

View File

@ -114,22 +114,56 @@ static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn)
kfree(nn->app_priv);
}
static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct tc_cls_bpf_offload *cls_bpf = type_data;
struct nfp_net *nn = cb_priv;
switch (type) {
case TC_SETUP_CLSBPF:
if (!nfp_net_ebpf_capable(nn) ||
cls_bpf->common.protocol != htons(ETH_P_ALL) ||
cls_bpf->common.chain_index)
return -EOPNOTSUPP;
return nfp_net_bpf_offload(nn, cls_bpf);
default:
return -EOPNOTSUPP;
}
}
static int nfp_bpf_setup_tc_block(struct net_device *netdev,
struct tc_block_offload *f)
{
struct nfp_net *nn = netdev_priv(netdev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block,
nfp_bpf_setup_tc_block_cb,
nn, nn);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block,
nfp_bpf_setup_tc_block_cb,
nn);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev,
enum tc_setup_type type, void *type_data)
{
struct tc_cls_bpf_offload *cls_bpf = type_data;
struct nfp_net *nn = netdev_priv(netdev);
if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) ||
!is_classid_clsact_ingress(cls_bpf->common.classid) ||
cls_bpf->common.protocol != htons(ETH_P_ALL) ||
cls_bpf->common.chain_index)
switch (type) {
case TC_SETUP_BLOCK:
return nfp_bpf_setup_tc_block(netdev, type_data);
default:
return -EOPNOTSUPP;
if (nn->dp.bpf_offload_xdp)
return -EBUSY;
return nfp_net_bpf_offload(nn, cls_bpf);
}
}
static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn)

View File

@ -449,6 +449,10 @@ static int
nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev,
struct tc_cls_flower_offload *flower)
{
if (!eth_proto_is_802_3(flower->common.protocol) ||
flower->common.chain_index)
return -EOPNOTSUPP;
switch (flower->command) {
case TC_CLSFLOWER_REPLACE:
return nfp_flower_add_offload(app, netdev, flower);
@ -461,16 +465,50 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev,
return -EOPNOTSUPP;
}
static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct nfp_net *nn = cb_priv;
switch (type) {
case TC_SETUP_CLSFLOWER:
return nfp_flower_repr_offload(nn->app, nn->port->netdev,
type_data);
default:
return -EOPNOTSUPP;
}
}
static int nfp_flower_setup_tc_block(struct net_device *netdev,
struct tc_block_offload *f)
{
struct nfp_net *nn = netdev_priv(netdev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block,
nfp_flower_setup_tc_block_cb,
nn, nn);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block,
nfp_flower_setup_tc_block_cb,
nn);
return 0;
default:
return -EOPNOTSUPP;
}
}
int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev,
enum tc_setup_type type, void *type_data)
{
struct tc_cls_flower_offload *cls_flower = type_data;
if (type != TC_SETUP_CLSFLOWER ||
!is_classid_clsact_ingress(cls_flower->common.classid) ||
!eth_proto_is_802_3(cls_flower->common.protocol) ||
cls_flower->common.chain_index)
switch (type) {
case TC_SETUP_BLOCK:
return nfp_flower_setup_tc_block(netdev, type_data);
default:
return -EOPNOTSUPP;
return nfp_flower_repr_offload(app, netdev, cls_flower);
}
}

View File

@ -775,6 +775,7 @@ enum tc_setup_type {
TC_SETUP_CLSFLOWER,
TC_SETUP_CLSMATCHALL,
TC_SETUP_CLSBPF,
TC_SETUP_BLOCK,
};
/* These structures hold the attributes of xdp state that are being passed

View File

@ -17,13 +17,31 @@ struct tcf_walker {
int register_tcf_proto_ops(struct tcf_proto_ops *ops);
int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
enum tcf_block_binder_type {
TCF_BLOCK_BINDER_TYPE_UNSPEC,
TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS,
TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS,
};
struct tcf_block_ext_info {
enum tcf_block_binder_type binder_type;
};
struct tcf_block_cb;
#ifdef CONFIG_NET_CLS
struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
bool create);
void tcf_chain_put(struct tcf_chain *chain);
int tcf_block_get(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q);
int tcf_block_get_ext(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei);
void tcf_block_put(struct tcf_block *block);
void tcf_block_put_ext(struct tcf_block *block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei);
static inline struct Qdisc *tcf_block_q(struct tcf_block *block)
{
@ -35,6 +53,21 @@ static inline struct net_device *tcf_block_dev(struct tcf_block *block)
return tcf_block_q(block)->dev_queue->dev;
}
void *tcf_block_cb_priv(struct tcf_block_cb *block_cb);
struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident);
void tcf_block_cb_incref(struct tcf_block_cb *block_cb);
unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb);
struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv);
int tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv);
void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb);
void tcf_block_cb_unregister(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident);
int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res, bool compat_mode);
@ -46,10 +79,25 @@ int tcf_block_get(struct tcf_block **p_block,
return 0;
}
static inline
int tcf_block_get_ext(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
return 0;
}
static inline void tcf_block_put(struct tcf_block *block)
{
}
static inline
void tcf_block_put_ext(struct tcf_block *block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
}
static inline struct Qdisc *tcf_block_q(struct tcf_block *block)
{
return NULL;
@ -60,6 +108,70 @@ static inline struct net_device *tcf_block_dev(struct tcf_block *block)
return NULL;
}
static inline
int tc_setup_cb_block_register(struct tcf_block *block, tc_setup_cb_t *cb,
void *cb_priv)
{
return 0;
}
static inline
void tc_setup_cb_block_unregister(struct tcf_block *block, tc_setup_cb_t *cb,
void *cb_priv)
{
}
static inline
void *tcf_block_cb_priv(struct tcf_block_cb *block_cb)
{
return NULL;
}
static inline
struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident)
{
return NULL;
}
static inline
void tcf_block_cb_incref(struct tcf_block_cb *block_cb)
{
}
static inline
unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb)
{
return 0;
}
static inline
struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv)
{
return NULL;
}
static inline
int tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv)
{
return 0;
}
static inline
void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb)
{
}
static inline
void tcf_block_cb_unregister(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident)
{
}
static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res, bool compat_mode)
{
@ -431,14 +543,24 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
}
#endif /* CONFIG_NET_CLS_IND */
int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type,
void *type_data, bool err_stop);
int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts,
enum tc_setup_type type, void *type_data, bool err_stop);
enum tc_block_command {
TC_BLOCK_BIND,
TC_BLOCK_UNBIND,
};
struct tc_block_offload {
enum tc_block_command command;
enum tcf_block_binder_type binder_type;
struct tcf_block *block;
};
struct tc_cls_common_offload {
u32 chain_index;
__be16 protocol;
u32 prio;
u32 classid;
};
static inline void
@ -448,7 +570,6 @@ tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common,
cls_common->chain_index = tp->chain->index;
cls_common->protocol = tp->protocol;
cls_common->prio = tp->prio;
cls_common->classid = tp->classid;
}
struct tc_cls_u32_knode {

View File

@ -135,19 +135,6 @@ static inline unsigned int psched_mtu(const struct net_device *dev)
return dev->mtu + dev->hard_header_len;
}
static inline bool is_classid_clsact_ingress(u32 classid)
{
/* This also returns true for ingress qdisc */
return TC_H_MAJ(classid) == TC_H_MAJ(TC_H_CLSACT) &&
TC_H_MIN(classid) != TC_H_MIN(TC_H_MIN_EGRESS);
}
static inline bool is_classid_clsact_egress(u32 classid)
{
return TC_H_MAJ(classid) == TC_H_MAJ(TC_H_CLSACT) &&
TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_EGRESS);
}
static inline struct net *qdisc_net(struct Qdisc *q)
{
return dev_net(q->dev_queue->dev);

View File

@ -272,6 +272,7 @@ struct tcf_block {
struct list_head chain_list;
struct net *net;
struct Qdisc *q;
struct list_head cb_list;
};
static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)

View File

@ -777,17 +777,9 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev,
}
static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
struct tc_cls_matchall_offload *cls,
bool ingress)
{
bool ingress;
if (is_classid_clsact_ingress(cls->common.classid))
ingress = true;
else if (is_classid_clsact_egress(cls->common.classid))
ingress = false;
else
return -EOPNOTSUPP;
if (cls->common.chain_index)
return -EOPNOTSUPP;
@ -802,12 +794,60 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev,
}
}
static int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv, bool ingress)
{
struct net_device *dev = cb_priv;
switch (type) {
case TC_SETUP_CLSMATCHALL:
return dsa_slave_setup_tc_cls_matchall(dev, type_data, ingress);
default:
return -EOPNOTSUPP;
}
}
static int dsa_slave_setup_tc_block_cb_ig(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, true);
}
static int dsa_slave_setup_tc_block_cb_eg(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, false);
}
static int dsa_slave_setup_tc_block(struct net_device *dev,
struct tc_block_offload *f)
{
tc_setup_cb_t *cb;
if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
cb = dsa_slave_setup_tc_block_cb_ig;
else if (f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
cb = dsa_slave_setup_tc_block_cb_eg;
else
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, cb, dev, dev);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, cb, dev);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_CLSMATCHALL:
return dsa_slave_setup_tc_cls_matchall(dev, type_data);
case TC_SETUP_BLOCK:
return dsa_slave_setup_tc_block(dev, type_data);
default:
return -EOPNOTSUPP;
}

View File

@ -240,8 +240,36 @@ tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain,
chain->p_filter_chain = p_filter_chain;
}
int tcf_block_get(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q)
static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q,
struct tcf_block_ext_info *ei,
enum tc_block_command command)
{
struct net_device *dev = q->dev_queue->dev;
struct tc_block_offload bo = {};
if (!tc_can_offload(dev))
return;
bo.command = command;
bo.binder_type = ei->binder_type;
bo.block = block;
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
}
static void tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
tcf_block_offload_cmd(block, q, ei, TC_BLOCK_BIND);
}
static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND);
}
int tcf_block_get_ext(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
struct tcf_chain *chain;
@ -250,6 +278,8 @@ int tcf_block_get(struct tcf_block **p_block,
if (!block)
return -ENOMEM;
INIT_LIST_HEAD(&block->chain_list);
INIT_LIST_HEAD(&block->cb_list);
/* Create chain 0 by default, it has to be always present. */
chain = tcf_chain_create(block, 0);
if (!chain) {
@ -259,6 +289,7 @@ int tcf_block_get(struct tcf_block **p_block,
tcf_chain_filter_chain_ptr_set(chain, p_filter_chain);
block->net = qdisc_net(q);
block->q = q;
tcf_block_offload_bind(block, q, ei);
*p_block = block;
return 0;
@ -266,15 +297,28 @@ err_chain_create:
kfree(block);
return err;
}
EXPORT_SYMBOL(tcf_block_get_ext);
int tcf_block_get(struct tcf_block **p_block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q)
{
struct tcf_block_ext_info ei = {0, };
return tcf_block_get_ext(p_block, p_filter_chain, q, &ei);
}
EXPORT_SYMBOL(tcf_block_get);
void tcf_block_put(struct tcf_block *block)
void tcf_block_put_ext(struct tcf_block *block,
struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
struct tcf_block_ext_info *ei)
{
struct tcf_chain *chain, *tmp;
if (!block)
return;
tcf_block_offload_unbind(block, q, ei);
/* XXX: Standalone actions are not allowed to jump to any chain, and
* bound actions should be all removed after flushing. However,
* filters are destroyed in RCU callbacks, we have to hold the chains
@ -302,8 +346,119 @@ void tcf_block_put(struct tcf_block *block)
tcf_chain_put(chain);
kfree(block);
}
EXPORT_SYMBOL(tcf_block_put_ext);
void tcf_block_put(struct tcf_block *block)
{
struct tcf_block_ext_info ei = {0, };
tcf_block_put_ext(block, NULL, block->q, &ei);
}
EXPORT_SYMBOL(tcf_block_put);
struct tcf_block_cb {
struct list_head list;
tc_setup_cb_t *cb;
void *cb_ident;
void *cb_priv;
unsigned int refcnt;
};
void *tcf_block_cb_priv(struct tcf_block_cb *block_cb)
{
return block_cb->cb_priv;
}
EXPORT_SYMBOL(tcf_block_cb_priv);
struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident)
{ struct tcf_block_cb *block_cb;
list_for_each_entry(block_cb, &block->cb_list, list)
if (block_cb->cb == cb && block_cb->cb_ident == cb_ident)
return block_cb;
return NULL;
}
EXPORT_SYMBOL(tcf_block_cb_lookup);
void tcf_block_cb_incref(struct tcf_block_cb *block_cb)
{
block_cb->refcnt++;
}
EXPORT_SYMBOL(tcf_block_cb_incref);
unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb)
{
return --block_cb->refcnt;
}
EXPORT_SYMBOL(tcf_block_cb_decref);
struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv)
{
struct tcf_block_cb *block_cb;
block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
if (!block_cb)
return NULL;
block_cb->cb = cb;
block_cb->cb_ident = cb_ident;
block_cb->cb_priv = cb_priv;
list_add(&block_cb->list, &block->cb_list);
return block_cb;
}
EXPORT_SYMBOL(__tcf_block_cb_register);
int tcf_block_cb_register(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident,
void *cb_priv)
{
struct tcf_block_cb *block_cb;
block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv);
return block_cb ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(tcf_block_cb_register);
void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb)
{
list_del(&block_cb->list);
kfree(block_cb);
}
EXPORT_SYMBOL(__tcf_block_cb_unregister);
void tcf_block_cb_unregister(struct tcf_block *block,
tc_setup_cb_t *cb, void *cb_ident)
{
struct tcf_block_cb *block_cb;
block_cb = tcf_block_cb_lookup(block, cb, cb_ident);
if (!block_cb)
return;
__tcf_block_cb_unregister(block_cb);
}
EXPORT_SYMBOL(tcf_block_cb_unregister);
static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type,
void *type_data, bool err_stop)
{
struct tcf_block_cb *block_cb;
int ok_count = 0;
int err;
list_for_each_entry(block_cb, &block->cb_list, list) {
err = block_cb->cb(type, type_data, block_cb->cb_priv);
if (err) {
if (err_stop)
return err;
} else {
ok_count++;
}
}
return ok_count;
}
/* Main classifier routine: scans classifier chain attached
* to this qdisc, (optionally) tests for protocol and asks
* specific classifiers.
@ -1051,10 +1206,25 @@ static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts,
return ok_count;
}
int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type,
void *type_data, bool err_stop)
int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts,
enum tc_setup_type type, void *type_data, bool err_stop)
{
return tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop);
int ok_count;
int ret;
ret = tcf_block_cb_call(block, type, type_data, err_stop);
if (ret < 0)
return ret;
ok_count = ret;
if (!exts)
return ok_count;
ret = tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop);
if (ret < 0)
return ret;
ok_count += ret;
return ok_count;
}
EXPORT_SYMBOL(tc_setup_cb_call);

View File

@ -147,7 +147,9 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog)
static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
enum tc_clsbpf_command cmd)
{
struct net_device *dev = tp->q->dev_queue->dev;
bool addorrep = cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE;
struct tcf_block *block = tp->chain->block;
bool skip_sw = tc_skip_sw(prog->gen_flags);
struct tc_cls_bpf_offload cls_bpf = {};
int err;
@ -159,17 +161,25 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
cls_bpf.exts_integrated = prog->exts_integrated;
cls_bpf.gen_flags = prog->gen_flags;
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &cls_bpf);
if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE))
prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
if (addorrep) {
if (err < 0) {
cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_DESTROY);
return err;
} else if (err > 0) {
prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;
}
}
return err;
if (addorrep && skip_sw && !(prog->gen_flags && TCA_CLS_FLAGS_IN_HW))
return -EINVAL;
return 0;
}
static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
struct cls_bpf_prog *oldprog)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct cls_bpf_prog *obj = prog;
enum tc_clsbpf_command cmd;
bool skip_sw;
@ -179,7 +189,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
(oldprog && tc_skip_sw(oldprog->gen_flags));
if (oldprog && oldprog->offloaded) {
if (tc_should_offload(dev, prog->gen_flags)) {
if (!tc_skip_hw(prog->gen_flags)) {
cmd = TC_CLSBPF_REPLACE;
} else if (!tc_skip_sw(prog->gen_flags)) {
obj = oldprog;
@ -188,14 +198,14 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
return -EINVAL;
}
} else {
if (!tc_should_offload(dev, prog->gen_flags))
if (tc_skip_hw(prog->gen_flags))
return skip_sw ? -EINVAL : 0;
cmd = TC_CLSBPF_ADD;
}
ret = cls_bpf_offload_cmd(tp, obj, cmd);
if (ret)
return skip_sw ? ret : 0;
return ret;
obj->offloaded = true;
if (oldprog)

View File

@ -200,16 +200,13 @@ static void fl_destroy_filter(struct rcu_head *head)
static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f)
{
struct tc_cls_flower_offload cls_flower = {};
struct net_device *dev = tp->q->dev_queue->dev;
struct tcf_block *block = tp->chain->block;
tc_cls_common_offload_init(&cls_flower.common, tp);
cls_flower.command = TC_CLSFLOWER_DESTROY;
cls_flower.cookie = (unsigned long) f;
if (tc_can_offload(dev))
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER,
&cls_flower);
tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER,
tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
&cls_flower, false);
}
@ -218,8 +215,8 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
struct fl_flow_key *mask,
struct cls_fl_filter *f)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_flower_offload cls_flower = {};
struct tcf_block *block = tp->chain->block;
bool skip_sw = tc_skip_sw(f->flags);
int err;
@ -231,18 +228,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
cls_flower.key = &f->mkey;
cls_flower.exts = &f->exts;
if (tc_can_offload(dev)) {
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER,
&cls_flower);
if (err) {
if (skip_sw)
return err;
} else {
f->flags |= TCA_CLS_FLAGS_IN_HW;
}
}
err = tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER,
err = tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
&cls_flower, skip_sw);
if (err < 0) {
fl_hw_destroy_filter(tp, f);
@ -260,17 +246,14 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
{
struct tc_cls_flower_offload cls_flower = {};
struct net_device *dev = tp->q->dev_queue->dev;
struct tcf_block *block = tp->chain->block;
tc_cls_common_offload_init(&cls_flower.common, tp);
cls_flower.command = TC_CLSFLOWER_STATS;
cls_flower.cookie = (unsigned long) f;
cls_flower.exts = &f->exts;
if (tc_can_offload(dev))
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER,
&cls_flower);
tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER,
tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER,
&cls_flower, false);
}

View File

@ -50,12 +50,27 @@ static void mall_destroy_rcu(struct rcu_head *rcu)
kfree(head);
}
static void mall_destroy_hw_filter(struct tcf_proto *tp,
struct cls_mall_head *head,
unsigned long cookie)
{
struct tc_cls_matchall_offload cls_mall = {};
struct tcf_block *block = tp->chain->block;
tc_cls_common_offload_init(&cls_mall.common, tp);
cls_mall.command = TC_CLSMATCHALL_DESTROY;
cls_mall.cookie = cookie;
tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL, &cls_mall, false);
}
static int mall_replace_hw_filter(struct tcf_proto *tp,
struct cls_mall_head *head,
unsigned long cookie)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_matchall_offload cls_mall = {};
struct tcf_block *block = tp->chain->block;
bool skip_sw = tc_skip_sw(head->flags);
int err;
tc_cls_common_offload_init(&cls_mall.common, tp);
@ -63,37 +78,29 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
cls_mall.exts = &head->exts;
cls_mall.cookie = cookie;
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL,
&cls_mall);
if (!err)
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL,
&cls_mall, skip_sw);
if (err < 0) {
mall_destroy_hw_filter(tp, head, cookie);
return err;
} else if (err > 0) {
head->flags |= TCA_CLS_FLAGS_IN_HW;
}
return err;
}
if (skip_sw && !(head->flags & TCA_CLS_FLAGS_IN_HW))
return -EINVAL;
static void mall_destroy_hw_filter(struct tcf_proto *tp,
struct cls_mall_head *head,
unsigned long cookie)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_matchall_offload cls_mall = {};
tc_cls_common_offload_init(&cls_mall.common, tp);
cls_mall.command = TC_CLSMATCHALL_DESTROY;
cls_mall.cookie = cookie;
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &cls_mall);
return 0;
}
static void mall_destroy(struct tcf_proto *tp)
{
struct cls_mall_head *head = rtnl_dereference(tp->root);
struct net_device *dev = tp->q->dev_queue->dev;
if (!head)
return;
if (tc_should_offload(dev, head->flags))
if (!tc_skip_hw(head->flags))
mall_destroy_hw_filter(tp, head, (unsigned long) head);
call_rcu(&head->rcu, mall_destroy_rcu);
@ -133,7 +140,6 @@ static int mall_change(struct net *net, struct sk_buff *in_skb,
void **arg, bool ovr)
{
struct cls_mall_head *head = rtnl_dereference(tp->root);
struct net_device *dev = tp->q->dev_queue->dev;
struct nlattr *tb[TCA_MATCHALL_MAX + 1];
struct cls_mall_head *new;
u32 flags = 0;
@ -173,14 +179,10 @@ static int mall_change(struct net *net, struct sk_buff *in_skb,
if (err)
goto err_set_parms;
if (tc_should_offload(dev, flags)) {
if (!tc_skip_hw(new->flags)) {
err = mall_replace_hw_filter(tp, new, (unsigned long) new);
if (err) {
if (tc_skip_sw(flags))
goto err_replace_hw_filter;
else
err = 0;
}
if (err)
goto err_replace_hw_filter;
}
if (!tc_in_hw(new->flags))

View File

@ -462,71 +462,69 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
return 0;
}
static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload cls_u32 = {};
if (!tc_should_offload(dev, 0))
return;
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_DELETE_KNODE;
cls_u32.knode.handle = handle;
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32);
}
static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
u32 flags)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tc_cls_u32_offload cls_u32 = {};
int err;
if (!tc_should_offload(dev, flags))
return tc_skip_sw(flags) ? -EINVAL : 0;
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_NEW_HNODE;
cls_u32.hnode.divisor = h->divisor;
cls_u32.hnode.handle = h->handle;
cls_u32.hnode.prio = h->prio;
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32);
if (tc_skip_sw(flags))
return err;
return 0;
}
static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tcf_block *block = tp->chain->block;
struct tc_cls_u32_offload cls_u32 = {};
if (!tc_should_offload(dev, 0))
return;
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_DELETE_HNODE;
cls_u32.hnode.divisor = h->divisor;
cls_u32.hnode.handle = h->handle;
cls_u32.hnode.prio = h->prio;
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32);
tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, false);
}
static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
u32 flags)
{
struct tcf_block *block = tp->chain->block;
struct tc_cls_u32_offload cls_u32 = {};
bool skip_sw = tc_skip_sw(flags);
bool offloaded = false;
int err;
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_NEW_HNODE;
cls_u32.hnode.divisor = h->divisor;
cls_u32.hnode.handle = h->handle;
cls_u32.hnode.prio = h->prio;
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
if (err < 0) {
u32_clear_hw_hnode(tp, h);
return err;
} else if (err > 0) {
offloaded = true;
}
if (skip_sw && !offloaded)
return -EINVAL;
return 0;
}
static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle)
{
struct tcf_block *block = tp->chain->block;
struct tc_cls_u32_offload cls_u32 = {};
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_DELETE_KNODE;
cls_u32.knode.handle = handle;
tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, false);
}
static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
u32 flags)
{
struct net_device *dev = tp->q->dev_queue->dev;
struct tcf_block *block = tp->chain->block;
struct tc_cls_u32_offload cls_u32 = {};
bool skip_sw = tc_skip_sw(flags);
int err;
if (!tc_should_offload(dev, flags))
return tc_skip_sw(flags) ? -EINVAL : 0;
tc_cls_common_offload_init(&cls_u32.common, tp);
cls_u32.command = TC_CLSU32_REPLACE_KNODE;
cls_u32.knode.handle = n->handle;
@ -543,13 +541,16 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
if (n->ht_down)
cls_u32.knode.link_handle = n->ht_down->handle;
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32);
if (!err)
n->flags |= TCA_CLS_FLAGS_IN_HW;
if (tc_skip_sw(flags))
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSU32, &cls_u32, skip_sw);
if (err < 0) {
u32_remove_hw_knode(tp, n->handle);
return err;
} else if (err > 0) {
n->flags |= TCA_CLS_FLAGS_IN_HW;
}
if (skip_sw && !(n->flags && TCA_CLS_FLAGS_IN_HW))
return -EINVAL;
return 0;
}

View File

@ -20,6 +20,7 @@
struct ingress_sched_data {
struct tcf_block *block;
struct tcf_block_ext_info block_info;
};
static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
@ -59,7 +60,10 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt)
struct net_device *dev = qdisc_dev(sch);
int err;
err = tcf_block_get(&q->block, &dev->ingress_cl_list, sch);
q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
err = tcf_block_get_ext(&q->block, &dev->ingress_cl_list,
sch, &q->block_info);
if (err)
return err;
@ -72,8 +76,10 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt)
static void ingress_destroy(struct Qdisc *sch)
{
struct ingress_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
tcf_block_put(q->block);
tcf_block_put_ext(q->block, &dev->ingress_cl_list,
sch, &q->block_info);
net_dec_ingress_queue();
}
@ -114,6 +120,8 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
struct clsact_sched_data {
struct tcf_block *ingress_block;
struct tcf_block *egress_block;
struct tcf_block_ext_info ingress_block_info;
struct tcf_block_ext_info egress_block_info;
};
static unsigned long clsact_find(struct Qdisc *sch, u32 classid)
@ -153,13 +161,19 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt)
struct net_device *dev = qdisc_dev(sch);
int err;
err = tcf_block_get(&q->ingress_block, &dev->ingress_cl_list, sch);
q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
err = tcf_block_get_ext(&q->ingress_block, &dev->ingress_cl_list,
sch, &q->ingress_block_info);
if (err)
return err;
err = tcf_block_get(&q->egress_block, &dev->egress_cl_list, sch);
q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS;
err = tcf_block_get_ext(&q->egress_block, &dev->egress_cl_list,
sch, &q->egress_block_info);
if (err)
return err;
goto err_egress_block_get;
net_inc_ingress_queue();
net_inc_egress_queue();
@ -167,14 +181,22 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt)
sch->flags |= TCQ_F_CPUSTATS;
return 0;
err_egress_block_get:
tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list,
sch, &q->ingress_block_info);
return err;
}
static void clsact_destroy(struct Qdisc *sch)
{
struct clsact_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
tcf_block_put(q->egress_block);
tcf_block_put(q->ingress_block);
tcf_block_put_ext(q->egress_block, &dev->egress_cl_list,
sch, &q->egress_block_info);
tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list,
sch, &q->ingress_block_info);
net_dec_ingress_queue();
net_dec_egress_queue();