nfp: flower: allow offloading of matches on 'internal' ports

Recent FW modifications allow the offloading of non repr ports. These
ports exist internally on the NFP. So if a rule outputs to an 'internal'
port, then the packet will recirculate back into the system but will now
have this internal port as it's incoming port. These ports are indicated
by a specific type field combined with an 8 bit port id.

Add private app data to assign additional port ids for use in offloads.
Provide functions to lookup or create new ids when a rule attempts to
match on an internal netdev - the only internal netdevs currently
supported are of type openvswitch. Have a netdev notifier to release
port ids on netdev unregister.

OvS offloads rules that match on internal ports as TC egress filters.
Ensure that such rules are accepted by the driver.

Signed-off-by: John Hurley <john.hurley@netronome.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
John Hurley 2019-04-15 16:55:54 +02:00 committed by David S. Miller
parent 2f2622f59c
commit 4d12ba4278
5 changed files with 153 additions and 9 deletions

View File

@ -474,6 +474,13 @@ enum nfp_flower_cmsg_port_vnic_type {
#define NFP_FLOWER_CMSG_PORT_PCIE_Q GENMASK(5, 0) #define NFP_FLOWER_CMSG_PORT_PCIE_Q GENMASK(5, 0)
#define NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM GENMASK(7, 0) #define NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM GENMASK(7, 0)
static inline u32 nfp_flower_internal_port_get_port_id(u8 internal_port)
{
return FIELD_PREP(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM, internal_port) |
FIELD_PREP(NFP_FLOWER_CMSG_PORT_TYPE,
NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT);
}
static inline u32 nfp_flower_cmsg_phys_port(u8 phys_port) static inline u32 nfp_flower_cmsg_phys_port(u8 phys_port)
{ {
return FIELD_PREP(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM, phys_port) | return FIELD_PREP(NFP_FLOWER_CMSG_PORT_PHYS_PORT_NUM, phys_port) |

View File

@ -22,6 +22,9 @@
#define NFP_FLOWER_ALLOWED_VER 0x0001000000010000UL #define NFP_FLOWER_ALLOWED_VER 0x0001000000010000UL
#define NFP_MIN_INT_PORT_ID 1
#define NFP_MAX_INT_PORT_ID 256
static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn) static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn)
{ {
return "FLOWER"; return "FLOWER";
@ -32,6 +35,100 @@ static enum devlink_eswitch_mode eswitch_mode_get(struct nfp_app *app)
return DEVLINK_ESWITCH_MODE_SWITCHDEV; return DEVLINK_ESWITCH_MODE_SWITCHDEV;
} }
static int
nfp_flower_lookup_internal_port_id(struct nfp_flower_priv *priv,
struct net_device *netdev)
{
struct net_device *entry;
int i, id = 0;
rcu_read_lock();
idr_for_each_entry(&priv->internal_ports.port_ids, entry, i)
if (entry == netdev) {
id = i;
break;
}
rcu_read_unlock();
return id;
}
static int
nfp_flower_get_internal_port_id(struct nfp_app *app, struct net_device *netdev)
{
struct nfp_flower_priv *priv = app->priv;
int id;
id = nfp_flower_lookup_internal_port_id(priv, netdev);
if (id > 0)
return id;
idr_preload(GFP_ATOMIC);
spin_lock_bh(&priv->internal_ports.lock);
id = idr_alloc(&priv->internal_ports.port_ids, netdev,
NFP_MIN_INT_PORT_ID, NFP_MAX_INT_PORT_ID, GFP_ATOMIC);
spin_unlock_bh(&priv->internal_ports.lock);
idr_preload_end();
return id;
}
u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app,
struct net_device *netdev)
{
int ext_port;
if (nfp_netdev_is_nfp_repr(netdev)) {
return nfp_repr_get_port_id(netdev);
} else if (nfp_flower_internal_port_can_offload(app, netdev)) {
ext_port = nfp_flower_get_internal_port_id(app, netdev);
if (ext_port < 0)
return 0;
return nfp_flower_internal_port_get_port_id(ext_port);
}
return 0;
}
static void
nfp_flower_free_internal_port_id(struct nfp_app *app, struct net_device *netdev)
{
struct nfp_flower_priv *priv = app->priv;
int id;
id = nfp_flower_lookup_internal_port_id(priv, netdev);
if (!id)
return;
spin_lock_bh(&priv->internal_ports.lock);
idr_remove(&priv->internal_ports.port_ids, id);
spin_unlock_bh(&priv->internal_ports.lock);
}
static int
nfp_flower_internal_port_event_handler(struct nfp_app *app,
struct net_device *netdev,
unsigned long event)
{
if (event == NETDEV_UNREGISTER &&
nfp_flower_internal_port_can_offload(app, netdev))
nfp_flower_free_internal_port_id(app, netdev);
return NOTIFY_OK;
}
static void nfp_flower_internal_port_init(struct nfp_flower_priv *priv)
{
spin_lock_init(&priv->internal_ports.lock);
idr_init(&priv->internal_ports.port_ids);
}
static void nfp_flower_internal_port_cleanup(struct nfp_flower_priv *priv)
{
idr_destroy(&priv->internal_ports.port_ids);
}
static struct nfp_flower_non_repr_priv * static struct nfp_flower_non_repr_priv *
nfp_flower_non_repr_priv_lookup(struct nfp_app *app, struct net_device *netdev) nfp_flower_non_repr_priv_lookup(struct nfp_app *app, struct net_device *netdev)
{ {
@ -645,12 +742,14 @@ static int nfp_flower_init(struct nfp_app *app)
/* Tell the firmware that the driver supports flow merging. */ /* Tell the firmware that the driver supports flow merging. */
err = nfp_rtsym_write_le(app->pf->rtbl, err = nfp_rtsym_write_le(app->pf->rtbl,
"_abi_flower_merge_hint_enable", 1); "_abi_flower_merge_hint_enable", 1);
if (!err) if (!err) {
app_priv->flower_ext_feats |= NFP_FL_FEATS_FLOW_MERGE; app_priv->flower_ext_feats |= NFP_FL_FEATS_FLOW_MERGE;
else if (err == -ENOENT) nfp_flower_internal_port_init(app_priv);
} else if (err == -ENOENT) {
nfp_warn(app->cpp, "Flow merge not supported by FW.\n"); nfp_warn(app->cpp, "Flow merge not supported by FW.\n");
else } else {
goto err_lag_clean; goto err_lag_clean;
}
} else { } else {
nfp_warn(app->cpp, "Flow mod/merge not supported by FW.\n"); nfp_warn(app->cpp, "Flow mod/merge not supported by FW.\n");
} }
@ -681,6 +780,9 @@ static void nfp_flower_clean(struct nfp_app *app)
if (app_priv->flower_ext_feats & NFP_FL_FEATS_LAG) if (app_priv->flower_ext_feats & NFP_FL_FEATS_LAG)
nfp_flower_lag_cleanup(&app_priv->nfp_lag); nfp_flower_lag_cleanup(&app_priv->nfp_lag);
if (app_priv->flower_ext_feats & NFP_FL_FEATS_FLOW_MERGE)
nfp_flower_internal_port_cleanup(app_priv);
nfp_flower_metadata_cleanup(app); nfp_flower_metadata_cleanup(app);
vfree(app->priv); vfree(app->priv);
app->priv = NULL; app->priv = NULL;
@ -779,6 +881,10 @@ nfp_flower_netdev_event(struct nfp_app *app, struct net_device *netdev,
if (ret & NOTIFY_STOP_MASK) if (ret & NOTIFY_STOP_MASK)
return ret; return ret;
ret = nfp_flower_internal_port_event_handler(app, netdev, event);
if (ret & NOTIFY_STOP_MASK)
return ret;
return nfp_tunnel_mac_event_handler(app, netdev, event, ptr); return nfp_tunnel_mac_event_handler(app, netdev, event, ptr);
} }

