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