summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Cree <ecree.xilinx@gmail.com>2023-08-07 15:48:05 +0200
committerDavid S. Miller <davem@davemloft.net>2023-08-09 12:14:37 +0200
commit3bf969e88ada7265ed6ef6f4b52a0c7d1d35c0b6 (patch)
tree58bd5ff4b90c339a4574e97846f1dc4bbb5b1428
parentMerge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/... (diff)
downloadlinux-3bf969e88ada7265ed6ef6f4b52a0c7d1d35c0b6.tar.xz
linux-3bf969e88ada7265ed6ef6f4b52a0c7d1d35c0b6.zip
sfc: add MAE table machinery for conntrack table
Access to the connection tracking table in EF100 hardware is through a "generic" table mechanism, whereby a firmware call at probe time gives the driver a description of the field widths and offsets, so that the driver can then construct key and response bitstrings at runtime. Probe the NIC for this information and populate the needed metadata into a new meta_ct field of struct efx_tc_state. Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Edward Cree <ecree.xilinx@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/sfc/mae.c250
-rw-r--r--drivers/net/ethernet/sfc/mae.h3
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h3
-rw-r--r--drivers/net/ethernet/sfc/tc.c9
-rw-r--r--drivers/net/ethernet/sfc/tc.h44
5 files changed, 308 insertions, 1 deletions
diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c
index 0cab508f2f9d..33ae2c852b44 100644
--- a/drivers/net/ethernet/sfc/mae.c
+++ b/drivers/net/ethernet/sfc/mae.c
@@ -227,6 +227,256 @@ void efx_mae_counters_grant_credits(struct work_struct *work)
rx_queue->granted_count += credits;
}
+static int efx_mae_table_get_desc(struct efx_nic *efx,
+ struct efx_tc_table_desc *desc,
+ u32 table_id)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(16));
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_TABLE_DESCRIPTOR_IN_LEN);
+ unsigned int offset = 0, i;
+ size_t outlen;
+ int rc;
+
+ memset(desc, 0, sizeof(*desc));
+
+ MCDI_SET_DWORD(inbuf, TABLE_DESCRIPTOR_IN_TABLE_ID, table_id);
+more:
+ MCDI_SET_DWORD(inbuf, TABLE_DESCRIPTOR_IN_FIRST_FIELDS_INDEX, offset);
+ rc = efx_mcdi_rpc(efx, MC_CMD_TABLE_DESCRIPTOR, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+ if (outlen < MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(1)) {
+ rc = -EIO;
+ goto fail;
+ }
+ if (!offset) { /* first iteration: get metadata */
+ desc->type = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_TYPE);
+ desc->key_width = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_KEY_WIDTH);
+ desc->resp_width = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_RESP_WIDTH);
+ desc->n_keys = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_KEY_FIELDS);
+ desc->n_resps = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_RESP_FIELDS);
+ desc->n_prios = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_PRIORITIES);
+ desc->flags = MCDI_BYTE(outbuf, TABLE_DESCRIPTOR_OUT_FLAGS);
+ rc = -EOPNOTSUPP;
+ if (desc->flags)
+ goto fail;
+ desc->scheme = MCDI_BYTE(outbuf, TABLE_DESCRIPTOR_OUT_SCHEME);
+ if (desc->scheme)
+ goto fail;
+ rc = -ENOMEM;
+ desc->keys = kcalloc(desc->n_keys,
+ sizeof(struct efx_tc_table_field_fmt),
+ GFP_KERNEL);
+ if (!desc->keys)
+ goto fail;
+ desc->resps = kcalloc(desc->n_resps,
+ sizeof(struct efx_tc_table_field_fmt),
+ GFP_KERNEL);
+ if (!desc->resps)
+ goto fail;
+ }
+ /* FW could have returned more than the 16 field_descrs we
+ * made room for in our outbuf
+ */
+ outlen = min(outlen, sizeof(outbuf));
+ for (i = 0; i + offset < desc->n_keys + desc->n_resps; i++) {
+ struct efx_tc_table_field_fmt *field;
+ MCDI_DECLARE_STRUCT_PTR(fdesc);
+
+ if (outlen < MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(i + 1)) {
+ offset += i;
+ goto more;
+ }
+ if (i + offset < desc->n_keys)
+ field = desc->keys + i + offset;
+ else
+ field = desc->resps + (i + offset - desc->n_keys);
+ fdesc = MCDI_ARRAY_STRUCT_PTR(outbuf,
+ TABLE_DESCRIPTOR_OUT_FIELDS, i);
+ field->field_id = MCDI_STRUCT_WORD(fdesc,
+ TABLE_FIELD_DESCR_FIELD_ID);
+ field->lbn = MCDI_STRUCT_WORD(fdesc, TABLE_FIELD_DESCR_LBN);
+ field->width = MCDI_STRUCT_WORD(fdesc, TABLE_FIELD_DESCR_WIDTH);
+ field->masking = MCDI_STRUCT_BYTE(fdesc, TABLE_FIELD_DESCR_MASK_TYPE);
+ field->scheme = MCDI_STRUCT_BYTE(fdesc, TABLE_FIELD_DESCR_SCHEME);
+ }
+ return 0;
+
+fail:
+ kfree(desc->keys);
+ kfree(desc->resps);
+ return rc;
+}
+
+static int efx_mae_table_hook_find(u16 n_fields,
+ struct efx_tc_table_field_fmt *fields,
+ u16 field_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < n_fields; i++) {
+ if (fields[i].field_id == field_id)
+ return i;
+ }
+ return -EPROTO;
+}
+
+#define TABLE_FIND_KEY(_desc, _id) \
+ efx_mae_table_hook_find((_desc)->n_keys, (_desc)->keys, _id)
+#define TABLE_FIND_RESP(_desc, _id) \
+ efx_mae_table_hook_find((_desc)->n_resps, (_desc)->resps, _id)
+
+#define TABLE_HOOK_KEY(_meta, _name, _mcdi_name) ({ \
+ int _rc = TABLE_FIND_KEY(&_meta->desc, TABLE_FIELD_ID_##_mcdi_name); \
+ \
+ if (_rc > U8_MAX) \
+ _rc = -EOPNOTSUPP; \
+ if (_rc >= 0) { \
+ _meta->keys._name##_idx = _rc; \
+ _rc = 0; \
+ } \
+ _rc; \
+})
+#define TABLE_HOOK_RESP(_meta, _name, _mcdi_name) ({ \
+ int _rc = TABLE_FIND_RESP(&_meta->desc, TABLE_FIELD_ID_##_mcdi_name); \
+ \
+ if (_rc > U8_MAX) \
+ _rc = -EOPNOTSUPP; \
+ if (_rc >= 0) { \
+ _meta->resps._name##_idx = _rc; \
+ _rc = 0; \
+ } \
+ _rc; \
+})
+
+static int efx_mae_table_hook_ct(struct efx_nic *efx,
+ struct efx_tc_table_ct *meta_ct)
+{
+ int rc;
+
+ rc = TABLE_HOOK_KEY(meta_ct, eth_proto, ETHER_TYPE);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, ip_proto, IP_PROTO);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, src_ip, SRC_IP);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, dst_ip, DST_IP);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, l4_sport, SRC_PORT);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, l4_dport, DST_PORT);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_KEY(meta_ct, zone, DOMAIN);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_RESP(meta_ct, dnat, NAT_DIR);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_RESP(meta_ct, nat_ip, NAT_IP);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_RESP(meta_ct, l4_natport, NAT_PORT);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_RESP(meta_ct, mark, CT_MARK);
+ if (rc)
+ return rc;
+ rc = TABLE_HOOK_RESP(meta_ct, counter_id, COUNTER_ID);
+ if (rc)
+ return rc;
+ meta_ct->hooked = true;
+ return 0;
+}
+
+static void efx_mae_table_free_desc(struct efx_tc_table_desc *desc)
+{
+ kfree(desc->keys);
+ kfree(desc->resps);
+ memset(desc, 0, sizeof(*desc));
+}
+
+static bool efx_mae_check_table_exists(struct efx_nic *efx, u32 tbl_req)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_TABLE_LIST_OUT_LEN(16));
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_TABLE_LIST_IN_LEN);
+ u32 tbl_id, tbl_total, tbl_cnt, pos = 0;
+ size_t outlen, msg_max;
+ bool ct_tbl = false;
+ int rc, idx;
+
+ msg_max = sizeof(outbuf);
+ efx->tc->meta_ct.hooked = false;
+more:
+ memset(outbuf, 0, sizeof(*outbuf));
+ MCDI_SET_DWORD(inbuf, TABLE_LIST_IN_FIRST_TABLE_ID_INDEX, pos);
+ rc = efx_mcdi_rpc(efx, MC_CMD_TABLE_LIST, inbuf, sizeof(inbuf), outbuf,
+ msg_max, &outlen);
+ if (rc)
+ return false;
+
+ if (outlen < MC_CMD_TABLE_LIST_OUT_LEN(1))
+ return false;
+
+ tbl_total = MCDI_DWORD(outbuf, TABLE_LIST_OUT_N_TABLES);
+ tbl_cnt = MC_CMD_TABLE_LIST_OUT_TABLE_ID_NUM(min(outlen, msg_max));
+
+ for (idx = 0; idx < tbl_cnt; idx++) {
+ tbl_id = MCDI_ARRAY_DWORD(outbuf, TABLE_LIST_OUT_TABLE_ID, idx);
+ if (tbl_id == tbl_req) {
+ ct_tbl = true;
+ break;
+ }
+ }
+
+ pos += tbl_cnt;
+ if (!ct_tbl && pos < tbl_total)
+ goto more;
+
+ return ct_tbl;
+}
+
+int efx_mae_get_tables(struct efx_nic *efx)
+{
+ int rc;
+
+ efx->tc->meta_ct.hooked = false;
+ if (efx_mae_check_table_exists(efx, TABLE_ID_CONNTRACK_TABLE)) {
+ rc = efx_mae_table_get_desc(efx, &efx->tc->meta_ct.desc,
+ TABLE_ID_CONNTRACK_TABLE);
+ if (rc) {
+ pci_info(efx->pci_dev,
+ "FW does not support conntrack desc rc %d\n",
+ rc);
+ return 0;
+ }
+
+ rc = efx_mae_table_hook_ct(efx, &efx->tc->meta_ct);
+ if (rc) {
+ pci_info(efx->pci_dev,
+ "FW does not support conntrack hook rc %d\n",
+ rc);
+ return 0;
+ }
+ } else {
+ pci_info(efx->pci_dev,
+ "FW does not support conntrack table\n");
+ }
+ return 0;
+}
+
+void efx_mae_free_tables(struct efx_nic *efx)
+{
+ efx_mae_table_free_desc(&efx->tc->meta_ct.desc);
+ efx->tc->meta_ct.hooked = false;
+}
+
static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h
index 24abfe509690..afdf738254b2 100644
--- a/drivers/net/ethernet/sfc/mae.h
+++ b/drivers/net/ethernet/sfc/mae.h
@@ -66,6 +66,9 @@ int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
void efx_mae_counters_grant_credits(struct work_struct *work);
+int efx_mae_get_tables(struct efx_nic *efx);
+void efx_mae_free_tables(struct efx_nic *efx);
+
#define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1)
struct mae_caps {
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index 454e9d51a4c2..995a26686fd8 100644
--- a/drivers/net/ethernet/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
@@ -221,6 +221,9 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
#define MCDI_BYTE(_buf, _field) \
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
*MCDI_PTR(_buf, _field))
+#define MCDI_STRUCT_BYTE(_buf, _field) \
+ ((void)BUILD_BUG_ON_ZERO(_field ## _LEN != 1), \
+ *MCDI_STRUCT_PTR(_buf, _field))
#define MCDI_SET_WORD(_buf, _field, _value) do { \
BUILD_BUG_ON(MC_CMD_ ## _field ## _LEN != 2); \
BUILD_BUG_ON(MC_CMD_ ## _field ## _OFST & 1); \
diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c
index 4dc881159246..4dc979fdc968 100644
--- a/drivers/net/ethernet/sfc/tc.c
+++ b/drivers/net/ethernet/sfc/tc.c
@@ -1660,11 +1660,17 @@ int efx_init_tc(struct efx_nic *efx)
rc = efx_tc_configure_fallback_acts_reps(efx);
if (rc)
return rc;
+ rc = efx_mae_get_tables(efx);
+ if (rc)
+ return rc;
efx->tc->up = true;
rc = flow_indr_dev_register(efx_tc_indr_setup_cb, efx);
if (rc)
- return rc;
+ goto out_free;
return 0;
+out_free:
+ efx_mae_free_tables(efx);
+ return rc;
}
void efx_fini_tc(struct efx_nic *efx)
@@ -1680,6 +1686,7 @@ void efx_fini_tc(struct efx_nic *efx)
efx_tc_deconfigure_fallback_acts(efx, &efx->tc->facts.pf);
efx_tc_deconfigure_fallback_acts(efx, &efx->tc->facts.reps);
efx->tc->up = false;
+ efx_mae_free_tables(efx);
}
/* At teardown time, all TC filter rules (and thus all resources they created)
diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h
index 1549c3df43bb..27592f10b536 100644
--- a/drivers/net/ethernet/sfc/tc.h
+++ b/drivers/net/ethernet/sfc/tc.h
@@ -143,6 +143,48 @@ enum efx_tc_rule_prios {
EFX_TC_PRIO__NUM
};
+struct efx_tc_table_field_fmt {
+ u16 field_id;
+ u16 lbn;
+ u16 width;
+ u8 masking;
+ u8 scheme;
+};
+
+struct efx_tc_table_desc {
+ u16 type;
+ u16 key_width;
+ u16 resp_width;
+ u16 n_keys;
+ u16 n_resps;
+ u16 n_prios;
+ u8 flags;
+ u8 scheme;
+ struct efx_tc_table_field_fmt *keys;
+ struct efx_tc_table_field_fmt *resps;
+};
+
+struct efx_tc_table_ct { /* TABLE_ID_CONNTRACK_TABLE */
+ struct efx_tc_table_desc desc;
+ bool hooked;
+ struct { /* indices of named fields within @desc.keys */
+ u8 eth_proto_idx;
+ u8 ip_proto_idx;
+ u8 src_ip_idx; /* either v4 or v6 */
+ u8 dst_ip_idx;
+ u8 l4_sport_idx;
+ u8 l4_dport_idx;
+ u8 zone_idx; /* for TABLE_FIELD_ID_DOMAIN */
+ } keys;
+ struct { /* indices of named fields within @desc.resps */
+ u8 dnat_idx;
+ u8 nat_ip_idx;
+ u8 l4_natport_idx;
+ u8 mark_idx;
+ u8 counter_id_idx;
+ } resps;
+};
+
/**
* struct efx_tc_state - control plane data for TC offload
*
@@ -155,6 +197,7 @@ enum efx_tc_rule_prios {
* @encap_match_ht: Hashtable of TC encap matches
* @match_action_ht: Hashtable of TC match-action rules
* @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder)
+ * @meta_ct: MAE table layout for conntrack table
* @reps_mport_id: MAE port allocated for representor RX
* @reps_filter_uc: VNIC filter for representor unicast RX (promisc)
* @reps_filter_mc: VNIC filter for representor multicast RX (allmulti)
@@ -186,6 +229,7 @@ struct efx_tc_state {
struct rhashtable encap_match_ht;
struct rhashtable match_action_ht;
struct rhashtable neigh_ht;
+ struct efx_tc_table_ct meta_ct;
u32 reps_mport_id, reps_mport_vport_id;
s32 reps_filter_uc, reps_filter_mc;
bool flush_counters;