View File

@ -116,6 +116,16 @@ struct nfp_fl_lag {
struct sk_buff_head retrans_skbs; struct sk_buff_head retrans_skbs;
}; };
/**
* struct nfp_fl_internal_ports - Flower APP priv data for additional ports
* @port_ids: Assignment of ids to any additional ports
* @lock: Lock for extra ports list
*/
struct nfp_fl_internal_ports {
struct idr port_ids;
spinlock_t lock;
};
/** /**
* struct nfp_flower_priv - Flower APP per-vNIC priv data * struct nfp_flower_priv - Flower APP per-vNIC priv data
* @app: Back pointer to app * @app: Back pointer to app
@ -145,6 +155,7 @@ struct nfp_fl_lag {
* @non_repr_priv: List of offloaded non-repr ports and their priv data * @non_repr_priv: List of offloaded non-repr ports and their priv data
* @active_mem_unit: Current active memory unit for flower rules * @active_mem_unit: Current active memory unit for flower rules
* @total_mem_units: Total number of available memory units for flower rules * @total_mem_units: Total number of available memory units for flower rules
* @internal_ports: Internal port ids used in offloaded rules
*/ */
struct nfp_flower_priv { struct nfp_flower_priv {
struct nfp_app *app; struct nfp_app *app;
@ -171,6 +182,7 @@ struct nfp_flower_priv {
struct list_head non_repr_priv; struct list_head non_repr_priv;
unsigned int active_mem_unit; unsigned int active_mem_unit;
unsigned int total_mem_units; unsigned int total_mem_units;
struct nfp_fl_internal_ports internal_ports;
}; };
/** /**
@ -249,6 +261,22 @@ struct nfp_fl_stats_frame {
__be64 stats_cookie; __be64 stats_cookie;
}; };
static inline bool
nfp_flower_internal_port_can_offload(struct nfp_app *app,
struct net_device *netdev)
{
struct nfp_flower_priv *app_priv = app->priv;
if (!(app_priv->flower_ext_feats & NFP_FL_FEATS_FLOW_MERGE))
return false;
if (!netdev->rtnl_link_ops)
return false;
if (!strcmp(netdev->rtnl_link_ops->kind, "openvswitch"))
return true;
return false;
}
int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count, int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
unsigned int host_ctx_split); unsigned int host_ctx_split);
void nfp_flower_metadata_cleanup(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app);
@ -313,4 +341,6 @@ void
__nfp_flower_non_repr_priv_put(struct nfp_flower_non_repr_priv *non_repr_priv); __nfp_flower_non_repr_priv_put(struct nfp_flower_non_repr_priv *non_repr_priv);
void void
nfp_flower_non_repr_priv_put(struct nfp_app *app, struct net_device *netdev); nfp_flower_non_repr_priv_put(struct nfp_app *app, struct net_device *netdev);
u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app,
struct net_device *netdev);
#endif #endif

View File

@ -326,13 +326,12 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
struct nfp_fl_payload *nfp_flow, struct nfp_fl_payload *nfp_flow,
enum nfp_flower_tun_type tun_type) enum nfp_flower_tun_type tun_type)
{ {
u32 cmsg_port = 0; u32 port_id;
int err; int err;
u8 *ext; u8 *ext;
u8 *msk; u8 *msk;
if (nfp_netdev_is_nfp_repr(netdev)) port_id = nfp_flower_get_port_id_from_netdev(app, netdev);
cmsg_port = nfp_repr_get_port_id(netdev);
memset(nfp_flow->unmasked_data, 0, key_ls->key_size); memset(nfp_flow->unmasked_data, 0, key_ls->key_size);
memset(nfp_flow->mask_data, 0, key_ls->key_size); memset(nfp_flow->mask_data, 0, key_ls->key_size);
@ -358,13 +357,13 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
/* Populate Exact Port data. */ /* Populate Exact Port data. */
err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext, err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext,
cmsg_port, false, tun_type); port_id, false, tun_type);
if (err) if (err)
return err; return err;
/* Populate Mask Port Data. */ /* Populate Mask Port Data. */
err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk, err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk,
cmsg_port, true, tun_type); port_id, true, tun_type);
if (err) if (err)
return err; return err;

View File

@ -682,7 +682,9 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app,
struct nfp_flower_priv *priv = app->priv; struct nfp_flower_priv *priv = app->priv;
int err; int err;
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
!(f->binder_type == TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS &&
nfp_flower_internal_port_can_offload(app, netdev)))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (f->command) { switch (f->command) {