Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Edward Cree and committed by
David S. Miller
3bf969e8 1c2c8c35

+308 -1
+250
drivers/net/ethernet/sfc/mae.c
··· 227 227 rx_queue->granted_count += credits; 228 228 } 229 229 230 + static int efx_mae_table_get_desc(struct efx_nic *efx, 231 + struct efx_tc_table_desc *desc, 232 + u32 table_id) 233 + { 234 + MCDI_DECLARE_BUF(outbuf, MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(16)); 235 + MCDI_DECLARE_BUF(inbuf, MC_CMD_TABLE_DESCRIPTOR_IN_LEN); 236 + unsigned int offset = 0, i; 237 + size_t outlen; 238 + int rc; 239 + 240 + memset(desc, 0, sizeof(*desc)); 241 + 242 + MCDI_SET_DWORD(inbuf, TABLE_DESCRIPTOR_IN_TABLE_ID, table_id); 243 + more: 244 + MCDI_SET_DWORD(inbuf, TABLE_DESCRIPTOR_IN_FIRST_FIELDS_INDEX, offset); 245 + rc = efx_mcdi_rpc(efx, MC_CMD_TABLE_DESCRIPTOR, inbuf, sizeof(inbuf), 246 + outbuf, sizeof(outbuf), &outlen); 247 + if (rc) 248 + goto fail; 249 + if (outlen < MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(1)) { 250 + rc = -EIO; 251 + goto fail; 252 + } 253 + if (!offset) { /* first iteration: get metadata */ 254 + desc->type = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_TYPE); 255 + desc->key_width = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_KEY_WIDTH); 256 + desc->resp_width = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_RESP_WIDTH); 257 + desc->n_keys = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_KEY_FIELDS); 258 + desc->n_resps = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_RESP_FIELDS); 259 + desc->n_prios = MCDI_WORD(outbuf, TABLE_DESCRIPTOR_OUT_N_PRIORITIES); 260 + desc->flags = MCDI_BYTE(outbuf, TABLE_DESCRIPTOR_OUT_FLAGS); 261 + rc = -EOPNOTSUPP; 262 + if (desc->flags) 263 + goto fail; 264 + desc->scheme = MCDI_BYTE(outbuf, TABLE_DESCRIPTOR_OUT_SCHEME); 265 + if (desc->scheme) 266 + goto fail; 267 + rc = -ENOMEM; 268 + desc->keys = kcalloc(desc->n_keys, 269 + sizeof(struct efx_tc_table_field_fmt), 270 + GFP_KERNEL); 271 + if (!desc->keys) 272 + goto fail; 273 + desc->resps = kcalloc(desc->n_resps, 274 + sizeof(struct efx_tc_table_field_fmt), 275 + GFP_KERNEL); 276 + if (!desc->resps) 277 + goto fail; 278 + } 279 + /* FW could have returned more than the 16 field_descrs we 280 + * made room for in our outbuf 281 + */ 282 + outlen = min(outlen, sizeof(outbuf)); 283 + for (i = 0; i + offset < desc->n_keys + desc->n_resps; i++) { 284 + struct efx_tc_table_field_fmt *field; 285 + MCDI_DECLARE_STRUCT_PTR(fdesc); 286 + 287 + if (outlen < MC_CMD_TABLE_DESCRIPTOR_OUT_LEN(i + 1)) { 288 + offset += i; 289 + goto more; 290 + } 291 + if (i + offset < desc->n_keys) 292 + field = desc->keys + i + offset; 293 + else 294 + field = desc->resps + (i + offset - desc->n_keys); 295 + fdesc = MCDI_ARRAY_STRUCT_PTR(outbuf, 296 + TABLE_DESCRIPTOR_OUT_FIELDS, i); 297 + field->field_id = MCDI_STRUCT_WORD(fdesc, 298 + TABLE_FIELD_DESCR_FIELD_ID); 299 + field->lbn = MCDI_STRUCT_WORD(fdesc, TABLE_FIELD_DESCR_LBN); 300 + field->width = MCDI_STRUCT_WORD(fdesc, TABLE_FIELD_DESCR_WIDTH); 301 + field->masking = MCDI_STRUCT_BYTE(fdesc, TABLE_FIELD_DESCR_MASK_TYPE); 302 + field->scheme = MCDI_STRUCT_BYTE(fdesc, TABLE_FIELD_DESCR_SCHEME); 303 + } 304 + return 0; 305 + 306 + fail: 307 + kfree(desc->keys); 308 + kfree(desc->resps); 309 + return rc; 310 + } 311 + 312 + static int efx_mae_table_hook_find(u16 n_fields, 313 + struct efx_tc_table_field_fmt *fields, 314 + u16 field_id) 315 + { 316 + unsigned int i; 317 + 318 + for (i = 0; i < n_fields; i++) { 319 + if (fields[i].field_id == field_id) 320 + return i; 321 + } 322 + return -EPROTO; 323 + } 324 + 325 + #define TABLE_FIND_KEY(_desc, _id) \ 326 + efx_mae_table_hook_find((_desc)->n_keys, (_desc)->keys, _id) 327 + #define TABLE_FIND_RESP(_desc, _id) \ 328 + efx_mae_table_hook_find((_desc)->n_resps, (_desc)->resps, _id) 329 + 330 + #define TABLE_HOOK_KEY(_meta, _name, _mcdi_name) ({ \ 331 + int _rc = TABLE_FIND_KEY(&_meta->desc, TABLE_FIELD_ID_##_mcdi_name); \ 332 + \ 333 + if (_rc > U8_MAX) \ 334 + _rc = -EOPNOTSUPP; \ 335 + if (_rc >= 0) { \ 336 + _meta->keys._name##_idx = _rc; \ 337 + _rc = 0; \ 338 + } \ 339 + _rc; \ 340 + }) 341 + #define TABLE_HOOK_RESP(_meta, _name, _mcdi_name) ({ \ 342 + int _rc = TABLE_FIND_RESP(&_meta->desc, TABLE_FIELD_ID_##_mcdi_name); \ 343 + \ 344 + if (_rc > U8_MAX) \ 345 + _rc = -EOPNOTSUPP; \ 346 + if (_rc >= 0) { \ 347 + _meta->resps._name##_idx = _rc; \ 348 + _rc = 0; \ 349 + } \ 350 + _rc; \ 351 + }) 352 + 353 + static int efx_mae_table_hook_ct(struct efx_nic *efx, 354 + struct efx_tc_table_ct *meta_ct) 355 + { 356 + int rc; 357 + 358 + rc = TABLE_HOOK_KEY(meta_ct, eth_proto, ETHER_TYPE); 359 + if (rc) 360 + return rc; 361 + rc = TABLE_HOOK_KEY(meta_ct, ip_proto, IP_PROTO); 362 + if (rc) 363 + return rc; 364 + rc = TABLE_HOOK_KEY(meta_ct, src_ip, SRC_IP); 365 + if (rc) 366 + return rc; 367 + rc = TABLE_HOOK_KEY(meta_ct, dst_ip, DST_IP); 368 + if (rc) 369 + return rc; 370 + rc = TABLE_HOOK_KEY(meta_ct, l4_sport, SRC_PORT); 371 + if (rc) 372 + return rc; 373 + rc = TABLE_HOOK_KEY(meta_ct, l4_dport, DST_PORT); 374 + if (rc) 375 + return rc; 376 + rc = TABLE_HOOK_KEY(meta_ct, zone, DOMAIN); 377 + if (rc) 378 + return rc; 379 + rc = TABLE_HOOK_RESP(meta_ct, dnat, NAT_DIR); 380 + if (rc) 381 + return rc; 382 + rc = TABLE_HOOK_RESP(meta_ct, nat_ip, NAT_IP); 383 + if (rc) 384 + return rc; 385 + rc = TABLE_HOOK_RESP(meta_ct, l4_natport, NAT_PORT); 386 + if (rc) 387 + return rc; 388 + rc = TABLE_HOOK_RESP(meta_ct, mark, CT_MARK); 389 + if (rc) 390 + return rc; 391 + rc = TABLE_HOOK_RESP(meta_ct, counter_id, COUNTER_ID); 392 + if (rc) 393 + return rc; 394 + meta_ct->hooked = true; 395 + return 0; 396 + } 397 + 398 + static void efx_mae_table_free_desc(struct efx_tc_table_desc *desc) 399 + { 400 + kfree(desc->keys); 401 + kfree(desc->resps); 402 + memset(desc, 0, sizeof(*desc)); 403 + } 404 + 405 + static bool efx_mae_check_table_exists(struct efx_nic *efx, u32 tbl_req) 406 + { 407 + MCDI_DECLARE_BUF(outbuf, MC_CMD_TABLE_LIST_OUT_LEN(16)); 408 + MCDI_DECLARE_BUF(inbuf, MC_CMD_TABLE_LIST_IN_LEN); 409 + u32 tbl_id, tbl_total, tbl_cnt, pos = 0; 410 + size_t outlen, msg_max; 411 + bool ct_tbl = false; 412 + int rc, idx; 413 + 414 + msg_max = sizeof(outbuf); 415 + efx->tc->meta_ct.hooked = false; 416 + more: 417 + memset(outbuf, 0, sizeof(*outbuf)); 418 + MCDI_SET_DWORD(inbuf, TABLE_LIST_IN_FIRST_TABLE_ID_INDEX, pos); 419 + rc = efx_mcdi_rpc(efx, MC_CMD_TABLE_LIST, inbuf, sizeof(inbuf), outbuf, 420 + msg_max, &outlen); 421 + if (rc) 422 + return false; 423 + 424 + if (outlen < MC_CMD_TABLE_LIST_OUT_LEN(1)) 425 + return false; 426 + 427 + tbl_total = MCDI_DWORD(outbuf, TABLE_LIST_OUT_N_TABLES); 428 + tbl_cnt = MC_CMD_TABLE_LIST_OUT_TABLE_ID_NUM(min(outlen, msg_max)); 429 + 430 + for (idx = 0; idx < tbl_cnt; idx++) { 431 + tbl_id = MCDI_ARRAY_DWORD(outbuf, TABLE_LIST_OUT_TABLE_ID, idx); 432 + if (tbl_id == tbl_req) { 433 + ct_tbl = true; 434 + break; 435 + } 436 + } 437 + 438 + pos += tbl_cnt; 439 + if (!ct_tbl && pos < tbl_total) 440 + goto more; 441 + 442 + return ct_tbl; 443 + } 444 + 445 + int efx_mae_get_tables(struct efx_nic *efx) 446 + { 447 + int rc; 448 + 449 + efx->tc->meta_ct.hooked = false; 450 + if (efx_mae_check_table_exists(efx, TABLE_ID_CONNTRACK_TABLE)) { 451 + rc = efx_mae_table_get_desc(efx, &efx->tc->meta_ct.desc, 452 + TABLE_ID_CONNTRACK_TABLE); 453 + if (rc) { 454 + pci_info(efx->pci_dev, 455 + "FW does not support conntrack desc rc %d\n", 456 + rc); 457 + return 0; 458 + } 459 + 460 + rc = efx_mae_table_hook_ct(efx, &efx->tc->meta_ct); 461 + if (rc) { 462 + pci_info(efx->pci_dev, 463 + "FW does not support conntrack hook rc %d\n", 464 + rc); 465 + return 0; 466 + } 467 + } else { 468 + pci_info(efx->pci_dev, 469 + "FW does not support conntrack table\n"); 470 + } 471 + return 0; 472 + } 473 + 474 + void efx_mae_free_tables(struct efx_nic *efx) 475 + { 476 + efx_mae_table_free_desc(&efx->tc->meta_ct.desc); 477 + efx->tc->meta_ct.hooked = false; 478 + } 479 + 230 480 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps) 231 481 { 232 482 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
+3
drivers/net/ethernet/sfc/mae.h
··· 66 66 int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue); 67 67 void efx_mae_counters_grant_credits(struct work_struct *work); 68 68 69 + int efx_mae_get_tables(struct efx_nic *efx); 70 + void efx_mae_free_tables(struct efx_nic *efx); 71 + 69 72 #define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1) 70 73 71 74 struct mae_caps {
+3
drivers/net/ethernet/sfc/mcdi.h
··· 221 221 #define MCDI_BYTE(_buf, _field) \ 222 222 ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \ 223 223 *MCDI_PTR(_buf, _field)) 224 + #define MCDI_STRUCT_BYTE(_buf, _field) \ 225 + ((void)BUILD_BUG_ON_ZERO(_field ## _LEN != 1), \ 226 + *MCDI_STRUCT_PTR(_buf, _field)) 224 227 #define MCDI_SET_WORD(_buf, _field, _value) do { \ 225 228 BUILD_BUG_ON(MC_CMD_ ## _field ## _LEN != 2); \ 226 229 BUILD_BUG_ON(MC_CMD_ ## _field ## _OFST & 1); \
+8 -1
drivers/net/ethernet/sfc/tc.c
··· 1660 1660 rc = efx_tc_configure_fallback_acts_reps(efx); 1661 1661 if (rc) 1662 1662 return rc; 1663 + rc = efx_mae_get_tables(efx); 1664 + if (rc) 1665 + return rc; 1663 1666 efx->tc->up = true; 1664 1667 rc = flow_indr_dev_register(efx_tc_indr_setup_cb, efx); 1665 1668 if (rc) 1666 - return rc; 1669 + goto out_free; 1667 1670 return 0; 1671 + out_free: 1672 + efx_mae_free_tables(efx); 1673 + return rc; 1668 1674 } 1669 1675 1670 1676 void efx_fini_tc(struct efx_nic *efx) ··· 1686 1680 efx_tc_deconfigure_fallback_acts(efx, &efx->tc->facts.pf); 1687 1681 efx_tc_deconfigure_fallback_acts(efx, &efx->tc->facts.reps); 1688 1682 efx->tc->up = false; 1683 + efx_mae_free_tables(efx); 1689 1684 } 1690 1685 1691 1686 /* At teardown time, all TC filter rules (and thus all resources they created)
+44
drivers/net/ethernet/sfc/tc.h
··· 143 143 EFX_TC_PRIO__NUM 144 144 }; 145 145 146 + struct efx_tc_table_field_fmt { 147 + u16 field_id; 148 + u16 lbn; 149 + u16 width; 150 + u8 masking; 151 + u8 scheme; 152 + }; 153 + 154 + struct efx_tc_table_desc { 155 + u16 type; 156 + u16 key_width; 157 + u16 resp_width; 158 + u16 n_keys; 159 + u16 n_resps; 160 + u16 n_prios; 161 + u8 flags; 162 + u8 scheme; 163 + struct efx_tc_table_field_fmt *keys; 164 + struct efx_tc_table_field_fmt *resps; 165 + }; 166 + 167 + struct efx_tc_table_ct { /* TABLE_ID_CONNTRACK_TABLE */ 168 + struct efx_tc_table_desc desc; 169 + bool hooked; 170 + struct { /* indices of named fields within @desc.keys */ 171 + u8 eth_proto_idx; 172 + u8 ip_proto_idx; 173 + u8 src_ip_idx; /* either v4 or v6 */ 174 + u8 dst_ip_idx; 175 + u8 l4_sport_idx; 176 + u8 l4_dport_idx; 177 + u8 zone_idx; /* for TABLE_FIELD_ID_DOMAIN */ 178 + } keys; 179 + struct { /* indices of named fields within @desc.resps */ 180 + u8 dnat_idx; 181 + u8 nat_ip_idx; 182 + u8 l4_natport_idx; 183 + u8 mark_idx; 184 + u8 counter_id_idx; 185 + } resps; 186 + }; 187 + 146 188 /** 147 189 * struct efx_tc_state - control plane data for TC offload 148 190 * ··· 197 155 * @encap_match_ht: Hashtable of TC encap matches 198 156 * @match_action_ht: Hashtable of TC match-action rules 199 157 * @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder) 158 + * @meta_ct: MAE table layout for conntrack table 200 159 * @reps_mport_id: MAE port allocated for representor RX 201 160 * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) 202 161 * @reps_filter_mc: VNIC filter for representor multicast RX (allmulti) ··· 229 186 struct rhashtable encap_match_ht; 230 187 struct rhashtable match_action_ht; 231 188 struct rhashtable neigh_ht; 189 + struct efx_tc_table_ct meta_ct; 232 190 u32 reps_mport_id, reps_mport_vport_id; 233 191 s32 reps_filter_uc, reps_filter_mc; 234 192 bool flush_counters;