18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_cpp.h" 78c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_nffw.h" 88c2ecf20Sopenharmony_ci#include "../nfpcore/nfp_nsp.h" 98c2ecf20Sopenharmony_ci#include "../nfp_app.h" 108c2ecf20Sopenharmony_ci#include "../nfp_main.h" 118c2ecf20Sopenharmony_ci#include "../nfp_net.h" 128c2ecf20Sopenharmony_ci#include "../nfp_port.h" 138c2ecf20Sopenharmony_ci#include "fw.h" 148c2ecf20Sopenharmony_ci#include "main.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciconst struct rhashtable_params nfp_bpf_maps_neutral_params = { 178c2ecf20Sopenharmony_ci .nelem_hint = 4, 188c2ecf20Sopenharmony_ci .key_len = sizeof_field(struct bpf_map, id), 198c2ecf20Sopenharmony_ci .key_offset = offsetof(struct nfp_bpf_neutral_map, map_id), 208c2ecf20Sopenharmony_ci .head_offset = offsetof(struct nfp_bpf_neutral_map, l), 218c2ecf20Sopenharmony_ci .automatic_shrinking = true, 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic bool nfp_net_ebpf_capable(struct nfp_net *nn) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN 278c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf = nn->app->priv; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return nn->cap & NFP_NET_CFG_CTRL_BPF && 308c2ecf20Sopenharmony_ci bpf->abi_version && 318c2ecf20Sopenharmony_ci nn_readb(nn, NFP_NET_CFG_BPF_ABI) == bpf->abi_version; 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_ci return false; 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int 388c2ecf20Sopenharmony_cinfp_bpf_xdp_offload(struct nfp_app *app, struct nfp_net *nn, 398c2ecf20Sopenharmony_ci struct bpf_prog *prog, struct netlink_ext_ack *extack) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci bool running, xdp_running; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!nfp_net_ebpf_capable(nn)) 448c2ecf20Sopenharmony_ci return -EINVAL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci running = nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF; 478c2ecf20Sopenharmony_ci xdp_running = running && nn->xdp_hw.prog; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!prog && !xdp_running) 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci if (prog && running && !xdp_running) 528c2ecf20Sopenharmony_ci return -EBUSY; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return nfp_net_bpf_offload(nn, prog, running, extack); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return nfp_net_ebpf_capable(nn) ? "BPF" : ""; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int 638c2ecf20Sopenharmony_cinfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct nfp_pf *pf = app->pf; 668c2ecf20Sopenharmony_ci struct nfp_bpf_vnic *bv; 678c2ecf20Sopenharmony_ci int err; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!pf->eth_tbl) { 708c2ecf20Sopenharmony_ci nfp_err(pf->cpp, "No ETH table\n"); 718c2ecf20Sopenharmony_ci return -EINVAL; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci if (pf->max_data_vnics != pf->eth_tbl->count) { 748c2ecf20Sopenharmony_ci nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n", 758c2ecf20Sopenharmony_ci pf->max_data_vnics, pf->eth_tbl->count); 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci bv = kzalloc(sizeof(*bv), GFP_KERNEL); 808c2ecf20Sopenharmony_ci if (!bv) 818c2ecf20Sopenharmony_ci return -ENOMEM; 828c2ecf20Sopenharmony_ci nn->app_priv = bv; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci err = nfp_app_nic_vnic_alloc(app, nn, id); 858c2ecf20Sopenharmony_ci if (err) 868c2ecf20Sopenharmony_ci goto err_free_priv; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci bv->start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); 898c2ecf20Sopenharmony_ci bv->tgt_done = nn_readw(nn, NFP_NET_CFG_BPF_DONE); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_cierr_free_priv: 938c2ecf20Sopenharmony_ci kfree(nn->app_priv); 948c2ecf20Sopenharmony_ci return err; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct nfp_bpf_vnic *bv = nn->app_priv; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci WARN_ON(bv->tc_prog); 1028c2ecf20Sopenharmony_ci kfree(bv); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type, 1068c2ecf20Sopenharmony_ci void *type_data, void *cb_priv) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct tc_cls_bpf_offload *cls_bpf = type_data; 1098c2ecf20Sopenharmony_ci struct nfp_net *nn = cb_priv; 1108c2ecf20Sopenharmony_ci struct bpf_prog *oldprog; 1118c2ecf20Sopenharmony_ci struct nfp_bpf_vnic *bv; 1128c2ecf20Sopenharmony_ci int err; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (type != TC_SETUP_CLSBPF) { 1158c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, 1168c2ecf20Sopenharmony_ci "only offload of BPF classifiers supported"); 1178c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci if (!tc_cls_can_offload_and_chain0(nn->dp.netdev, &cls_bpf->common)) 1208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1218c2ecf20Sopenharmony_ci if (!nfp_net_ebpf_capable(nn)) { 1228c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, 1238c2ecf20Sopenharmony_ci "NFP firmware does not support eBPF offload"); 1248c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci if (cls_bpf->common.protocol != htons(ETH_P_ALL)) { 1278c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, 1288c2ecf20Sopenharmony_ci "only ETH_P_ALL supported as filter protocol"); 1298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Only support TC direct action */ 1338c2ecf20Sopenharmony_ci if (!cls_bpf->exts_integrated || 1348c2ecf20Sopenharmony_ci tcf_exts_has_actions(cls_bpf->exts)) { 1358c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, 1368c2ecf20Sopenharmony_ci "only direct action with no legacy actions supported"); 1378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (cls_bpf->command != TC_CLSBPF_OFFLOAD) 1418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci bv = nn->app_priv; 1448c2ecf20Sopenharmony_ci oldprog = cls_bpf->oldprog; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Don't remove if oldprog doesn't match driver's state */ 1478c2ecf20Sopenharmony_ci if (bv->tc_prog != oldprog) { 1488c2ecf20Sopenharmony_ci oldprog = NULL; 1498c2ecf20Sopenharmony_ci if (!cls_bpf->prog) 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog, 1548c2ecf20Sopenharmony_ci cls_bpf->common.extack); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci bv->tc_prog = cls_bpf->prog; 1598c2ecf20Sopenharmony_ci nn->port->tc_offload_cnt = !!bv->tc_prog; 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic LIST_HEAD(nfp_bpf_block_cb_list); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, 1668c2ecf20Sopenharmony_ci enum tc_setup_type type, void *type_data) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct nfp_net *nn = netdev_priv(netdev); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci switch (type) { 1718c2ecf20Sopenharmony_ci case TC_SETUP_BLOCK: 1728c2ecf20Sopenharmony_ci return flow_block_cb_setup_simple(type_data, 1738c2ecf20Sopenharmony_ci &nfp_bpf_block_cb_list, 1748c2ecf20Sopenharmony_ci nfp_bpf_setup_tc_block_cb, 1758c2ecf20Sopenharmony_ci nn, nn, true); 1768c2ecf20Sopenharmony_ci default: 1778c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int 1828c2ecf20Sopenharmony_cinfp_bpf_check_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct nfp_net *nn = netdev_priv(netdev); 1858c2ecf20Sopenharmony_ci struct nfp_bpf_vnic *bv; 1868c2ecf20Sopenharmony_ci struct bpf_prog *prog; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (~nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (nn->xdp_hw.prog) { 1928c2ecf20Sopenharmony_ci prog = nn->xdp_hw.prog; 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci bv = nn->app_priv; 1958c2ecf20Sopenharmony_ci prog = bv->tc_prog; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (nfp_bpf_offload_check_mtu(nn, prog, new_mtu)) { 1998c2ecf20Sopenharmony_ci nn_info(nn, "BPF offload active, potential packet access beyond hardware packet boundary"); 2008c2ecf20Sopenharmony_ci return -EBUSY; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int 2068c2ecf20Sopenharmony_cinfp_bpf_parse_cap_adjust_head(struct nfp_app_bpf *bpf, void __iomem *value, 2078c2ecf20Sopenharmony_ci u32 length) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct nfp_bpf_cap_tlv_adjust_head __iomem *cap = value; 2108c2ecf20Sopenharmony_ci struct nfp_cpp *cpp = bpf->app->pf->cpp; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (length < sizeof(*cap)) { 2138c2ecf20Sopenharmony_ci nfp_err(cpp, "truncated adjust_head TLV: %d\n", length); 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci bpf->adjust_head.flags = readl(&cap->flags); 2188c2ecf20Sopenharmony_ci bpf->adjust_head.off_min = readl(&cap->off_min); 2198c2ecf20Sopenharmony_ci bpf->adjust_head.off_max = readl(&cap->off_max); 2208c2ecf20Sopenharmony_ci bpf->adjust_head.guaranteed_sub = readl(&cap->guaranteed_sub); 2218c2ecf20Sopenharmony_ci bpf->adjust_head.guaranteed_add = readl(&cap->guaranteed_add); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (bpf->adjust_head.off_min > bpf->adjust_head.off_max) { 2248c2ecf20Sopenharmony_ci nfp_err(cpp, "invalid adjust_head TLV: min > max\n"); 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci if (!FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_min) || 2288c2ecf20Sopenharmony_ci !FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_max)) { 2298c2ecf20Sopenharmony_ci nfp_warn(cpp, "disabling adjust_head - driver expects min/max to fit in as immediates\n"); 2308c2ecf20Sopenharmony_ci memset(&bpf->adjust_head, 0, sizeof(bpf->adjust_head)); 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int 2388c2ecf20Sopenharmony_cinfp_bpf_parse_cap_func(struct nfp_app_bpf *bpf, void __iomem *value, u32 length) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct nfp_bpf_cap_tlv_func __iomem *cap = value; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (length < sizeof(*cap)) { 2438c2ecf20Sopenharmony_ci nfp_err(bpf->app->cpp, "truncated function TLV: %d\n", length); 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci switch (readl(&cap->func_id)) { 2488c2ecf20Sopenharmony_ci case BPF_FUNC_map_lookup_elem: 2498c2ecf20Sopenharmony_ci bpf->helpers.map_lookup = readl(&cap->func_addr); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case BPF_FUNC_map_update_elem: 2528c2ecf20Sopenharmony_ci bpf->helpers.map_update = readl(&cap->func_addr); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci case BPF_FUNC_map_delete_elem: 2558c2ecf20Sopenharmony_ci bpf->helpers.map_delete = readl(&cap->func_addr); 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci case BPF_FUNC_perf_event_output: 2588c2ecf20Sopenharmony_ci bpf->helpers.perf_event_output = readl(&cap->func_addr); 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int 2668c2ecf20Sopenharmony_cinfp_bpf_parse_cap_maps(struct nfp_app_bpf *bpf, void __iomem *value, u32 length) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct nfp_bpf_cap_tlv_maps __iomem *cap = value; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (length < sizeof(*cap)) { 2718c2ecf20Sopenharmony_ci nfp_err(bpf->app->cpp, "truncated maps TLV: %d\n", length); 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci bpf->maps.types = readl(&cap->types); 2768c2ecf20Sopenharmony_ci bpf->maps.max_maps = readl(&cap->max_maps); 2778c2ecf20Sopenharmony_ci bpf->maps.max_elems = readl(&cap->max_elems); 2788c2ecf20Sopenharmony_ci bpf->maps.max_key_sz = readl(&cap->max_key_sz); 2798c2ecf20Sopenharmony_ci bpf->maps.max_val_sz = readl(&cap->max_val_sz); 2808c2ecf20Sopenharmony_ci bpf->maps.max_elem_sz = readl(&cap->max_elem_sz); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int 2868c2ecf20Sopenharmony_cinfp_bpf_parse_cap_random(struct nfp_app_bpf *bpf, void __iomem *value, 2878c2ecf20Sopenharmony_ci u32 length) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci bpf->pseudo_random = true; 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int 2948c2ecf20Sopenharmony_cinfp_bpf_parse_cap_qsel(struct nfp_app_bpf *bpf, void __iomem *value, u32 length) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci bpf->queue_select = true; 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int 3018c2ecf20Sopenharmony_cinfp_bpf_parse_cap_adjust_tail(struct nfp_app_bpf *bpf, void __iomem *value, 3028c2ecf20Sopenharmony_ci u32 length) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci bpf->adjust_tail = true; 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int 3098c2ecf20Sopenharmony_cinfp_bpf_parse_cap_cmsg_multi_ent(struct nfp_app_bpf *bpf, void __iomem *value, 3108c2ecf20Sopenharmony_ci u32 length) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci bpf->cmsg_multi_ent = true; 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int 3178c2ecf20Sopenharmony_cinfp_bpf_parse_cap_abi_version(struct nfp_app_bpf *bpf, void __iomem *value, 3188c2ecf20Sopenharmony_ci u32 length) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci if (length < 4) { 3218c2ecf20Sopenharmony_ci nfp_err(bpf->app->cpp, "truncated ABI version TLV: %d\n", 3228c2ecf20Sopenharmony_ci length); 3238c2ecf20Sopenharmony_ci return -EINVAL; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci bpf->abi_version = readl(value); 3278c2ecf20Sopenharmony_ci if (bpf->abi_version < 2 || bpf->abi_version > 3) { 3288c2ecf20Sopenharmony_ci nfp_warn(bpf->app->cpp, "unsupported BPF ABI version: %d\n", 3298c2ecf20Sopenharmony_ci bpf->abi_version); 3308c2ecf20Sopenharmony_ci bpf->abi_version = 0; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int nfp_bpf_parse_capabilities(struct nfp_app *app) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct nfp_cpp *cpp = app->pf->cpp; 3398c2ecf20Sopenharmony_ci struct nfp_cpp_area *area; 3408c2ecf20Sopenharmony_ci u8 __iomem *mem, *start; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci mem = nfp_rtsym_map(app->pf->rtbl, "_abi_bpf_capabilities", "bpf.cap", 3438c2ecf20Sopenharmony_ci 8, &area); 3448c2ecf20Sopenharmony_ci if (IS_ERR(mem)) 3458c2ecf20Sopenharmony_ci return PTR_ERR(mem) == -ENOENT ? 0 : PTR_ERR(mem); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci start = mem; 3488c2ecf20Sopenharmony_ci while (mem - start + 8 <= nfp_cpp_area_size(area)) { 3498c2ecf20Sopenharmony_ci u8 __iomem *value; 3508c2ecf20Sopenharmony_ci u32 type, length; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci type = readl(mem); 3538c2ecf20Sopenharmony_ci length = readl(mem + 4); 3548c2ecf20Sopenharmony_ci value = mem + 8; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci mem += 8 + length; 3578c2ecf20Sopenharmony_ci if (mem - start > nfp_cpp_area_size(area)) 3588c2ecf20Sopenharmony_ci goto err_release_free; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci switch (type) { 3618c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_FUNC: 3628c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_func(app->priv, value, length)) 3638c2ecf20Sopenharmony_ci goto err_release_free; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_ADJUST_HEAD: 3668c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_adjust_head(app->priv, value, 3678c2ecf20Sopenharmony_ci length)) 3688c2ecf20Sopenharmony_ci goto err_release_free; 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_MAPS: 3718c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_maps(app->priv, value, length)) 3728c2ecf20Sopenharmony_ci goto err_release_free; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_RANDOM: 3758c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_random(app->priv, value, length)) 3768c2ecf20Sopenharmony_ci goto err_release_free; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_QUEUE_SELECT: 3798c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_qsel(app->priv, value, length)) 3808c2ecf20Sopenharmony_ci goto err_release_free; 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_ADJUST_TAIL: 3838c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_adjust_tail(app->priv, value, 3848c2ecf20Sopenharmony_ci length)) 3858c2ecf20Sopenharmony_ci goto err_release_free; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_ABI_VERSION: 3888c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_abi_version(app->priv, value, 3898c2ecf20Sopenharmony_ci length)) 3908c2ecf20Sopenharmony_ci goto err_release_free; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case NFP_BPF_CAP_TYPE_CMSG_MULTI_ENT: 3938c2ecf20Sopenharmony_ci if (nfp_bpf_parse_cap_cmsg_multi_ent(app->priv, value, 3948c2ecf20Sopenharmony_ci length)) 3958c2ecf20Sopenharmony_ci goto err_release_free; 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci default: 3988c2ecf20Sopenharmony_ci nfp_dbg(cpp, "unknown BPF capability: %d\n", type); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci if (mem - start != nfp_cpp_area_size(area)) { 4038c2ecf20Sopenharmony_ci nfp_err(cpp, "BPF capabilities left after parsing, parsed:%zd total length:%zu\n", 4048c2ecf20Sopenharmony_ci mem - start, nfp_cpp_area_size(area)); 4058c2ecf20Sopenharmony_ci goto err_release_free; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci nfp_cpp_area_release_free(area); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cierr_release_free: 4138c2ecf20Sopenharmony_ci nfp_err(cpp, "invalid BPF capabilities at offset:%zd\n", mem - start); 4148c2ecf20Sopenharmony_ci nfp_cpp_area_release_free(area); 4158c2ecf20Sopenharmony_ci return -EINVAL; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void nfp_bpf_init_capabilities(struct nfp_app_bpf *bpf) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci bpf->abi_version = 2; /* Original BPF ABI version */ 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int nfp_bpf_ndo_init(struct nfp_app *app, struct net_device *netdev) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf = app->priv; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return bpf_offload_dev_netdev_register(bpf->bpf_dev, netdev); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void nfp_bpf_ndo_uninit(struct nfp_app *app, struct net_device *netdev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf = app->priv; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci bpf_offload_dev_netdev_unregister(bpf->bpf_dev, netdev); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int nfp_bpf_start(struct nfp_app *app) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf = app->priv; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (app->ctrl->dp.mtu < nfp_bpf_ctrl_cmsg_min_mtu(bpf)) { 4428c2ecf20Sopenharmony_ci nfp_err(bpf->app->cpp, 4438c2ecf20Sopenharmony_ci "ctrl channel MTU below min required %u < %u\n", 4448c2ecf20Sopenharmony_ci app->ctrl->dp.mtu, nfp_bpf_ctrl_cmsg_min_mtu(bpf)); 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (bpf->cmsg_multi_ent) 4498c2ecf20Sopenharmony_ci bpf->cmsg_cache_cnt = nfp_bpf_ctrl_cmsg_cache_cnt(bpf); 4508c2ecf20Sopenharmony_ci else 4518c2ecf20Sopenharmony_ci bpf->cmsg_cache_cnt = 1; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int nfp_bpf_init(struct nfp_app *app) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf; 4598c2ecf20Sopenharmony_ci int err; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci bpf = kzalloc(sizeof(*bpf), GFP_KERNEL); 4628c2ecf20Sopenharmony_ci if (!bpf) 4638c2ecf20Sopenharmony_ci return -ENOMEM; 4648c2ecf20Sopenharmony_ci bpf->app = app; 4658c2ecf20Sopenharmony_ci app->priv = bpf; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bpf->map_list); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci err = nfp_ccm_init(&bpf->ccm, app); 4708c2ecf20Sopenharmony_ci if (err) 4718c2ecf20Sopenharmony_ci goto err_free_bpf; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci err = rhashtable_init(&bpf->maps_neutral, &nfp_bpf_maps_neutral_params); 4748c2ecf20Sopenharmony_ci if (err) 4758c2ecf20Sopenharmony_ci goto err_clean_ccm; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci nfp_bpf_init_capabilities(bpf); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci err = nfp_bpf_parse_capabilities(app); 4808c2ecf20Sopenharmony_ci if (err) 4818c2ecf20Sopenharmony_ci goto err_free_neutral_maps; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (bpf->abi_version < 3) { 4848c2ecf20Sopenharmony_ci bpf->cmsg_key_sz = CMSG_MAP_KEY_LW * 4; 4858c2ecf20Sopenharmony_ci bpf->cmsg_val_sz = CMSG_MAP_VALUE_LW * 4; 4868c2ecf20Sopenharmony_ci } else { 4878c2ecf20Sopenharmony_ci bpf->cmsg_key_sz = bpf->maps.max_key_sz; 4888c2ecf20Sopenharmony_ci bpf->cmsg_val_sz = bpf->maps.max_val_sz; 4898c2ecf20Sopenharmony_ci app->ctrl_mtu = nfp_bpf_ctrl_cmsg_mtu(bpf); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops, bpf); 4938c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(bpf->bpf_dev); 4948c2ecf20Sopenharmony_ci if (err) 4958c2ecf20Sopenharmony_ci goto err_free_neutral_maps; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cierr_free_neutral_maps: 5008c2ecf20Sopenharmony_ci rhashtable_destroy(&bpf->maps_neutral); 5018c2ecf20Sopenharmony_cierr_clean_ccm: 5028c2ecf20Sopenharmony_ci nfp_ccm_clean(&bpf->ccm); 5038c2ecf20Sopenharmony_cierr_free_bpf: 5048c2ecf20Sopenharmony_ci kfree(bpf); 5058c2ecf20Sopenharmony_ci return err; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void nfp_bpf_clean(struct nfp_app *app) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct nfp_app_bpf *bpf = app->priv; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci bpf_offload_dev_destroy(bpf->bpf_dev); 5138c2ecf20Sopenharmony_ci nfp_ccm_clean(&bpf->ccm); 5148c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&bpf->map_list)); 5158c2ecf20Sopenharmony_ci WARN_ON(bpf->maps_in_use || bpf->map_elems_in_use); 5168c2ecf20Sopenharmony_ci rhashtable_free_and_destroy(&bpf->maps_neutral, 5178c2ecf20Sopenharmony_ci nfp_check_rhashtable_empty, NULL); 5188c2ecf20Sopenharmony_ci kfree(bpf); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciconst struct nfp_app_type app_bpf = { 5228c2ecf20Sopenharmony_ci .id = NFP_APP_BPF_NIC, 5238c2ecf20Sopenharmony_ci .name = "ebpf", 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci .ctrl_cap_mask = 0, 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci .init = nfp_bpf_init, 5288c2ecf20Sopenharmony_ci .clean = nfp_bpf_clean, 5298c2ecf20Sopenharmony_ci .start = nfp_bpf_start, 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci .check_mtu = nfp_bpf_check_mtu, 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci .extra_cap = nfp_bpf_extra_cap, 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci .ndo_init = nfp_bpf_ndo_init, 5368c2ecf20Sopenharmony_ci .ndo_uninit = nfp_bpf_ndo_uninit, 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci .vnic_alloc = nfp_bpf_vnic_alloc, 5398c2ecf20Sopenharmony_ci .vnic_free = nfp_bpf_vnic_free, 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci .ctrl_msg_rx = nfp_bpf_ctrl_msg_rx, 5428c2ecf20Sopenharmony_ci .ctrl_msg_rx_raw = nfp_bpf_ctrl_msg_rx_raw, 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci .setup_tc = nfp_bpf_setup_tc, 5458c2ecf20Sopenharmony_ci .bpf = nfp_ndo_bpf, 5468c2ecf20Sopenharmony_ci .xdp_offload = nfp_bpf_xdp_offload, 5478c2ecf20Sopenharmony_ci}; 548