18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci/* Copyright (C) 2016-2018 Netronome Systems, Inc. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* 58c2ecf20Sopenharmony_ci * nfp_net_offload.c 68c2ecf20Sopenharmony_ci * Netronome network device driver: TC offload functions for PF and VF 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "NFP net bpf: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bpf.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 218c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gact.h> 228c2ecf20Sopenharmony_ci#include <net/tc_act/tc_mirred.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "main.h" 258c2ecf20Sopenharmony_ci#include "../ccm.h" 268c2ecf20Sopenharmony_ci#include "../nfp_app.h" 278c2ecf20Sopenharmony_ci#include "../nfp_net_ctrl.h" 288c2ecf20Sopenharmony_ci#include "../nfp_net.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int 318c2ecf20Sopenharmony_cinfp_map_ptr_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, 328c2ecf20Sopenharmony_ci struct bpf_map *map) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct nfp_bpf_neutral_map *record; 358c2ecf20Sopenharmony_ci int err; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Reuse path - other offloaded program is already tracking this map. */ 388c2ecf20Sopenharmony_ci record = rhashtable_lookup_fast(&bpf->maps_neutral, &map->id, 398c2ecf20Sopenharmony_ci nfp_bpf_maps_neutral_params); 408c2ecf20Sopenharmony_ci if (record) { 418c2ecf20Sopenharmony_ci nfp_prog->map_records[nfp_prog->map_records_cnt++] = record; 428c2ecf20Sopenharmony_ci record->count++; 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Grab a single ref to the map for our record. The prog destroy ndo 478c2ecf20Sopenharmony_ci * happens after free_used_maps(). 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci bpf_map_inc(map); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci record = kmalloc(sizeof(*record), GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!record) { 538c2ecf20Sopenharmony_ci err = -ENOMEM; 548c2ecf20Sopenharmony_ci goto err_map_put; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci record->ptr = map; 588c2ecf20Sopenharmony_ci record->map_id = map->id; 598c2ecf20Sopenharmony_ci record->count = 1; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci err = rhashtable_insert_fast(&bpf->maps_neutral, &record->l, 628c2ecf20Sopenharmony_ci nfp_bpf_maps_neutral_params); 638c2ecf20Sopenharmony_ci if (err) 648c2ecf20Sopenharmony_ci goto err_free_rec; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci nfp_prog->map_records[nfp_prog->map_records_cnt++] = record; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cierr_free_rec: 718c2ecf20Sopenharmony_ci kfree(record); 728c2ecf20Sopenharmony_cierr_map_put: 738c2ecf20Sopenharmony_ci bpf_map_put(map); 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void 788c2ecf20Sopenharmony_cinfp_map_ptrs_forget(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci bool freed = false; 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < nfp_prog->map_records_cnt; i++) { 848c2ecf20Sopenharmony_ci if (--nfp_prog->map_records[i]->count) { 858c2ecf20Sopenharmony_ci nfp_prog->map_records[i] = NULL; 868c2ecf20Sopenharmony_ci continue; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci WARN_ON(rhashtable_remove_fast(&bpf->maps_neutral, 908c2ecf20Sopenharmony_ci &nfp_prog->map_records[i]->l, 918c2ecf20Sopenharmony_ci nfp_bpf_maps_neutral_params)); 928c2ecf20Sopenharmony_ci freed = true; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (freed) { 968c2ecf20Sopenharmony_ci synchronize_rcu(); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci for (i = 0; i < nfp_prog->map_records_cnt; i++) 998c2ecf20Sopenharmony_ci if (nfp_prog->map_records[i]) { 1008c2ecf20Sopenharmony_ci bpf_map_put(nfp_prog->map_records[i]->ptr); 1018c2ecf20Sopenharmony_ci kfree(nfp_prog->map_records[i]); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci kfree(nfp_prog->map_records); 1068c2ecf20Sopenharmony_ci nfp_prog->map_records = NULL; 1078c2ecf20Sopenharmony_ci nfp_prog->map_records_cnt = 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int 1118c2ecf20Sopenharmony_cinfp_map_ptrs_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, 1128c2ecf20Sopenharmony_ci struct bpf_prog *prog) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int i, cnt, err = 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mutex_lock(&prog->aux->used_maps_mutex); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Quickly count the maps we will have to remember */ 1198c2ecf20Sopenharmony_ci cnt = 0; 1208c2ecf20Sopenharmony_ci for (i = 0; i < prog->aux->used_map_cnt; i++) 1218c2ecf20Sopenharmony_ci if (bpf_map_offload_neutral(prog->aux->used_maps[i])) 1228c2ecf20Sopenharmony_ci cnt++; 1238c2ecf20Sopenharmony_ci if (!cnt) 1248c2ecf20Sopenharmony_ci goto out; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci nfp_prog->map_records = kmalloc_array(cnt, 1278c2ecf20Sopenharmony_ci sizeof(nfp_prog->map_records[0]), 1288c2ecf20Sopenharmony_ci GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!nfp_prog->map_records) { 1308c2ecf20Sopenharmony_ci err = -ENOMEM; 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (i = 0; i < prog->aux->used_map_cnt; i++) 1358c2ecf20Sopenharmony_ci if (bpf_map_offload_neutral(prog->aux->used_maps[i])) { 1368c2ecf20Sopenharmony_ci err = nfp_map_ptr_record(bpf, nfp_prog, 1378c2ecf20Sopenharmony_ci prog->aux->used_maps[i]); 1388c2ecf20Sopenharmony_ci if (err) { 1398c2ecf20Sopenharmony_ci nfp_map_ptrs_forget(bpf, nfp_prog); 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci WARN_ON(cnt != nfp_prog->map_records_cnt); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciout: 1468c2ecf20Sopenharmony_ci mutex_unlock(&prog->aux->used_maps_mutex); 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int 1518c2ecf20Sopenharmony_cinfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog, 1528c2ecf20Sopenharmony_ci unsigned int cnt) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta; 1558c2ecf20Sopenharmony_ci unsigned int i; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) { 1588c2ecf20Sopenharmony_ci meta = kzalloc(sizeof(*meta), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!meta) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci meta->insn = prog[i]; 1638c2ecf20Sopenharmony_ci meta->n = i; 1648c2ecf20Sopenharmony_ci if (is_mbpf_alu(meta)) { 1658c2ecf20Sopenharmony_ci meta->umin_src = U64_MAX; 1668c2ecf20Sopenharmony_ci meta->umin_dst = U64_MAX; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci list_add_tail(&meta->l, &nfp_prog->insns); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci nfp_prog->n_insns = cnt; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci nfp_bpf_jit_prepare(nfp_prog); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void nfp_prog_free(struct nfp_prog *nfp_prog) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct nfp_insn_meta *meta, *tmp; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci kfree(nfp_prog->subprog); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci list_for_each_entry_safe(meta, tmp, &nfp_prog->insns, l) { 1858c2ecf20Sopenharmony_ci list_del(&meta->l); 1868c2ecf20Sopenharmony_ci kfree(meta); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci kfree(nfp_prog); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int nfp_bpf_verifier_prep(struct bpf_prog *prog) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct nfp_prog *nfp_prog; 1948c2ecf20Sopenharmony_ci int ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci nfp_prog = kzalloc(sizeof(*nfp_prog), GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!nfp_prog) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci prog->aux->offload->dev_priv = nfp_prog; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nfp_prog->insns); 2028c2ecf20Sopenharmony_ci nfp_prog->type = prog->type; 2038c2ecf20Sopenharmony_ci nfp_prog->bpf = bpf_offload_dev_priv(prog->aux->offload->offdev); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = nfp_prog_prepare(nfp_prog, prog->insnsi, prog->len); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto err_free; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci nfp_prog->verifier_meta = nfp_prog_first_meta(nfp_prog); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cierr_free: 2148c2ecf20Sopenharmony_ci nfp_prog_free(nfp_prog); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int nfp_bpf_translate(struct bpf_prog *prog) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev); 2228c2ecf20Sopenharmony_ci struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv; 2238c2ecf20Sopenharmony_ci unsigned int max_instr; 2248c2ecf20Sopenharmony_ci int err; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* We depend on dead code elimination succeeding */ 2278c2ecf20Sopenharmony_ci if (prog->aux->offload->opt_failed) 2288c2ecf20Sopenharmony_ci return -EINVAL; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci max_instr = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN); 2318c2ecf20Sopenharmony_ci nfp_prog->__prog_alloc_len = max_instr * sizeof(u64); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci nfp_prog->prog = kvmalloc(nfp_prog->__prog_alloc_len, GFP_KERNEL); 2348c2ecf20Sopenharmony_ci if (!nfp_prog->prog) 2358c2ecf20Sopenharmony_ci return -ENOMEM; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci err = nfp_bpf_jit(nfp_prog); 2388c2ecf20Sopenharmony_ci if (err) 2398c2ecf20Sopenharmony_ci return err; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci prog->aux->offload->jited_len = nfp_prog->prog_len * sizeof(u64); 2428c2ecf20Sopenharmony_ci prog->aux->offload->jited_image = nfp_prog->prog; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return nfp_map_ptrs_record(nfp_prog->bpf, nfp_prog, prog); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void nfp_bpf_destroy(struct bpf_prog *prog) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci kvfree(nfp_prog->prog); 2528c2ecf20Sopenharmony_ci nfp_map_ptrs_forget(nfp_prog->bpf, nfp_prog); 2538c2ecf20Sopenharmony_ci nfp_prog_free(nfp_prog); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* Atomic engine requires values to be in big endian, we need to byte swap 2578c2ecf20Sopenharmony_ci * the value words used with xadd. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic void nfp_map_bpf_byte_swap(struct nfp_bpf_map *nfp_map, void *value) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci u32 *word = value; 2628c2ecf20Sopenharmony_ci unsigned int i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++) 2658c2ecf20Sopenharmony_ci if (nfp_map->use_map[i].type == NFP_MAP_USE_ATOMIC_CNT) 2668c2ecf20Sopenharmony_ci word[i] = (__force u32)cpu_to_be32(word[i]); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* Mark value as unsafely initialized in case it becomes atomic later 2708c2ecf20Sopenharmony_ci * and we didn't byte swap something non-byte swap neutral. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic void 2738c2ecf20Sopenharmony_cinfp_map_bpf_byte_swap_record(struct nfp_bpf_map *nfp_map, void *value) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u32 *word = value; 2768c2ecf20Sopenharmony_ci unsigned int i; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++) 2798c2ecf20Sopenharmony_ci if (nfp_map->use_map[i].type == NFP_MAP_UNUSED && 2808c2ecf20Sopenharmony_ci word[i] != (__force u32)cpu_to_be32(word[i])) 2818c2ecf20Sopenharmony_ci nfp_map->use_map[i].non_zero_update = 1; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int 2858c2ecf20Sopenharmony_cinfp_bpf_map_lookup_entry(struct bpf_offloaded_map *offmap, 2868c2ecf20Sopenharmony_ci void *key, void *value) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int err; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci err = nfp_bpf_ctrl_lookup_entry(offmap, key, value); 2918c2ecf20Sopenharmony_ci if (err) 2928c2ecf20Sopenharmony_ci return err; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci nfp_map_bpf_byte_swap(offmap->dev_priv, value); 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int 2998c2ecf20Sopenharmony_cinfp_bpf_map_update_entry(struct bpf_offloaded_map *offmap, 3008c2ecf20Sopenharmony_ci void *key, void *value, u64 flags) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci nfp_map_bpf_byte_swap(offmap->dev_priv, value); 3038c2ecf20Sopenharmony_ci nfp_map_bpf_byte_swap_record(offmap->dev_priv, value); 3048c2ecf20Sopenharmony_ci return nfp_bpf_ctrl_update_entry(offmap, key, value, flags); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int 3088c2ecf20Sopenharmony_cinfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap, 3098c2ecf20Sopenharmony_ci void *key, void *next_key) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (!key) 3128c2ecf20Sopenharmony_ci return nfp_bpf_ctrl_getfirst_entry(offmap, next_key); 3138c2ecf20Sopenharmony_ci return nfp_bpf_ctrl_getnext_entry(offmap, key, next_key); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int 3178c2ecf20Sopenharmony_cinfp_bpf_map_delete_elem(struct bpf_offloaded_map *offmap, void *key) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci return nfp_bpf_ctrl_del_entry(offmap, key); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const struct bpf_map_dev_ops nfp_bpf_map_ops = { 3258c2ecf20Sopenharmony_ci .map_get_next_key = nfp_bpf_map_get_next_key, 3268c2ecf20Sopenharmony_ci .map_lookup_elem = nfp_bpf_map_lookup_entry, 3278c2ecf20Sopenharmony_ci .map_update_elem = nfp_bpf_map_update_entry, 3288c2ecf20Sopenharmony_ci .map_delete_elem = nfp_bpf_map_delete_elem, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int 3328c2ecf20Sopenharmony_cinfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct nfp_bpf_map *nfp_map; 3358c2ecf20Sopenharmony_ci unsigned int use_map_size; 3368c2ecf20Sopenharmony_ci long long int res; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!bpf->maps.types) 3398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (offmap->map.map_flags || 3428c2ecf20Sopenharmony_ci offmap->map.numa_node != NUMA_NO_NODE) { 3438c2ecf20Sopenharmony_ci pr_info("map flags are not supported\n"); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!(bpf->maps.types & 1 << offmap->map.map_type)) { 3488c2ecf20Sopenharmony_ci pr_info("map type not supported\n"); 3498c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci if (bpf->maps.max_maps == bpf->maps_in_use) { 3528c2ecf20Sopenharmony_ci pr_info("too many maps for a device\n"); 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (bpf->maps.max_elems - bpf->map_elems_in_use < 3568c2ecf20Sopenharmony_ci offmap->map.max_entries) { 3578c2ecf20Sopenharmony_ci pr_info("map with too many elements: %u, left: %u\n", 3588c2ecf20Sopenharmony_ci offmap->map.max_entries, 3598c2ecf20Sopenharmony_ci bpf->maps.max_elems - bpf->map_elems_in_use); 3608c2ecf20Sopenharmony_ci return -ENOMEM; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (round_up(offmap->map.key_size, 8) + 3648c2ecf20Sopenharmony_ci round_up(offmap->map.value_size, 8) > bpf->maps.max_elem_sz) { 3658c2ecf20Sopenharmony_ci pr_info("map elements too large: %u, FW max element size (key+value): %u\n", 3668c2ecf20Sopenharmony_ci round_up(offmap->map.key_size, 8) + 3678c2ecf20Sopenharmony_ci round_up(offmap->map.value_size, 8), 3688c2ecf20Sopenharmony_ci bpf->maps.max_elem_sz); 3698c2ecf20Sopenharmony_ci return -ENOMEM; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci if (offmap->map.key_size > bpf->maps.max_key_sz) { 3728c2ecf20Sopenharmony_ci pr_info("map key size %u, FW max is %u\n", 3738c2ecf20Sopenharmony_ci offmap->map.key_size, bpf->maps.max_key_sz); 3748c2ecf20Sopenharmony_ci return -ENOMEM; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci if (offmap->map.value_size > bpf->maps.max_val_sz) { 3778c2ecf20Sopenharmony_ci pr_info("map value size %u, FW max is %u\n", 3788c2ecf20Sopenharmony_ci offmap->map.value_size, bpf->maps.max_val_sz); 3798c2ecf20Sopenharmony_ci return -ENOMEM; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci use_map_size = DIV_ROUND_UP(offmap->map.value_size, 4) * 3838c2ecf20Sopenharmony_ci sizeof_field(struct nfp_bpf_map, use_map[0]); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci nfp_map = kzalloc(sizeof(*nfp_map) + use_map_size, GFP_USER); 3868c2ecf20Sopenharmony_ci if (!nfp_map) 3878c2ecf20Sopenharmony_ci return -ENOMEM; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci offmap->dev_priv = nfp_map; 3908c2ecf20Sopenharmony_ci nfp_map->offmap = offmap; 3918c2ecf20Sopenharmony_ci nfp_map->bpf = bpf; 3928c2ecf20Sopenharmony_ci spin_lock_init(&nfp_map->cache_lock); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci res = nfp_bpf_ctrl_alloc_map(bpf, &offmap->map); 3958c2ecf20Sopenharmony_ci if (res < 0) { 3968c2ecf20Sopenharmony_ci kfree(nfp_map); 3978c2ecf20Sopenharmony_ci return res; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci nfp_map->tid = res; 4018c2ecf20Sopenharmony_ci offmap->dev_ops = &nfp_bpf_map_ops; 4028c2ecf20Sopenharmony_ci bpf->maps_in_use++; 4038c2ecf20Sopenharmony_ci bpf->map_elems_in_use += offmap->map.max_entries; 4048c2ecf20Sopenharmony_ci list_add_tail(&nfp_map->l, &bpf->map_list); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int 4108c2ecf20Sopenharmony_cinfp_bpf_map_free(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct nfp_bpf_map *nfp_map = offmap->dev_priv; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci nfp_bpf_ctrl_free_map(bpf, nfp_map); 4158c2ecf20Sopenharmony_ci dev_consume_skb_any(nfp_map->cache); 4168c2ecf20Sopenharmony_ci WARN_ON_ONCE(nfp_map->cache_blockers); 4178c2ecf20Sopenharmony_ci list_del_init(&nfp_map->l); 4188c2ecf20Sopenharmony_ci bpf->map_elems_in_use -= offmap->map.max_entries; 4198c2ecf20Sopenharmony_ci bpf->maps_in_use--; 4208c2ecf20Sopenharmony_ci kfree(nfp_map); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciint nfp_ndo_bpf(struct nfp_app *app, struct nfp_net *nn, struct netdev_bpf *bpf) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci switch (bpf->command) { 4288c2ecf20Sopenharmony_ci case BPF_OFFLOAD_MAP_ALLOC: 4298c2ecf20Sopenharmony_ci return nfp_bpf_map_alloc(app->priv, bpf->offmap); 4308c2ecf20Sopenharmony_ci case BPF_OFFLOAD_MAP_FREE: 4318c2ecf20Sopenharmony_ci return nfp_bpf_map_free(app->priv, bpf->offmap); 4328c2ecf20Sopenharmony_ci default: 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic unsigned long 4388c2ecf20Sopenharmony_cinfp_bpf_perf_event_copy(void *dst, const void *src, 4398c2ecf20Sopenharmony_ci unsigned long off, unsigned long len) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci memcpy(dst, src + off, len); 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciint nfp_bpf_event_output(struct nfp_app_bpf *bpf, const void *data, 4468c2ecf20Sopenharmony_ci unsigned int len) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct cmsg_bpf_event *cbe = (void *)data; 4498c2ecf20Sopenharmony_ci struct nfp_bpf_neutral_map *record; 4508c2ecf20Sopenharmony_ci u32 pkt_size, data_size, map_id; 4518c2ecf20Sopenharmony_ci u64 map_id_full; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (len < sizeof(struct cmsg_bpf_event)) 4548c2ecf20Sopenharmony_ci return -EINVAL; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci pkt_size = be32_to_cpu(cbe->pkt_size); 4578c2ecf20Sopenharmony_ci data_size = be32_to_cpu(cbe->data_size); 4588c2ecf20Sopenharmony_ci map_id_full = be64_to_cpu(cbe->map_ptr); 4598c2ecf20Sopenharmony_ci map_id = map_id_full; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (len < sizeof(struct cmsg_bpf_event) + pkt_size + data_size) 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci if (cbe->hdr.ver != NFP_CCM_ABI_VERSION) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci rcu_read_lock(); 4678c2ecf20Sopenharmony_ci record = rhashtable_lookup(&bpf->maps_neutral, &map_id, 4688c2ecf20Sopenharmony_ci nfp_bpf_maps_neutral_params); 4698c2ecf20Sopenharmony_ci if (!record || map_id_full > U32_MAX) { 4708c2ecf20Sopenharmony_ci rcu_read_unlock(); 4718c2ecf20Sopenharmony_ci cmsg_warn(bpf, "perf event: map id %lld (0x%llx) not recognized, dropping event\n", 4728c2ecf20Sopenharmony_ci map_id_full, map_id_full); 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci bpf_event_output(record->ptr, be32_to_cpu(cbe->cpu_id), 4778c2ecf20Sopenharmony_ci &cbe->data[round_up(pkt_size, 4)], data_size, 4788c2ecf20Sopenharmony_ci cbe->data, pkt_size, nfp_bpf_perf_event_copy); 4798c2ecf20Sopenharmony_ci rcu_read_unlock(); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cibool nfp_bpf_offload_check_mtu(struct nfp_net *nn, struct bpf_prog *prog, 4858c2ecf20Sopenharmony_ci unsigned int mtu) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci unsigned int fw_mtu, pkt_off; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci fw_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32; 4908c2ecf20Sopenharmony_ci pkt_off = min(prog->aux->max_pkt_offset, mtu); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return fw_mtu < pkt_off; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int 4968c2ecf20Sopenharmony_cinfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog, 4978c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv; 5008c2ecf20Sopenharmony_ci unsigned int max_stack, max_prog_len; 5018c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 5028c2ecf20Sopenharmony_ci void *img; 5038c2ecf20Sopenharmony_ci int err; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (nfp_bpf_offload_check_mtu(nn, prog, nn->dp.netdev->mtu)) { 5068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with potential packet access beyond HW packet split boundary"); 5078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci max_stack = nn_readb(nn, NFP_NET_CFG_BPF_STACK_SZ) * 64; 5118c2ecf20Sopenharmony_ci if (nfp_prog->stack_size > max_stack) { 5128c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "stack too large"); 5138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci max_prog_len = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN); 5178c2ecf20Sopenharmony_ci if (nfp_prog->prog_len > max_prog_len) { 5188c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "program too long"); 5198c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci img = nfp_bpf_relo_for_vnic(nfp_prog, nn->app_priv); 5238c2ecf20Sopenharmony_ci if (IS_ERR(img)) 5248c2ecf20Sopenharmony_ci return PTR_ERR(img); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci dma_addr = dma_map_single(nn->dp.dev, img, 5278c2ecf20Sopenharmony_ci nfp_prog->prog_len * sizeof(u64), 5288c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 5298c2ecf20Sopenharmony_ci if (dma_mapping_error(nn->dp.dev, dma_addr)) { 5308c2ecf20Sopenharmony_ci kfree(img); 5318c2ecf20Sopenharmony_ci return -ENOMEM; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci nn_writew(nn, NFP_NET_CFG_BPF_SIZE, nfp_prog->prog_len); 5358c2ecf20Sopenharmony_ci nn_writeq(nn, NFP_NET_CFG_BPF_ADDR, dma_addr); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Load up the JITed code */ 5388c2ecf20Sopenharmony_ci err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_BPF); 5398c2ecf20Sopenharmony_ci if (err) 5408c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 5418c2ecf20Sopenharmony_ci "FW command error while loading BPF"); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci dma_unmap_single(nn->dp.dev, dma_addr, nfp_prog->prog_len * sizeof(u64), 5448c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 5458c2ecf20Sopenharmony_ci kfree(img); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return err; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void 5518c2ecf20Sopenharmony_cinfp_net_bpf_start(struct nfp_net *nn, struct netlink_ext_ack *extack) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci int err; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* Enable passing packets through BPF function */ 5568c2ecf20Sopenharmony_ci nn->dp.ctrl |= NFP_NET_CFG_CTRL_BPF; 5578c2ecf20Sopenharmony_ci nn_writel(nn, NFP_NET_CFG_CTRL, nn->dp.ctrl); 5588c2ecf20Sopenharmony_ci err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); 5598c2ecf20Sopenharmony_ci if (err) 5608c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 5618c2ecf20Sopenharmony_ci "FW command error while enabling BPF"); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int nfp_net_bpf_stop(struct nfp_net *nn) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci if (!(nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF)) 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci nn->dp.ctrl &= ~NFP_NET_CFG_CTRL_BPF; 5708c2ecf20Sopenharmony_ci nn_writel(nn, NFP_NET_CFG_CTRL, nn->dp.ctrl); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ciint nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog, 5768c2ecf20Sopenharmony_ci bool old_prog, struct netlink_ext_ack *extack) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci int err; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (prog && !bpf_offload_dev_match(prog, nn->dp.netdev)) 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (prog && old_prog) { 5848c2ecf20Sopenharmony_ci u8 cap; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci cap = nn_readb(nn, NFP_NET_CFG_BPF_CAP); 5878c2ecf20Sopenharmony_ci if (!(cap & NFP_NET_BPF_CAP_RELO)) { 5888c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 5898c2ecf20Sopenharmony_ci "FW does not support live reload"); 5908c2ecf20Sopenharmony_ci return -EBUSY; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Something else is loaded, different program type? */ 5958c2ecf20Sopenharmony_ci if (!old_prog && nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) 5968c2ecf20Sopenharmony_ci return -EBUSY; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (old_prog && !prog) 5998c2ecf20Sopenharmony_ci return nfp_net_bpf_stop(nn); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci err = nfp_net_bpf_load(nn, prog, extack); 6028c2ecf20Sopenharmony_ci if (err) 6038c2ecf20Sopenharmony_ci return err; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!old_prog) 6068c2ecf20Sopenharmony_ci nfp_net_bpf_start(nn, extack); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ciconst struct bpf_prog_offload_ops nfp_bpf_dev_ops = { 6128c2ecf20Sopenharmony_ci .insn_hook = nfp_verify_insn, 6138c2ecf20Sopenharmony_ci .finalize = nfp_bpf_finalize, 6148c2ecf20Sopenharmony_ci .replace_insn = nfp_bpf_opt_replace_insn, 6158c2ecf20Sopenharmony_ci .remove_insns = nfp_bpf_opt_remove_insns, 6168c2ecf20Sopenharmony_ci .prepare = nfp_bpf_verifier_prep, 6178c2ecf20Sopenharmony_ci .translate = nfp_bpf_translate, 6188c2ecf20Sopenharmony_ci .destroy = nfp_bpf_destroy, 6198c2ecf20Sopenharmony_ci}; 620