18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Netronome Systems, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This software is licensed under the GNU General License Version 2, 58c2ecf20Sopenharmony_ci * June 1991 as shown in the file COPYING in the top-level directory of this 68c2ecf20Sopenharmony_ci * source tree. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 98c2ecf20Sopenharmony_ci * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 108c2ecf20Sopenharmony_ci * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 118c2ecf20Sopenharmony_ci * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 128c2ecf20Sopenharmony_ci * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 138c2ecf20Sopenharmony_ci * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/bpf.h> 178c2ecf20Sopenharmony_ci#include <linux/bpf_verifier.h> 188c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 228c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "netdevsim.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define pr_vlog(env, fmt, ...) \ 278c2ecf20Sopenharmony_ci bpf_verifier_log_write(env, "[netdevsim] " fmt, ##__VA_ARGS__) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct nsim_bpf_bound_prog { 308c2ecf20Sopenharmony_ci struct nsim_dev *nsim_dev; 318c2ecf20Sopenharmony_ci struct bpf_prog *prog; 328c2ecf20Sopenharmony_ci struct dentry *ddir; 338c2ecf20Sopenharmony_ci const char *state; 348c2ecf20Sopenharmony_ci bool is_loaded; 358c2ecf20Sopenharmony_ci struct list_head l; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define NSIM_BPF_MAX_KEYS 2 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct nsim_bpf_bound_map { 418c2ecf20Sopenharmony_ci struct netdevsim *ns; 428c2ecf20Sopenharmony_ci struct bpf_offloaded_map *map; 438c2ecf20Sopenharmony_ci struct mutex mutex; 448c2ecf20Sopenharmony_ci struct nsim_map_entry { 458c2ecf20Sopenharmony_ci void *key; 468c2ecf20Sopenharmony_ci void *value; 478c2ecf20Sopenharmony_ci } entry[NSIM_BPF_MAX_KEYS]; 488c2ecf20Sopenharmony_ci struct list_head l; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int nsim_bpf_string_show(struct seq_file *file, void *data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci const char **str = file->private; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (*str) 568c2ecf20Sopenharmony_ci seq_printf(file, "%s\n", *str); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(nsim_bpf_string); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int 638c2ecf20Sopenharmony_cinsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state; 668c2ecf20Sopenharmony_ci int ret = 0; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci state = env->prog->aux->offload->dev_priv; 698c2ecf20Sopenharmony_ci if (state->nsim_dev->bpf_bind_verifier_delay && !insn_idx) 708c2ecf20Sopenharmony_ci msleep(state->nsim_dev->bpf_bind_verifier_delay); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (insn_idx == env->prog->len - 1) { 738c2ecf20Sopenharmony_ci pr_vlog(env, "Hello from netdevsim!\n"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!state->nsim_dev->bpf_bind_verifier_accept) 768c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int nsim_bpf_finalize(struct bpf_verifier_env *env) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic bool nsim_xdp_offload_active(struct netdevsim *ns) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return ns->xdp_hw.prog; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!prog || !prog->aux->offload) 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci state = prog->aux->offload->dev_priv; 1008c2ecf20Sopenharmony_ci state->is_loaded = loaded; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int 1048c2ecf20Sopenharmony_cinsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci nsim_prog_set_loaded(ns->bpf_offloaded, false); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci WARN(!!ns->bpf_offloaded != oldprog, 1098c2ecf20Sopenharmony_ci "bad offload state, expected offload %sto be active", 1108c2ecf20Sopenharmony_ci oldprog ? "" : "not "); 1118c2ecf20Sopenharmony_ci ns->bpf_offloaded = prog; 1128c2ecf20Sopenharmony_ci ns->bpf_offloaded_id = prog ? prog->aux->id : 0; 1138c2ecf20Sopenharmony_ci nsim_prog_set_loaded(prog, true); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciint nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, 1198c2ecf20Sopenharmony_ci void *type_data, void *cb_priv) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct tc_cls_bpf_offload *cls_bpf = type_data; 1228c2ecf20Sopenharmony_ci struct bpf_prog *prog = cls_bpf->prog; 1238c2ecf20Sopenharmony_ci struct netdevsim *ns = cb_priv; 1248c2ecf20Sopenharmony_ci struct bpf_prog *oldprog; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (type != TC_SETUP_CLSBPF) { 1278c2ecf20Sopenharmony_ci NSIM_EA(cls_bpf->common.extack, 1288c2ecf20Sopenharmony_ci "only offload of BPF classifiers supported"); 1298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!tc_cls_can_offload_and_chain0(ns->netdev, &cls_bpf->common)) 1338c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (cls_bpf->common.protocol != htons(ETH_P_ALL)) { 1368c2ecf20Sopenharmony_ci NSIM_EA(cls_bpf->common.extack, 1378c2ecf20Sopenharmony_ci "only ETH_P_ALL supported as filter protocol"); 1388c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!ns->bpf_tc_accept) { 1428c2ecf20Sopenharmony_ci NSIM_EA(cls_bpf->common.extack, 1438c2ecf20Sopenharmony_ci "netdevsim configured to reject BPF TC offload"); 1448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci /* Note: progs without skip_sw will probably not be dev bound */ 1478c2ecf20Sopenharmony_ci if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept) { 1488c2ecf20Sopenharmony_ci NSIM_EA(cls_bpf->common.extack, 1498c2ecf20Sopenharmony_ci "netdevsim configured to reject unbound programs"); 1508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (cls_bpf->command != TC_CLSBPF_OFFLOAD) 1548c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci oldprog = cls_bpf->oldprog; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Don't remove if oldprog doesn't match driver's state */ 1598c2ecf20Sopenharmony_ci if (ns->bpf_offloaded != oldprog) { 1608c2ecf20Sopenharmony_ci oldprog = NULL; 1618c2ecf20Sopenharmony_ci if (!cls_bpf->prog) 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci if (ns->bpf_offloaded) { 1648c2ecf20Sopenharmony_ci NSIM_EA(cls_bpf->common.extack, 1658c2ecf20Sopenharmony_ci "driver and netdev offload states mismatch"); 1668c2ecf20Sopenharmony_ci return -EBUSY; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return nsim_bpf_offload(ns, cls_bpf->prog, oldprog); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint nsim_bpf_disable_tc(struct netdevsim *ns) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns)) 1768c2ecf20Sopenharmony_ci return -EBUSY; 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci if (!nsim_xdp_offload_active(ns) && !bpf->prog) 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) { 1858c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "TC program is already loaded"); 1868c2ecf20Sopenharmony_ci return -EBUSY; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns)); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int 1938c2ecf20Sopenharmony_cinsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf, 1948c2ecf20Sopenharmony_ci struct xdp_attachment_info *xdp) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci int err; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) { 1998c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS"); 2008c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) { 2038c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS"); 2048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (bpf->command == XDP_SETUP_PROG_HW) { 2088c2ecf20Sopenharmony_ci err = nsim_xdp_offload_prog(ns, bpf); 2098c2ecf20Sopenharmony_ci if (err) 2108c2ecf20Sopenharmony_ci return err; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci xdp_attachment_setup(xdp, bpf); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int nsim_bpf_create_prog(struct nsim_dev *nsim_dev, 2198c2ecf20Sopenharmony_ci struct bpf_prog *prog) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state; 2228c2ecf20Sopenharmony_ci char name[16]; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!state) 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci state->nsim_dev = nsim_dev; 2308c2ecf20Sopenharmony_ci state->prog = prog; 2318c2ecf20Sopenharmony_ci state->state = "verify"; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Program id is not populated yet when we create the state. */ 2348c2ecf20Sopenharmony_ci sprintf(name, "%u", nsim_dev->prog_id_gen++); 2358c2ecf20Sopenharmony_ci state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs); 2368c2ecf20Sopenharmony_ci if (IS_ERR(state->ddir)) { 2378c2ecf20Sopenharmony_ci ret = PTR_ERR(state->ddir); 2388c2ecf20Sopenharmony_ci kfree(state); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id); 2438c2ecf20Sopenharmony_ci debugfs_create_file("state", 0400, state->ddir, 2448c2ecf20Sopenharmony_ci &state->state, &nsim_bpf_string_fops); 2458c2ecf20Sopenharmony_ci debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci list_add_tail(&state->l, &nsim_dev->bpf_bound_progs); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci prog->aux->offload->dev_priv = state; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int nsim_bpf_verifier_prep(struct bpf_prog *prog) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct nsim_dev *nsim_dev = 2578c2ecf20Sopenharmony_ci bpf_offload_dev_priv(prog->aux->offload->offdev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!nsim_dev->bpf_bind_accept) 2608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return nsim_bpf_create_prog(nsim_dev, prog); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int nsim_bpf_translate(struct bpf_prog *prog) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci state->state = "xlated"; 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void nsim_bpf_destroy_prog(struct bpf_prog *prog) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci state = prog->aux->offload->dev_priv; 2788c2ecf20Sopenharmony_ci WARN(state->is_loaded, 2798c2ecf20Sopenharmony_ci "offload state destroyed while program still bound"); 2808c2ecf20Sopenharmony_ci debugfs_remove_recursive(state->ddir); 2818c2ecf20Sopenharmony_ci list_del(&state->l); 2828c2ecf20Sopenharmony_ci kfree(state); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct bpf_prog_offload_ops nsim_bpf_dev_ops = { 2868c2ecf20Sopenharmony_ci .insn_hook = nsim_bpf_verify_insn, 2878c2ecf20Sopenharmony_ci .finalize = nsim_bpf_finalize, 2888c2ecf20Sopenharmony_ci .prepare = nsim_bpf_verifier_prep, 2898c2ecf20Sopenharmony_ci .translate = nsim_bpf_translate, 2908c2ecf20Sopenharmony_ci .destroy = nsim_bpf_destroy_prog, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci if (bpf->prog && bpf->prog->aux->offload) { 2968c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv"); 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci if (ns->netdev->mtu > NSIM_XDP_MAX_MTU) { 3008c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled"); 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int 3078c2ecf20Sopenharmony_cinsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct nsim_bpf_bound_prog *state; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!bpf->prog) 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!bpf->prog->aux->offload) { 3158c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "xdpoffload of non-bound program"); 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci if (!bpf_offload_dev_match(bpf->prog, ns->netdev)) { 3198c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "program bound to different dev"); 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci state = bpf->prog->aux->offload->dev_priv; 3248c2ecf20Sopenharmony_ci if (WARN_ON(strcmp(state->state, "xlated"))) { 3258c2ecf20Sopenharmony_ci NSIM_EA(bpf->extack, "offloading program in bad state"); 3268c2ecf20Sopenharmony_ci return -EINVAL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic bool 3328c2ecf20Sopenharmony_cinsim_map_key_match(struct bpf_map *map, struct nsim_map_entry *e, void *key) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci return e->key && !memcmp(key, e->key, map->key_size); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int nsim_map_key_find(struct bpf_offloaded_map *offmap, void *key) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 3408c2ecf20Sopenharmony_ci unsigned int i; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) 3438c2ecf20Sopenharmony_ci if (nsim_map_key_match(&offmap->map, &nmap->entry[i], key)) 3448c2ecf20Sopenharmony_ci return i; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return -ENOENT; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int 3508c2ecf20Sopenharmony_cinsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci nmap->entry[idx].key = kmalloc(offmap->map.key_size, 3558c2ecf20Sopenharmony_ci GFP_KERNEL_ACCOUNT | __GFP_NOWARN); 3568c2ecf20Sopenharmony_ci if (!nmap->entry[idx].key) 3578c2ecf20Sopenharmony_ci return -ENOMEM; 3588c2ecf20Sopenharmony_ci nmap->entry[idx].value = kmalloc(offmap->map.value_size, 3598c2ecf20Sopenharmony_ci GFP_KERNEL_ACCOUNT | __GFP_NOWARN); 3608c2ecf20Sopenharmony_ci if (!nmap->entry[idx].value) { 3618c2ecf20Sopenharmony_ci kfree(nmap->entry[idx].key); 3628c2ecf20Sopenharmony_ci nmap->entry[idx].key = NULL; 3638c2ecf20Sopenharmony_ci return -ENOMEM; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int 3708c2ecf20Sopenharmony_cinsim_map_get_next_key(struct bpf_offloaded_map *offmap, 3718c2ecf20Sopenharmony_ci void *key, void *next_key) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 3748c2ecf20Sopenharmony_ci int idx = -ENOENT; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci mutex_lock(&nmap->mutex); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (key) 3798c2ecf20Sopenharmony_ci idx = nsim_map_key_find(offmap, key); 3808c2ecf20Sopenharmony_ci if (idx == -ENOENT) 3818c2ecf20Sopenharmony_ci idx = 0; 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci idx++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci for (; idx < ARRAY_SIZE(nmap->entry); idx++) { 3868c2ecf20Sopenharmony_ci if (nmap->entry[idx].key) { 3878c2ecf20Sopenharmony_ci memcpy(next_key, nmap->entry[idx].key, 3888c2ecf20Sopenharmony_ci offmap->map.key_size); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci mutex_unlock(&nmap->mutex); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (idx == ARRAY_SIZE(nmap->entry)) 3968c2ecf20Sopenharmony_ci return -ENOENT; 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int 4018c2ecf20Sopenharmony_cinsim_map_lookup_elem(struct bpf_offloaded_map *offmap, void *key, void *value) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 4048c2ecf20Sopenharmony_ci int idx; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci mutex_lock(&nmap->mutex); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci idx = nsim_map_key_find(offmap, key); 4098c2ecf20Sopenharmony_ci if (idx >= 0) 4108c2ecf20Sopenharmony_ci memcpy(value, nmap->entry[idx].value, offmap->map.value_size); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci mutex_unlock(&nmap->mutex); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return idx < 0 ? idx : 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int 4188c2ecf20Sopenharmony_cinsim_map_update_elem(struct bpf_offloaded_map *offmap, 4198c2ecf20Sopenharmony_ci void *key, void *value, u64 flags) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 4228c2ecf20Sopenharmony_ci int idx, err = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci mutex_lock(&nmap->mutex); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci idx = nsim_map_key_find(offmap, key); 4278c2ecf20Sopenharmony_ci if (idx < 0 && flags == BPF_EXIST) { 4288c2ecf20Sopenharmony_ci err = idx; 4298c2ecf20Sopenharmony_ci goto exit_unlock; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci if (idx >= 0 && flags == BPF_NOEXIST) { 4328c2ecf20Sopenharmony_ci err = -EEXIST; 4338c2ecf20Sopenharmony_ci goto exit_unlock; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (idx < 0) { 4378c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(nmap->entry); idx++) 4388c2ecf20Sopenharmony_ci if (!nmap->entry[idx].key) 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci if (idx == ARRAY_SIZE(nmap->entry)) { 4418c2ecf20Sopenharmony_ci err = -E2BIG; 4428c2ecf20Sopenharmony_ci goto exit_unlock; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci err = nsim_map_alloc_elem(offmap, idx); 4468c2ecf20Sopenharmony_ci if (err) 4478c2ecf20Sopenharmony_ci goto exit_unlock; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci memcpy(nmap->entry[idx].key, key, offmap->map.key_size); 4518c2ecf20Sopenharmony_ci memcpy(nmap->entry[idx].value, value, offmap->map.value_size); 4528c2ecf20Sopenharmony_ciexit_unlock: 4538c2ecf20Sopenharmony_ci mutex_unlock(&nmap->mutex); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return err; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int nsim_map_delete_elem(struct bpf_offloaded_map *offmap, void *key) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 4618c2ecf20Sopenharmony_ci int idx; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mutex_lock(&nmap->mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci idx = nsim_map_key_find(offmap, key); 4698c2ecf20Sopenharmony_ci if (idx >= 0) { 4708c2ecf20Sopenharmony_ci kfree(nmap->entry[idx].key); 4718c2ecf20Sopenharmony_ci kfree(nmap->entry[idx].value); 4728c2ecf20Sopenharmony_ci memset(&nmap->entry[idx], 0, sizeof(nmap->entry[idx])); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci mutex_unlock(&nmap->mutex); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return idx < 0 ? idx : 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic const struct bpf_map_dev_ops nsim_bpf_map_ops = { 4818c2ecf20Sopenharmony_ci .map_get_next_key = nsim_map_get_next_key, 4828c2ecf20Sopenharmony_ci .map_lookup_elem = nsim_map_lookup_elem, 4838c2ecf20Sopenharmony_ci .map_update_elem = nsim_map_update_elem, 4848c2ecf20Sopenharmony_ci .map_delete_elem = nsim_map_delete_elem, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int 4888c2ecf20Sopenharmony_cinsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap; 4918c2ecf20Sopenharmony_ci int i, err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (WARN_ON(offmap->map.map_type != BPF_MAP_TYPE_ARRAY && 4948c2ecf20Sopenharmony_ci offmap->map.map_type != BPF_MAP_TYPE_HASH)) 4958c2ecf20Sopenharmony_ci return -EINVAL; 4968c2ecf20Sopenharmony_ci if (offmap->map.max_entries > NSIM_BPF_MAX_KEYS) 4978c2ecf20Sopenharmony_ci return -ENOMEM; 4988c2ecf20Sopenharmony_ci if (offmap->map.map_flags) 4998c2ecf20Sopenharmony_ci return -EINVAL; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci nmap = kzalloc(sizeof(*nmap), GFP_KERNEL_ACCOUNT); 5028c2ecf20Sopenharmony_ci if (!nmap) 5038c2ecf20Sopenharmony_ci return -ENOMEM; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci offmap->dev_priv = nmap; 5068c2ecf20Sopenharmony_ci nmap->ns = ns; 5078c2ecf20Sopenharmony_ci nmap->map = offmap; 5088c2ecf20Sopenharmony_ci mutex_init(&nmap->mutex); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) { 5118c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) { 5128c2ecf20Sopenharmony_ci u32 *key; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci err = nsim_map_alloc_elem(offmap, i); 5158c2ecf20Sopenharmony_ci if (err) 5168c2ecf20Sopenharmony_ci goto err_free; 5178c2ecf20Sopenharmony_ci key = nmap->entry[i].key; 5188c2ecf20Sopenharmony_ci *key = i; 5198c2ecf20Sopenharmony_ci memset(nmap->entry[i].value, 0, offmap->map.value_size); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci offmap->dev_ops = &nsim_bpf_map_ops; 5248c2ecf20Sopenharmony_ci list_add_tail(&nmap->l, &ns->nsim_dev->bpf_bound_maps); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cierr_free: 5298c2ecf20Sopenharmony_ci while (--i >= 0) { 5308c2ecf20Sopenharmony_ci kfree(nmap->entry[i].key); 5318c2ecf20Sopenharmony_ci kfree(nmap->entry[i].value); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci kfree(nmap); 5348c2ecf20Sopenharmony_ci return err; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void nsim_bpf_map_free(struct bpf_offloaded_map *offmap) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct nsim_bpf_bound_map *nmap = offmap->dev_priv; 5408c2ecf20Sopenharmony_ci unsigned int i; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) { 5438c2ecf20Sopenharmony_ci kfree(nmap->entry[i].key); 5448c2ecf20Sopenharmony_ci kfree(nmap->entry[i].value); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci list_del_init(&nmap->l); 5478c2ecf20Sopenharmony_ci mutex_destroy(&nmap->mutex); 5488c2ecf20Sopenharmony_ci kfree(nmap); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ciint nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct netdevsim *ns = netdev_priv(dev); 5548c2ecf20Sopenharmony_ci int err; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ASSERT_RTNL(); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci switch (bpf->command) { 5598c2ecf20Sopenharmony_ci case XDP_SETUP_PROG: 5608c2ecf20Sopenharmony_ci err = nsim_setup_prog_checks(ns, bpf); 5618c2ecf20Sopenharmony_ci if (err) 5628c2ecf20Sopenharmony_ci return err; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return nsim_xdp_set_prog(ns, bpf, &ns->xdp); 5658c2ecf20Sopenharmony_ci case XDP_SETUP_PROG_HW: 5668c2ecf20Sopenharmony_ci err = nsim_setup_prog_hw_checks(ns, bpf); 5678c2ecf20Sopenharmony_ci if (err) 5688c2ecf20Sopenharmony_ci return err; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw); 5718c2ecf20Sopenharmony_ci case BPF_OFFLOAD_MAP_ALLOC: 5728c2ecf20Sopenharmony_ci if (!ns->bpf_map_accept) 5738c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return nsim_bpf_map_alloc(ns, bpf->offmap); 5768c2ecf20Sopenharmony_ci case BPF_OFFLOAD_MAP_FREE: 5778c2ecf20Sopenharmony_ci nsim_bpf_map_free(bpf->offmap); 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciint nsim_bpf_dev_init(struct nsim_dev *nsim_dev) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci int err; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nsim_dev->bpf_bound_progs); 5898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nsim_dev->bpf_bound_maps); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs", 5928c2ecf20Sopenharmony_ci nsim_dev->ddir); 5938c2ecf20Sopenharmony_ci if (IS_ERR(nsim_dev->ddir_bpf_bound_progs)) 5948c2ecf20Sopenharmony_ci return PTR_ERR(nsim_dev->ddir_bpf_bound_progs); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev); 5978c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev); 5988c2ecf20Sopenharmony_ci if (err) 5998c2ecf20Sopenharmony_ci return err; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci nsim_dev->bpf_bind_accept = true; 6028c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_bind_accept", 0600, nsim_dev->ddir, 6038c2ecf20Sopenharmony_ci &nsim_dev->bpf_bind_accept); 6048c2ecf20Sopenharmony_ci debugfs_create_u32("bpf_bind_verifier_delay", 0600, nsim_dev->ddir, 6058c2ecf20Sopenharmony_ci &nsim_dev->bpf_bind_verifier_delay); 6068c2ecf20Sopenharmony_ci nsim_dev->bpf_bind_verifier_accept = true; 6078c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_bind_verifier_accept", 0600, nsim_dev->ddir, 6088c2ecf20Sopenharmony_ci &nsim_dev->bpf_bind_verifier_accept); 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_civoid nsim_bpf_dev_exit(struct nsim_dev *nsim_dev) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&nsim_dev->bpf_bound_progs)); 6158c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&nsim_dev->bpf_bound_maps)); 6168c2ecf20Sopenharmony_ci bpf_offload_dev_destroy(nsim_dev->bpf_dev); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ciint nsim_bpf_init(struct netdevsim *ns) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct dentry *ddir = ns->nsim_dev_port->ddir; 6228c2ecf20Sopenharmony_ci int err; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci err = bpf_offload_dev_netdev_register(ns->nsim_dev->bpf_dev, 6258c2ecf20Sopenharmony_ci ns->netdev); 6268c2ecf20Sopenharmony_ci if (err) 6278c2ecf20Sopenharmony_ci return err; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci debugfs_create_u32("bpf_offloaded_id", 0400, ddir, 6308c2ecf20Sopenharmony_ci &ns->bpf_offloaded_id); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ns->bpf_tc_accept = true; 6338c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_tc_accept", 0600, ddir, 6348c2ecf20Sopenharmony_ci &ns->bpf_tc_accept); 6358c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ddir, 6368c2ecf20Sopenharmony_ci &ns->bpf_tc_non_bound_accept); 6378c2ecf20Sopenharmony_ci ns->bpf_xdpdrv_accept = true; 6388c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_xdpdrv_accept", 0600, ddir, 6398c2ecf20Sopenharmony_ci &ns->bpf_xdpdrv_accept); 6408c2ecf20Sopenharmony_ci ns->bpf_xdpoffload_accept = true; 6418c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_xdpoffload_accept", 0600, ddir, 6428c2ecf20Sopenharmony_ci &ns->bpf_xdpoffload_accept); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ns->bpf_map_accept = true; 6458c2ecf20Sopenharmony_ci debugfs_create_bool("bpf_map_accept", 0600, ddir, 6468c2ecf20Sopenharmony_ci &ns->bpf_map_accept); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return 0; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_civoid nsim_bpf_uninit(struct netdevsim *ns) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci WARN_ON(ns->xdp.prog); 6548c2ecf20Sopenharmony_ci WARN_ON(ns->xdp_hw.prog); 6558c2ecf20Sopenharmony_ci WARN_ON(ns->bpf_offloaded); 6568c2ecf20Sopenharmony_ci bpf_offload_dev_netdev_unregister(ns->nsim_dev->bpf_dev, ns->netdev); 6578c2ecf20Sopenharmony_ci} 658