1a39e17b2SJakub Kicinski /* 20cd3cbedSJakub Kicinski * Copyright (C) 2017-2018 Netronome Systems, Inc. 3a39e17b2SJakub Kicinski * 4a39e17b2SJakub Kicinski * This software is licensed under the GNU General License Version 2, 5a39e17b2SJakub Kicinski * June 1991 as shown in the file COPYING in the top-level directory of this 6a39e17b2SJakub Kicinski * source tree. 7a39e17b2SJakub Kicinski * 8a39e17b2SJakub Kicinski * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 9a39e17b2SJakub Kicinski * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 10a39e17b2SJakub Kicinski * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 11a39e17b2SJakub Kicinski * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 12a39e17b2SJakub Kicinski * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 13a39e17b2SJakub Kicinski * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 14a39e17b2SJakub Kicinski */ 15a39e17b2SJakub Kicinski 16ab3f0063SJakub Kicinski #include <linux/bpf.h> 17ab3f0063SJakub Kicinski #include <linux/bpf_verifier.h> 18ab3f0063SJakub Kicinski #include <linux/bug.h> 19675fc275SJakub Kicinski #include <linux/kdev_t.h> 20ab3f0063SJakub Kicinski #include <linux/list.h> 21*9fd7c555SJakub Kicinski #include <linux/lockdep.h> 22ab3f0063SJakub Kicinski #include <linux/netdevice.h> 23ab3f0063SJakub Kicinski #include <linux/printk.h> 24675fc275SJakub Kicinski #include <linux/proc_ns.h> 25*9fd7c555SJakub Kicinski #include <linux/rhashtable.h> 26ab3f0063SJakub Kicinski #include <linux/rtnetlink.h> 27e0d3974aSJakub Kicinski #include <linux/rwsem.h> 28ab3f0063SJakub Kicinski 29*9fd7c555SJakub Kicinski /* Protects offdevs, members of bpf_offload_netdev and offload members 30a3884572SJakub Kicinski * of all progs. 31e0d3974aSJakub Kicinski * RTNL lock cannot be taken when holding this lock. 32e0d3974aSJakub Kicinski */ 33e0d3974aSJakub Kicinski static DECLARE_RWSEM(bpf_devs_lock); 34*9fd7c555SJakub Kicinski 35*9fd7c555SJakub Kicinski struct bpf_offload_netdev { 36*9fd7c555SJakub Kicinski struct rhash_head l; 37*9fd7c555SJakub Kicinski struct net_device *netdev; 38*9fd7c555SJakub Kicinski struct list_head progs; 39*9fd7c555SJakub Kicinski struct list_head maps; 40*9fd7c555SJakub Kicinski }; 41*9fd7c555SJakub Kicinski 42*9fd7c555SJakub Kicinski static const struct rhashtable_params offdevs_params = { 43*9fd7c555SJakub Kicinski .nelem_hint = 4, 44*9fd7c555SJakub Kicinski .key_len = sizeof(struct net_device *), 45*9fd7c555SJakub Kicinski .key_offset = offsetof(struct bpf_offload_netdev, netdev), 46*9fd7c555SJakub Kicinski .head_offset = offsetof(struct bpf_offload_netdev, l), 47*9fd7c555SJakub Kicinski .automatic_shrinking = true, 48*9fd7c555SJakub Kicinski }; 49*9fd7c555SJakub Kicinski 50*9fd7c555SJakub Kicinski static struct rhashtable offdevs; 51*9fd7c555SJakub Kicinski static bool offdevs_inited; 52ab3f0063SJakub Kicinski 535bc2d55cSJakub Kicinski static int bpf_dev_offload_check(struct net_device *netdev) 545bc2d55cSJakub Kicinski { 555bc2d55cSJakub Kicinski if (!netdev) 565bc2d55cSJakub Kicinski return -EINVAL; 575bc2d55cSJakub Kicinski if (!netdev->netdev_ops->ndo_bpf) 585bc2d55cSJakub Kicinski return -EOPNOTSUPP; 595bc2d55cSJakub Kicinski return 0; 605bc2d55cSJakub Kicinski } 615bc2d55cSJakub Kicinski 62*9fd7c555SJakub Kicinski static struct bpf_offload_netdev * 63*9fd7c555SJakub Kicinski bpf_offload_find_netdev(struct net_device *netdev) 64*9fd7c555SJakub Kicinski { 65*9fd7c555SJakub Kicinski lockdep_assert_held(&bpf_devs_lock); 66*9fd7c555SJakub Kicinski 67*9fd7c555SJakub Kicinski if (!offdevs_inited) 68*9fd7c555SJakub Kicinski return NULL; 69*9fd7c555SJakub Kicinski return rhashtable_lookup_fast(&offdevs, &netdev, offdevs_params); 70*9fd7c555SJakub Kicinski } 71*9fd7c555SJakub Kicinski 72ab3f0063SJakub Kicinski int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) 73ab3f0063SJakub Kicinski { 74*9fd7c555SJakub Kicinski struct bpf_offload_netdev *ondev; 750a9c1991SJakub Kicinski struct bpf_prog_offload *offload; 765bc2d55cSJakub Kicinski int err; 77ab3f0063SJakub Kicinski 78649f11dcSJakub Kicinski if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS && 79649f11dcSJakub Kicinski attr->prog_type != BPF_PROG_TYPE_XDP) 80649f11dcSJakub Kicinski return -EINVAL; 81ab3f0063SJakub Kicinski 82ab3f0063SJakub Kicinski if (attr->prog_flags) 83ab3f0063SJakub Kicinski return -EINVAL; 84ab3f0063SJakub Kicinski 85ab3f0063SJakub Kicinski offload = kzalloc(sizeof(*offload), GFP_USER); 86ab3f0063SJakub Kicinski if (!offload) 87ab3f0063SJakub Kicinski return -ENOMEM; 88ab3f0063SJakub Kicinski 89ab3f0063SJakub Kicinski offload->prog = prog; 90ab3f0063SJakub Kicinski 91e0d3974aSJakub Kicinski offload->netdev = dev_get_by_index(current->nsproxy->net_ns, 92e0d3974aSJakub Kicinski attr->prog_ifindex); 935bc2d55cSJakub Kicinski err = bpf_dev_offload_check(offload->netdev); 945bc2d55cSJakub Kicinski if (err) 955bc2d55cSJakub Kicinski goto err_maybe_put; 96ab3f0063SJakub Kicinski 97e0d3974aSJakub Kicinski down_write(&bpf_devs_lock); 98*9fd7c555SJakub Kicinski ondev = bpf_offload_find_netdev(offload->netdev); 99*9fd7c555SJakub Kicinski if (!ondev) { 1005bc2d55cSJakub Kicinski err = -EINVAL; 101e0d3974aSJakub Kicinski goto err_unlock; 1025bc2d55cSJakub Kicinski } 103ab3f0063SJakub Kicinski prog->aux->offload = offload; 104*9fd7c555SJakub Kicinski list_add_tail(&offload->offloads, &ondev->progs); 105e0d3974aSJakub Kicinski dev_put(offload->netdev); 106e0d3974aSJakub Kicinski up_write(&bpf_devs_lock); 107ab3f0063SJakub Kicinski 108ab3f0063SJakub Kicinski return 0; 109e0d3974aSJakub Kicinski err_unlock: 110e0d3974aSJakub Kicinski up_write(&bpf_devs_lock); 1115bc2d55cSJakub Kicinski err_maybe_put: 1125bc2d55cSJakub Kicinski if (offload->netdev) 113e0d3974aSJakub Kicinski dev_put(offload->netdev); 114e0d3974aSJakub Kicinski kfree(offload); 1155bc2d55cSJakub Kicinski return err; 116ab3f0063SJakub Kicinski } 117ab3f0063SJakub Kicinski 118ab3f0063SJakub Kicinski static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, 119ab3f0063SJakub Kicinski struct netdev_bpf *data) 120ab3f0063SJakub Kicinski { 1210a9c1991SJakub Kicinski struct bpf_prog_offload *offload = prog->aux->offload; 122ce3b9db4SJakub Kicinski struct net_device *netdev; 123ab3f0063SJakub Kicinski 124ab3f0063SJakub Kicinski ASSERT_RTNL(); 125ab3f0063SJakub Kicinski 126ce3b9db4SJakub Kicinski if (!offload) 127ab3f0063SJakub Kicinski return -ENODEV; 128ce3b9db4SJakub Kicinski netdev = offload->netdev; 129ab3f0063SJakub Kicinski 130ab3f0063SJakub Kicinski data->command = cmd; 131ab3f0063SJakub Kicinski 132ab3f0063SJakub Kicinski return netdev->netdev_ops->ndo_bpf(netdev, data); 133ab3f0063SJakub Kicinski } 134ab3f0063SJakub Kicinski 135ab3f0063SJakub Kicinski int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) 136ab3f0063SJakub Kicinski { 137ab3f0063SJakub Kicinski struct netdev_bpf data = {}; 138ab3f0063SJakub Kicinski int err; 139ab3f0063SJakub Kicinski 140ab3f0063SJakub Kicinski data.verifier.prog = env->prog; 141ab3f0063SJakub Kicinski 142ab3f0063SJakub Kicinski rtnl_lock(); 143ab3f0063SJakub Kicinski err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data); 144ab3f0063SJakub Kicinski if (err) 145ab3f0063SJakub Kicinski goto exit_unlock; 146ab3f0063SJakub Kicinski 147cae1927cSJakub Kicinski env->prog->aux->offload->dev_ops = data.verifier.ops; 148ab3f0063SJakub Kicinski env->prog->aux->offload->dev_state = true; 149ab3f0063SJakub Kicinski exit_unlock: 150ab3f0063SJakub Kicinski rtnl_unlock(); 151ab3f0063SJakub Kicinski return err; 152ab3f0063SJakub Kicinski } 153ab3f0063SJakub Kicinski 154cae1927cSJakub Kicinski int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env, 155cae1927cSJakub Kicinski int insn_idx, int prev_insn_idx) 156cae1927cSJakub Kicinski { 1570a9c1991SJakub Kicinski struct bpf_prog_offload *offload; 158cae1927cSJakub Kicinski int ret = -ENODEV; 159cae1927cSJakub Kicinski 160cae1927cSJakub Kicinski down_read(&bpf_devs_lock); 161cae1927cSJakub Kicinski offload = env->prog->aux->offload; 162ce3b9db4SJakub Kicinski if (offload) 163cae1927cSJakub Kicinski ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); 164cae1927cSJakub Kicinski up_read(&bpf_devs_lock); 165cae1927cSJakub Kicinski 166cae1927cSJakub Kicinski return ret; 167cae1927cSJakub Kicinski } 168cae1927cSJakub Kicinski 169ab3f0063SJakub Kicinski static void __bpf_prog_offload_destroy(struct bpf_prog *prog) 170ab3f0063SJakub Kicinski { 1710a9c1991SJakub Kicinski struct bpf_prog_offload *offload = prog->aux->offload; 172ab3f0063SJakub Kicinski struct netdev_bpf data = {}; 173ab3f0063SJakub Kicinski 174ab3f0063SJakub Kicinski data.offload.prog = prog; 175ab3f0063SJakub Kicinski 176ab3f0063SJakub Kicinski if (offload->dev_state) 177ab3f0063SJakub Kicinski WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); 178ab3f0063SJakub Kicinski 179ad8ad79fSJakub Kicinski /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */ 180ad8ad79fSJakub Kicinski bpf_prog_free_id(prog, true); 181ad8ad79fSJakub Kicinski 182ab3f0063SJakub Kicinski list_del_init(&offload->offloads); 183ce3b9db4SJakub Kicinski kfree(offload); 184ce3b9db4SJakub Kicinski prog->aux->offload = NULL; 185ab3f0063SJakub Kicinski } 186ab3f0063SJakub Kicinski 187ab3f0063SJakub Kicinski void bpf_prog_offload_destroy(struct bpf_prog *prog) 188ab3f0063SJakub Kicinski { 189ab3f0063SJakub Kicinski rtnl_lock(); 190e0d3974aSJakub Kicinski down_write(&bpf_devs_lock); 191ce3b9db4SJakub Kicinski if (prog->aux->offload) 192ab3f0063SJakub Kicinski __bpf_prog_offload_destroy(prog); 193e0d3974aSJakub Kicinski up_write(&bpf_devs_lock); 194ab3f0063SJakub Kicinski rtnl_unlock(); 195ab3f0063SJakub Kicinski } 196ab3f0063SJakub Kicinski 197ab3f0063SJakub Kicinski static int bpf_prog_offload_translate(struct bpf_prog *prog) 198ab3f0063SJakub Kicinski { 199ab3f0063SJakub Kicinski struct netdev_bpf data = {}; 200ab3f0063SJakub Kicinski int ret; 201ab3f0063SJakub Kicinski 202ab3f0063SJakub Kicinski data.offload.prog = prog; 203ab3f0063SJakub Kicinski 204ab3f0063SJakub Kicinski rtnl_lock(); 205ab3f0063SJakub Kicinski ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); 206ab3f0063SJakub Kicinski rtnl_unlock(); 207ab3f0063SJakub Kicinski 208ab3f0063SJakub Kicinski return ret; 209ab3f0063SJakub Kicinski } 210ab3f0063SJakub Kicinski 211ab3f0063SJakub Kicinski static unsigned int bpf_prog_warn_on_exec(const void *ctx, 212ab3f0063SJakub Kicinski const struct bpf_insn *insn) 213ab3f0063SJakub Kicinski { 214ab3f0063SJakub Kicinski WARN(1, "attempt to execute device eBPF program on the host!"); 215ab3f0063SJakub Kicinski return 0; 216ab3f0063SJakub Kicinski } 217ab3f0063SJakub Kicinski 218ab3f0063SJakub Kicinski int bpf_prog_offload_compile(struct bpf_prog *prog) 219ab3f0063SJakub Kicinski { 220ab3f0063SJakub Kicinski prog->bpf_func = bpf_prog_warn_on_exec; 221ab3f0063SJakub Kicinski 222ab3f0063SJakub Kicinski return bpf_prog_offload_translate(prog); 223ab3f0063SJakub Kicinski } 224ab3f0063SJakub Kicinski 225675fc275SJakub Kicinski struct ns_get_path_bpf_prog_args { 226675fc275SJakub Kicinski struct bpf_prog *prog; 227675fc275SJakub Kicinski struct bpf_prog_info *info; 228675fc275SJakub Kicinski }; 229675fc275SJakub Kicinski 230675fc275SJakub Kicinski static struct ns_common *bpf_prog_offload_info_fill_ns(void *private_data) 231675fc275SJakub Kicinski { 232675fc275SJakub Kicinski struct ns_get_path_bpf_prog_args *args = private_data; 233675fc275SJakub Kicinski struct bpf_prog_aux *aux = args->prog->aux; 234675fc275SJakub Kicinski struct ns_common *ns; 235675fc275SJakub Kicinski struct net *net; 236675fc275SJakub Kicinski 237675fc275SJakub Kicinski rtnl_lock(); 238675fc275SJakub Kicinski down_read(&bpf_devs_lock); 239675fc275SJakub Kicinski 240675fc275SJakub Kicinski if (aux->offload) { 241675fc275SJakub Kicinski args->info->ifindex = aux->offload->netdev->ifindex; 242675fc275SJakub Kicinski net = dev_net(aux->offload->netdev); 243675fc275SJakub Kicinski get_net(net); 244675fc275SJakub Kicinski ns = &net->ns; 245675fc275SJakub Kicinski } else { 246675fc275SJakub Kicinski args->info->ifindex = 0; 247675fc275SJakub Kicinski ns = NULL; 248675fc275SJakub Kicinski } 249675fc275SJakub Kicinski 250675fc275SJakub Kicinski up_read(&bpf_devs_lock); 251675fc275SJakub Kicinski rtnl_unlock(); 252675fc275SJakub Kicinski 253675fc275SJakub Kicinski return ns; 254675fc275SJakub Kicinski } 255675fc275SJakub Kicinski 256675fc275SJakub Kicinski int bpf_prog_offload_info_fill(struct bpf_prog_info *info, 257675fc275SJakub Kicinski struct bpf_prog *prog) 258675fc275SJakub Kicinski { 259675fc275SJakub Kicinski struct ns_get_path_bpf_prog_args args = { 260675fc275SJakub Kicinski .prog = prog, 261675fc275SJakub Kicinski .info = info, 262675fc275SJakub Kicinski }; 263fcfb126dSJiong Wang struct bpf_prog_aux *aux = prog->aux; 264675fc275SJakub Kicinski struct inode *ns_inode; 265675fc275SJakub Kicinski struct path ns_path; 266fcfb126dSJiong Wang char __user *uinsns; 267675fc275SJakub Kicinski void *res; 268fcfb126dSJiong Wang u32 ulen; 269675fc275SJakub Kicinski 270675fc275SJakub Kicinski res = ns_get_path_cb(&ns_path, bpf_prog_offload_info_fill_ns, &args); 271675fc275SJakub Kicinski if (IS_ERR(res)) { 272675fc275SJakub Kicinski if (!info->ifindex) 273675fc275SJakub Kicinski return -ENODEV; 274675fc275SJakub Kicinski return PTR_ERR(res); 275675fc275SJakub Kicinski } 276675fc275SJakub Kicinski 277fcfb126dSJiong Wang down_read(&bpf_devs_lock); 278fcfb126dSJiong Wang 279fcfb126dSJiong Wang if (!aux->offload) { 280fcfb126dSJiong Wang up_read(&bpf_devs_lock); 281fcfb126dSJiong Wang return -ENODEV; 282fcfb126dSJiong Wang } 283fcfb126dSJiong Wang 284fcfb126dSJiong Wang ulen = info->jited_prog_len; 285fcfb126dSJiong Wang info->jited_prog_len = aux->offload->jited_len; 286fcfb126dSJiong Wang if (info->jited_prog_len & ulen) { 287fcfb126dSJiong Wang uinsns = u64_to_user_ptr(info->jited_prog_insns); 288fcfb126dSJiong Wang ulen = min_t(u32, info->jited_prog_len, ulen); 289fcfb126dSJiong Wang if (copy_to_user(uinsns, aux->offload->jited_image, ulen)) { 290fcfb126dSJiong Wang up_read(&bpf_devs_lock); 291fcfb126dSJiong Wang return -EFAULT; 292fcfb126dSJiong Wang } 293fcfb126dSJiong Wang } 294fcfb126dSJiong Wang 295fcfb126dSJiong Wang up_read(&bpf_devs_lock); 296fcfb126dSJiong Wang 297675fc275SJakub Kicinski ns_inode = ns_path.dentry->d_inode; 298675fc275SJakub Kicinski info->netns_dev = new_encode_dev(ns_inode->i_sb->s_dev); 299675fc275SJakub Kicinski info->netns_ino = ns_inode->i_ino; 300675fc275SJakub Kicinski path_put(&ns_path); 301675fc275SJakub Kicinski 302675fc275SJakub Kicinski return 0; 303675fc275SJakub Kicinski } 304675fc275SJakub Kicinski 305ab3f0063SJakub Kicinski const struct bpf_prog_ops bpf_offload_prog_ops = { 306ab3f0063SJakub Kicinski }; 307ab3f0063SJakub Kicinski 308a3884572SJakub Kicinski static int bpf_map_offload_ndo(struct bpf_offloaded_map *offmap, 309a3884572SJakub Kicinski enum bpf_netdev_command cmd) 310a3884572SJakub Kicinski { 311a3884572SJakub Kicinski struct netdev_bpf data = {}; 312a3884572SJakub Kicinski struct net_device *netdev; 313a3884572SJakub Kicinski 314a3884572SJakub Kicinski ASSERT_RTNL(); 315a3884572SJakub Kicinski 316a3884572SJakub Kicinski data.command = cmd; 317a3884572SJakub Kicinski data.offmap = offmap; 318a3884572SJakub Kicinski /* Caller must make sure netdev is valid */ 319a3884572SJakub Kicinski netdev = offmap->netdev; 320a3884572SJakub Kicinski 321a3884572SJakub Kicinski return netdev->netdev_ops->ndo_bpf(netdev, &data); 322a3884572SJakub Kicinski } 323a3884572SJakub Kicinski 324a3884572SJakub Kicinski struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr) 325a3884572SJakub Kicinski { 326a3884572SJakub Kicinski struct net *net = current->nsproxy->net_ns; 327*9fd7c555SJakub Kicinski struct bpf_offload_netdev *ondev; 328a3884572SJakub Kicinski struct bpf_offloaded_map *offmap; 329a3884572SJakub Kicinski int err; 330a3884572SJakub Kicinski 331a3884572SJakub Kicinski if (!capable(CAP_SYS_ADMIN)) 332a3884572SJakub Kicinski return ERR_PTR(-EPERM); 3337a0ef693SJakub Kicinski if (attr->map_type != BPF_MAP_TYPE_ARRAY && 3347a0ef693SJakub Kicinski attr->map_type != BPF_MAP_TYPE_HASH) 335a3884572SJakub Kicinski return ERR_PTR(-EINVAL); 336a3884572SJakub Kicinski 337a3884572SJakub Kicinski offmap = kzalloc(sizeof(*offmap), GFP_USER); 338a3884572SJakub Kicinski if (!offmap) 339a3884572SJakub Kicinski return ERR_PTR(-ENOMEM); 340a3884572SJakub Kicinski 341a3884572SJakub Kicinski bpf_map_init_from_attr(&offmap->map, attr); 342a3884572SJakub Kicinski 343a3884572SJakub Kicinski rtnl_lock(); 344a3884572SJakub Kicinski down_write(&bpf_devs_lock); 345a3884572SJakub Kicinski offmap->netdev = __dev_get_by_index(net, attr->map_ifindex); 346a3884572SJakub Kicinski err = bpf_dev_offload_check(offmap->netdev); 347a3884572SJakub Kicinski if (err) 348a3884572SJakub Kicinski goto err_unlock; 349a3884572SJakub Kicinski 350*9fd7c555SJakub Kicinski ondev = bpf_offload_find_netdev(offmap->netdev); 351*9fd7c555SJakub Kicinski if (!ondev) { 352*9fd7c555SJakub Kicinski err = -EINVAL; 353*9fd7c555SJakub Kicinski goto err_unlock; 354*9fd7c555SJakub Kicinski } 355*9fd7c555SJakub Kicinski 356a3884572SJakub Kicinski err = bpf_map_offload_ndo(offmap, BPF_OFFLOAD_MAP_ALLOC); 357a3884572SJakub Kicinski if (err) 358a3884572SJakub Kicinski goto err_unlock; 359a3884572SJakub Kicinski 360*9fd7c555SJakub Kicinski list_add_tail(&offmap->offloads, &ondev->maps); 361a3884572SJakub Kicinski up_write(&bpf_devs_lock); 362a3884572SJakub Kicinski rtnl_unlock(); 363a3884572SJakub Kicinski 364a3884572SJakub Kicinski return &offmap->map; 365a3884572SJakub Kicinski 366a3884572SJakub Kicinski err_unlock: 367a3884572SJakub Kicinski up_write(&bpf_devs_lock); 368a3884572SJakub Kicinski rtnl_unlock(); 369a3884572SJakub Kicinski kfree(offmap); 370a3884572SJakub Kicinski return ERR_PTR(err); 371a3884572SJakub Kicinski } 372a3884572SJakub Kicinski 373a3884572SJakub Kicinski static void __bpf_map_offload_destroy(struct bpf_offloaded_map *offmap) 374a3884572SJakub Kicinski { 375a3884572SJakub Kicinski WARN_ON(bpf_map_offload_ndo(offmap, BPF_OFFLOAD_MAP_FREE)); 376a3884572SJakub Kicinski /* Make sure BPF_MAP_GET_NEXT_ID can't find this dead map */ 377a3884572SJakub Kicinski bpf_map_free_id(&offmap->map, true); 378a3884572SJakub Kicinski list_del_init(&offmap->offloads); 379a3884572SJakub Kicinski offmap->netdev = NULL; 380a3884572SJakub Kicinski } 381a3884572SJakub Kicinski 382a3884572SJakub Kicinski void bpf_map_offload_map_free(struct bpf_map *map) 383a3884572SJakub Kicinski { 384a3884572SJakub Kicinski struct bpf_offloaded_map *offmap = map_to_offmap(map); 385a3884572SJakub Kicinski 386a3884572SJakub Kicinski rtnl_lock(); 387a3884572SJakub Kicinski down_write(&bpf_devs_lock); 388a3884572SJakub Kicinski if (offmap->netdev) 389a3884572SJakub Kicinski __bpf_map_offload_destroy(offmap); 390a3884572SJakub Kicinski up_write(&bpf_devs_lock); 391a3884572SJakub Kicinski rtnl_unlock(); 392a3884572SJakub Kicinski 393a3884572SJakub Kicinski kfree(offmap); 394a3884572SJakub Kicinski } 395a3884572SJakub Kicinski 396a3884572SJakub Kicinski int bpf_map_offload_lookup_elem(struct bpf_map *map, void *key, void *value) 397a3884572SJakub Kicinski { 398a3884572SJakub Kicinski struct bpf_offloaded_map *offmap = map_to_offmap(map); 399a3884572SJakub Kicinski int ret = -ENODEV; 400a3884572SJakub Kicinski 401a3884572SJakub Kicinski down_read(&bpf_devs_lock); 402a3884572SJakub Kicinski if (offmap->netdev) 403a3884572SJakub Kicinski ret = offmap->dev_ops->map_lookup_elem(offmap, key, value); 404a3884572SJakub Kicinski up_read(&bpf_devs_lock); 405a3884572SJakub Kicinski 406a3884572SJakub Kicinski return ret; 407a3884572SJakub Kicinski } 408a3884572SJakub Kicinski 409a3884572SJakub Kicinski int bpf_map_offload_update_elem(struct bpf_map *map, 410a3884572SJakub Kicinski void *key, void *value, u64 flags) 411a3884572SJakub Kicinski { 412a3884572SJakub Kicinski struct bpf_offloaded_map *offmap = map_to_offmap(map); 413a3884572SJakub Kicinski int ret = -ENODEV; 414a3884572SJakub Kicinski 415a3884572SJakub Kicinski if (unlikely(flags > BPF_EXIST)) 416a3884572SJakub Kicinski return -EINVAL; 417a3884572SJakub Kicinski 418a3884572SJakub Kicinski down_read(&bpf_devs_lock); 419a3884572SJakub Kicinski if (offmap->netdev) 420a3884572SJakub Kicinski ret = offmap->dev_ops->map_update_elem(offmap, key, value, 421a3884572SJakub Kicinski flags); 422a3884572SJakub Kicinski up_read(&bpf_devs_lock); 423a3884572SJakub Kicinski 424a3884572SJakub Kicinski return ret; 425a3884572SJakub Kicinski } 426a3884572SJakub Kicinski 427a3884572SJakub Kicinski int bpf_map_offload_delete_elem(struct bpf_map *map, void *key) 428a3884572SJakub Kicinski { 429a3884572SJakub Kicinski struct bpf_offloaded_map *offmap = map_to_offmap(map); 430a3884572SJakub Kicinski int ret = -ENODEV; 431a3884572SJakub Kicinski 432a3884572SJakub Kicinski down_read(&bpf_devs_lock); 433a3884572SJakub Kicinski if (offmap->netdev) 434a3884572SJakub Kicinski ret = offmap->dev_ops->map_delete_elem(offmap, key); 435a3884572SJakub Kicinski up_read(&bpf_devs_lock); 436a3884572SJakub Kicinski 437a3884572SJakub Kicinski return ret; 438a3884572SJakub Kicinski } 439a3884572SJakub Kicinski 440a3884572SJakub Kicinski int bpf_map_offload_get_next_key(struct bpf_map *map, void *key, void *next_key) 441a3884572SJakub Kicinski { 442a3884572SJakub Kicinski struct bpf_offloaded_map *offmap = map_to_offmap(map); 443a3884572SJakub Kicinski int ret = -ENODEV; 444a3884572SJakub Kicinski 445a3884572SJakub Kicinski down_read(&bpf_devs_lock); 446a3884572SJakub Kicinski if (offmap->netdev) 447a3884572SJakub Kicinski ret = offmap->dev_ops->map_get_next_key(offmap, key, next_key); 448a3884572SJakub Kicinski up_read(&bpf_devs_lock); 449a3884572SJakub Kicinski 450a3884572SJakub Kicinski return ret; 451a3884572SJakub Kicinski } 452a3884572SJakub Kicinski 45352775b33SJakub Kicinski struct ns_get_path_bpf_map_args { 45452775b33SJakub Kicinski struct bpf_offloaded_map *offmap; 45552775b33SJakub Kicinski struct bpf_map_info *info; 45652775b33SJakub Kicinski }; 45752775b33SJakub Kicinski 45852775b33SJakub Kicinski static struct ns_common *bpf_map_offload_info_fill_ns(void *private_data) 45952775b33SJakub Kicinski { 46052775b33SJakub Kicinski struct ns_get_path_bpf_map_args *args = private_data; 46152775b33SJakub Kicinski struct ns_common *ns; 46252775b33SJakub Kicinski struct net *net; 46352775b33SJakub Kicinski 46452775b33SJakub Kicinski rtnl_lock(); 46552775b33SJakub Kicinski down_read(&bpf_devs_lock); 46652775b33SJakub Kicinski 46752775b33SJakub Kicinski if (args->offmap->netdev) { 46852775b33SJakub Kicinski args->info->ifindex = args->offmap->netdev->ifindex; 46952775b33SJakub Kicinski net = dev_net(args->offmap->netdev); 47052775b33SJakub Kicinski get_net(net); 47152775b33SJakub Kicinski ns = &net->ns; 47252775b33SJakub Kicinski } else { 47352775b33SJakub Kicinski args->info->ifindex = 0; 47452775b33SJakub Kicinski ns = NULL; 47552775b33SJakub Kicinski } 47652775b33SJakub Kicinski 47752775b33SJakub Kicinski up_read(&bpf_devs_lock); 47852775b33SJakub Kicinski rtnl_unlock(); 47952775b33SJakub Kicinski 48052775b33SJakub Kicinski return ns; 48152775b33SJakub Kicinski } 48252775b33SJakub Kicinski 48352775b33SJakub Kicinski int bpf_map_offload_info_fill(struct bpf_map_info *info, struct bpf_map *map) 48452775b33SJakub Kicinski { 48552775b33SJakub Kicinski struct ns_get_path_bpf_map_args args = { 48652775b33SJakub Kicinski .offmap = map_to_offmap(map), 48752775b33SJakub Kicinski .info = info, 48852775b33SJakub Kicinski }; 48952775b33SJakub Kicinski struct inode *ns_inode; 49052775b33SJakub Kicinski struct path ns_path; 49152775b33SJakub Kicinski void *res; 49252775b33SJakub Kicinski 49352775b33SJakub Kicinski res = ns_get_path_cb(&ns_path, bpf_map_offload_info_fill_ns, &args); 49452775b33SJakub Kicinski if (IS_ERR(res)) { 49552775b33SJakub Kicinski if (!info->ifindex) 49652775b33SJakub Kicinski return -ENODEV; 49752775b33SJakub Kicinski return PTR_ERR(res); 49852775b33SJakub Kicinski } 49952775b33SJakub Kicinski 50052775b33SJakub Kicinski ns_inode = ns_path.dentry->d_inode; 50152775b33SJakub Kicinski info->netns_dev = new_encode_dev(ns_inode->i_sb->s_dev); 50252775b33SJakub Kicinski info->netns_ino = ns_inode->i_ino; 50352775b33SJakub Kicinski path_put(&ns_path); 50452775b33SJakub Kicinski 50552775b33SJakub Kicinski return 0; 50652775b33SJakub Kicinski } 50752775b33SJakub Kicinski 50809728266SJakub Kicinski bool bpf_offload_prog_map_match(struct bpf_prog *prog, struct bpf_map *map) 509a3884572SJakub Kicinski { 510a3884572SJakub Kicinski struct bpf_offloaded_map *offmap; 511a3884572SJakub Kicinski struct bpf_prog_offload *offload; 512a3884572SJakub Kicinski bool ret; 513a3884572SJakub Kicinski 5140cd3cbedSJakub Kicinski if (!bpf_prog_is_dev_bound(prog->aux)) 515a3884572SJakub Kicinski return false; 5160cd3cbedSJakub Kicinski if (!bpf_map_is_dev_bound(map)) 5170cd3cbedSJakub Kicinski return bpf_map_offload_neutral(map); 518a3884572SJakub Kicinski 519a3884572SJakub Kicinski down_read(&bpf_devs_lock); 520a3884572SJakub Kicinski offload = prog->aux->offload; 521a3884572SJakub Kicinski offmap = map_to_offmap(map); 522a3884572SJakub Kicinski 523a3884572SJakub Kicinski ret = offload && offload->netdev == offmap->netdev; 524a3884572SJakub Kicinski up_read(&bpf_devs_lock); 525a3884572SJakub Kicinski 526a3884572SJakub Kicinski return ret; 527a3884572SJakub Kicinski } 528a3884572SJakub Kicinski 529*9fd7c555SJakub Kicinski int bpf_offload_dev_netdev_register(struct net_device *netdev) 530a3884572SJakub Kicinski { 531*9fd7c555SJakub Kicinski struct bpf_offload_netdev *ondev; 532*9fd7c555SJakub Kicinski int err; 533a3884572SJakub Kicinski 534*9fd7c555SJakub Kicinski down_write(&bpf_devs_lock); 535*9fd7c555SJakub Kicinski if (!offdevs_inited) { 536*9fd7c555SJakub Kicinski err = rhashtable_init(&offdevs, &offdevs_params); 537*9fd7c555SJakub Kicinski if (err) 538*9fd7c555SJakub Kicinski return err; 539*9fd7c555SJakub Kicinski offdevs_inited = true; 540*9fd7c555SJakub Kicinski } 541*9fd7c555SJakub Kicinski up_write(&bpf_devs_lock); 542*9fd7c555SJakub Kicinski 543*9fd7c555SJakub Kicinski ondev = kzalloc(sizeof(*ondev), GFP_KERNEL); 544*9fd7c555SJakub Kicinski if (!ondev) 545*9fd7c555SJakub Kicinski return -ENOMEM; 546*9fd7c555SJakub Kicinski 547*9fd7c555SJakub Kicinski ondev->netdev = netdev; 548*9fd7c555SJakub Kicinski INIT_LIST_HEAD(&ondev->progs); 549*9fd7c555SJakub Kicinski INIT_LIST_HEAD(&ondev->maps); 550*9fd7c555SJakub Kicinski 551*9fd7c555SJakub Kicinski down_write(&bpf_devs_lock); 552*9fd7c555SJakub Kicinski err = rhashtable_insert_fast(&offdevs, &ondev->l, offdevs_params); 553*9fd7c555SJakub Kicinski if (err) { 554*9fd7c555SJakub Kicinski netdev_warn(netdev, "failed to register for BPF offload\n"); 555*9fd7c555SJakub Kicinski goto err_unlock_free; 556a3884572SJakub Kicinski } 557a3884572SJakub Kicinski 558*9fd7c555SJakub Kicinski up_write(&bpf_devs_lock); 559*9fd7c555SJakub Kicinski return 0; 560a3884572SJakub Kicinski 561*9fd7c555SJakub Kicinski err_unlock_free: 562*9fd7c555SJakub Kicinski up_write(&bpf_devs_lock); 563*9fd7c555SJakub Kicinski kfree(ondev); 564*9fd7c555SJakub Kicinski return err; 565a3884572SJakub Kicinski } 566*9fd7c555SJakub Kicinski EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_register); 567a3884572SJakub Kicinski 568*9fd7c555SJakub Kicinski void bpf_offload_dev_netdev_unregister(struct net_device *netdev) 569ab3f0063SJakub Kicinski { 570*9fd7c555SJakub Kicinski struct bpf_offloaded_map *offmap, *mtmp; 571*9fd7c555SJakub Kicinski struct bpf_prog_offload *offload, *ptmp; 572*9fd7c555SJakub Kicinski struct bpf_offload_netdev *ondev; 573ab3f0063SJakub Kicinski 574ab3f0063SJakub Kicinski ASSERT_RTNL(); 575ab3f0063SJakub Kicinski 576e0d3974aSJakub Kicinski down_write(&bpf_devs_lock); 577*9fd7c555SJakub Kicinski ondev = rhashtable_lookup_fast(&offdevs, &netdev, offdevs_params); 578*9fd7c555SJakub Kicinski if (WARN_ON(!ondev)) 579*9fd7c555SJakub Kicinski goto unlock; 580*9fd7c555SJakub Kicinski 581*9fd7c555SJakub Kicinski WARN_ON(rhashtable_remove_fast(&offdevs, &ondev->l, offdevs_params)); 582*9fd7c555SJakub Kicinski 583*9fd7c555SJakub Kicinski list_for_each_entry_safe(offload, ptmp, &ondev->progs, offloads) 584*9fd7c555SJakub Kicinski __bpf_prog_offload_destroy(offload->prog); 585*9fd7c555SJakub Kicinski list_for_each_entry_safe(offmap, mtmp, &ondev->maps, offloads) 586*9fd7c555SJakub Kicinski __bpf_map_offload_destroy(offmap); 587*9fd7c555SJakub Kicinski 588*9fd7c555SJakub Kicinski WARN_ON(!list_empty(&ondev->progs)); 589*9fd7c555SJakub Kicinski WARN_ON(!list_empty(&ondev->maps)); 590*9fd7c555SJakub Kicinski kfree(ondev); 591*9fd7c555SJakub Kicinski unlock: 592e0d3974aSJakub Kicinski up_write(&bpf_devs_lock); 593ab3f0063SJakub Kicinski } 594*9fd7c555SJakub Kicinski EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister); 595