18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* net/core/xdp.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/bpf.h> 78c2ecf20Sopenharmony_ci#include <linux/filter.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/idr.h> 138c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 148c2ecf20Sopenharmony_ci#include <linux/bug.h> 158c2ecf20Sopenharmony_ci#include <net/page_pool.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <net/xdp.h> 188c2ecf20Sopenharmony_ci#include <net/xdp_priv.h> /* struct xdp_mem_allocator */ 198c2ecf20Sopenharmony_ci#include <trace/events/xdp.h> 208c2ecf20Sopenharmony_ci#include <net/xdp_sock_drv.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define REG_STATE_NEW 0x0 238c2ecf20Sopenharmony_ci#define REG_STATE_REGISTERED 0x1 248c2ecf20Sopenharmony_ci#define REG_STATE_UNREGISTERED 0x2 258c2ecf20Sopenharmony_ci#define REG_STATE_UNUSED 0x3 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic DEFINE_IDA(mem_id_pool); 288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mem_id_lock); 298c2ecf20Sopenharmony_ci#define MEM_ID_MAX 0xFFFE 308c2ecf20Sopenharmony_ci#define MEM_ID_MIN 1 318c2ecf20Sopenharmony_cistatic int mem_id_next = MEM_ID_MIN; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic bool mem_id_init; /* false */ 348c2ecf20Sopenharmony_cistatic struct rhashtable *mem_id_ht; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic u32 xdp_mem_id_hashfn(const void *data, u32 len, u32 seed) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci const u32 *k = data; 398c2ecf20Sopenharmony_ci const u32 key = *k; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof_field(struct xdp_mem_allocator, mem.id) 428c2ecf20Sopenharmony_ci != sizeof(u32)); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Use cyclic increasing ID as direct hash key */ 458c2ecf20Sopenharmony_ci return key; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int xdp_mem_id_cmp(struct rhashtable_compare_arg *arg, 498c2ecf20Sopenharmony_ci const void *ptr) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci const struct xdp_mem_allocator *xa = ptr; 528c2ecf20Sopenharmony_ci u32 mem_id = *(u32 *)arg->key; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return xa->mem.id != mem_id; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct rhashtable_params mem_id_rht_params = { 588c2ecf20Sopenharmony_ci .nelem_hint = 64, 598c2ecf20Sopenharmony_ci .head_offset = offsetof(struct xdp_mem_allocator, node), 608c2ecf20Sopenharmony_ci .key_offset = offsetof(struct xdp_mem_allocator, mem.id), 618c2ecf20Sopenharmony_ci .key_len = sizeof_field(struct xdp_mem_allocator, mem.id), 628c2ecf20Sopenharmony_ci .max_size = MEM_ID_MAX, 638c2ecf20Sopenharmony_ci .min_size = 8, 648c2ecf20Sopenharmony_ci .automatic_shrinking = true, 658c2ecf20Sopenharmony_ci .hashfn = xdp_mem_id_hashfn, 668c2ecf20Sopenharmony_ci .obj_cmpfn = xdp_mem_id_cmp, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void __xdp_mem_allocator_rcu_free(struct rcu_head *rcu) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xa; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci xa = container_of(rcu, struct xdp_mem_allocator, rcu); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Allow this ID to be reused */ 768c2ecf20Sopenharmony_ci ida_simple_remove(&mem_id_pool, xa->mem.id); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci kfree(xa); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void mem_xa_remove(struct xdp_mem_allocator *xa) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci trace_mem_disconnect(xa); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!rhashtable_remove_fast(mem_id_ht, &xa->node, mem_id_rht_params)) 868c2ecf20Sopenharmony_ci call_rcu(&xa->rcu, __xdp_mem_allocator_rcu_free); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void mem_allocator_disconnect(void *allocator) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xa; 928c2ecf20Sopenharmony_ci struct rhashtable_iter iter; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mutex_lock(&mem_id_lock); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci rhashtable_walk_enter(mem_id_ht, &iter); 978c2ecf20Sopenharmony_ci do { 988c2ecf20Sopenharmony_ci rhashtable_walk_start(&iter); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci while ((xa = rhashtable_walk_next(&iter)) && !IS_ERR(xa)) { 1018c2ecf20Sopenharmony_ci if (xa->allocator == allocator) 1028c2ecf20Sopenharmony_ci mem_xa_remove(xa); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci rhashtable_walk_stop(&iter); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci } while (xa == ERR_PTR(-EAGAIN)); 1088c2ecf20Sopenharmony_ci rhashtable_walk_exit(&iter); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci mutex_unlock(&mem_id_lock); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid xdp_unreg_mem_model(struct xdp_mem_info *mem) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xa; 1168c2ecf20Sopenharmony_ci int type = mem->type; 1178c2ecf20Sopenharmony_ci int id = mem->id; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Reset mem info to defaults */ 1208c2ecf20Sopenharmony_ci mem->id = 0; 1218c2ecf20Sopenharmony_ci mem->type = 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (id == 0) 1248c2ecf20Sopenharmony_ci return; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (type == MEM_TYPE_PAGE_POOL) { 1278c2ecf20Sopenharmony_ci xa = rhashtable_lookup_fast(mem_id_ht, &id, mem_id_rht_params); 1288c2ecf20Sopenharmony_ci page_pool_destroy(xa->page_pool); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_unreg_mem_model); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_civoid xdp_rxq_info_unreg_mem_model(struct xdp_rxq_info *xdp_rxq) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci if (xdp_rxq->reg_state != REG_STATE_REGISTERED) { 1368c2ecf20Sopenharmony_ci WARN(1, "Missing register, driver bug"); 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci xdp_unreg_mem_model(&xdp_rxq->mem); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_unreg_mem_model); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_civoid xdp_rxq_info_unreg(struct xdp_rxq_info *xdp_rxq) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci /* Simplify driver cleanup code paths, allow unreg "unused" */ 1478c2ecf20Sopenharmony_ci if (xdp_rxq->reg_state == REG_STATE_UNUSED) 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci WARN(!(xdp_rxq->reg_state == REG_STATE_REGISTERED), "Driver BUG"); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci xdp_rxq_info_unreg_mem_model(xdp_rxq); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci xdp_rxq->reg_state = REG_STATE_UNREGISTERED; 1558c2ecf20Sopenharmony_ci xdp_rxq->dev = NULL; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_unreg); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void xdp_rxq_info_init(struct xdp_rxq_info *xdp_rxq) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci memset(xdp_rxq, 0, sizeof(*xdp_rxq)); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* Returns 0 on success, negative on failure */ 1658c2ecf20Sopenharmony_ciint xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, 1668c2ecf20Sopenharmony_ci struct net_device *dev, u32 queue_index) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci if (xdp_rxq->reg_state == REG_STATE_UNUSED) { 1698c2ecf20Sopenharmony_ci WARN(1, "Driver promised not to register this"); 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (xdp_rxq->reg_state == REG_STATE_REGISTERED) { 1748c2ecf20Sopenharmony_ci WARN(1, "Missing unregister, handled but fix driver"); 1758c2ecf20Sopenharmony_ci xdp_rxq_info_unreg(xdp_rxq); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!dev) { 1798c2ecf20Sopenharmony_ci WARN(1, "Missing net_device from driver"); 1808c2ecf20Sopenharmony_ci return -ENODEV; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* State either UNREGISTERED or NEW */ 1848c2ecf20Sopenharmony_ci xdp_rxq_info_init(xdp_rxq); 1858c2ecf20Sopenharmony_ci xdp_rxq->dev = dev; 1868c2ecf20Sopenharmony_ci xdp_rxq->queue_index = queue_index; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci xdp_rxq->reg_state = REG_STATE_REGISTERED; 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_reg); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid xdp_rxq_info_unused(struct xdp_rxq_info *xdp_rxq) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci xdp_rxq->reg_state = REG_STATE_UNUSED; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_unused); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cibool xdp_rxq_info_is_reg(struct xdp_rxq_info *xdp_rxq) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci return (xdp_rxq->reg_state == REG_STATE_REGISTERED); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_is_reg); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int __mem_id_init_hash_table(void) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct rhashtable *rht; 2088c2ecf20Sopenharmony_ci int ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (unlikely(mem_id_init)) 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci rht = kzalloc(sizeof(*rht), GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!rht) 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = rhashtable_init(rht, &mem_id_rht_params); 2188c2ecf20Sopenharmony_ci if (ret < 0) { 2198c2ecf20Sopenharmony_ci kfree(rht); 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci mem_id_ht = rht; 2238c2ecf20Sopenharmony_ci smp_mb(); /* mutex lock should provide enough pairing */ 2248c2ecf20Sopenharmony_ci mem_id_init = true; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* Allocate a cyclic ID that maps to allocator pointer. 2308c2ecf20Sopenharmony_ci * See: https://www.kernel.org/doc/html/latest/core-api/idr.html 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Caller must lock mem_id_lock. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic int __mem_id_cyclic_get(gfp_t gfp) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int retries = 1; 2378c2ecf20Sopenharmony_ci int id; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciagain: 2408c2ecf20Sopenharmony_ci id = ida_simple_get(&mem_id_pool, mem_id_next, MEM_ID_MAX, gfp); 2418c2ecf20Sopenharmony_ci if (id < 0) { 2428c2ecf20Sopenharmony_ci if (id == -ENOSPC) { 2438c2ecf20Sopenharmony_ci /* Cyclic allocator, reset next id */ 2448c2ecf20Sopenharmony_ci if (retries--) { 2458c2ecf20Sopenharmony_ci mem_id_next = MEM_ID_MIN; 2468c2ecf20Sopenharmony_ci goto again; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci return id; /* errno */ 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci mem_id_next = id + 1; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return id; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic bool __is_supported_mem_type(enum xdp_mem_type type) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (type == MEM_TYPE_PAGE_POOL) 2598c2ecf20Sopenharmony_ci return is_page_pool_compiled_in(); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (type >= MEM_TYPE_MAX) 2628c2ecf20Sopenharmony_ci return false; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return true; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic struct xdp_mem_allocator *__xdp_reg_mem_model(struct xdp_mem_info *mem, 2688c2ecf20Sopenharmony_ci enum xdp_mem_type type, 2698c2ecf20Sopenharmony_ci void *allocator) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xdp_alloc; 2728c2ecf20Sopenharmony_ci gfp_t gfp = GFP_KERNEL; 2738c2ecf20Sopenharmony_ci int id, errno, ret; 2748c2ecf20Sopenharmony_ci void *ptr; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!__is_supported_mem_type(type)) 2778c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci mem->type = type; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!allocator) { 2828c2ecf20Sopenharmony_ci if (type == MEM_TYPE_PAGE_POOL) 2838c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); /* Setup time check page_pool req */ 2848c2ecf20Sopenharmony_ci return NULL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Delay init of rhashtable to save memory if feature isn't used */ 2888c2ecf20Sopenharmony_ci if (!mem_id_init) { 2898c2ecf20Sopenharmony_ci mutex_lock(&mem_id_lock); 2908c2ecf20Sopenharmony_ci ret = __mem_id_init_hash_table(); 2918c2ecf20Sopenharmony_ci mutex_unlock(&mem_id_lock); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci xdp_alloc = kzalloc(sizeof(*xdp_alloc), gfp); 2978c2ecf20Sopenharmony_ci if (!xdp_alloc) 2988c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci mutex_lock(&mem_id_lock); 3018c2ecf20Sopenharmony_ci id = __mem_id_cyclic_get(gfp); 3028c2ecf20Sopenharmony_ci if (id < 0) { 3038c2ecf20Sopenharmony_ci errno = id; 3048c2ecf20Sopenharmony_ci goto err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci mem->id = id; 3078c2ecf20Sopenharmony_ci xdp_alloc->mem = *mem; 3088c2ecf20Sopenharmony_ci xdp_alloc->allocator = allocator; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Insert allocator into ID lookup table */ 3118c2ecf20Sopenharmony_ci ptr = rhashtable_insert_slow(mem_id_ht, &id, &xdp_alloc->node); 3128c2ecf20Sopenharmony_ci if (IS_ERR(ptr)) { 3138c2ecf20Sopenharmony_ci ida_simple_remove(&mem_id_pool, mem->id); 3148c2ecf20Sopenharmony_ci mem->id = 0; 3158c2ecf20Sopenharmony_ci errno = PTR_ERR(ptr); 3168c2ecf20Sopenharmony_ci goto err; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (type == MEM_TYPE_PAGE_POOL) 3208c2ecf20Sopenharmony_ci page_pool_use_xdp_mem(allocator, mem_allocator_disconnect); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_unlock(&mem_id_lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return xdp_alloc; 3258c2ecf20Sopenharmony_cierr: 3268c2ecf20Sopenharmony_ci mutex_unlock(&mem_id_lock); 3278c2ecf20Sopenharmony_ci kfree(xdp_alloc); 3288c2ecf20Sopenharmony_ci return ERR_PTR(errno); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciint xdp_reg_mem_model(struct xdp_mem_info *mem, 3328c2ecf20Sopenharmony_ci enum xdp_mem_type type, void *allocator) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xdp_alloc; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci xdp_alloc = __xdp_reg_mem_model(mem, type, allocator); 3378c2ecf20Sopenharmony_ci if (IS_ERR(xdp_alloc)) 3388c2ecf20Sopenharmony_ci return PTR_ERR(xdp_alloc); 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_reg_mem_model); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint xdp_rxq_info_reg_mem_model(struct xdp_rxq_info *xdp_rxq, 3448c2ecf20Sopenharmony_ci enum xdp_mem_type type, void *allocator) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xdp_alloc; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (xdp_rxq->reg_state != REG_STATE_REGISTERED) { 3498c2ecf20Sopenharmony_ci WARN(1, "Missing register, driver bug"); 3508c2ecf20Sopenharmony_ci return -EFAULT; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci xdp_alloc = __xdp_reg_mem_model(&xdp_rxq->mem, type, allocator); 3548c2ecf20Sopenharmony_ci if (IS_ERR(xdp_alloc)) 3558c2ecf20Sopenharmony_ci return PTR_ERR(xdp_alloc); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (trace_mem_connect_enabled() && xdp_alloc) 3588c2ecf20Sopenharmony_ci trace_mem_connect(xdp_alloc, xdp_rxq); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_rxq_info_reg_mem_model); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* XDP RX runs under NAPI protection, and in different delivery error 3658c2ecf20Sopenharmony_ci * scenarios (e.g. queue full), it is possible to return the xdp_frame 3668c2ecf20Sopenharmony_ci * while still leveraging this protection. The @napi_direct boolean 3678c2ecf20Sopenharmony_ci * is used for those calls sites. Thus, allowing for faster recycling 3688c2ecf20Sopenharmony_ci * of xdp_frames/pages in those cases. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_cistatic void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, 3718c2ecf20Sopenharmony_ci struct xdp_buff *xdp) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xa; 3748c2ecf20Sopenharmony_ci struct page *page; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci switch (mem->type) { 3778c2ecf20Sopenharmony_ci case MEM_TYPE_PAGE_POOL: 3788c2ecf20Sopenharmony_ci rcu_read_lock(); 3798c2ecf20Sopenharmony_ci /* mem->id is valid, checked in xdp_rxq_info_reg_mem_model() */ 3808c2ecf20Sopenharmony_ci xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params); 3818c2ecf20Sopenharmony_ci page = virt_to_head_page(data); 3828c2ecf20Sopenharmony_ci if (napi_direct && xdp_return_frame_no_direct()) 3838c2ecf20Sopenharmony_ci napi_direct = false; 3848c2ecf20Sopenharmony_ci page_pool_put_full_page(xa->page_pool, page, napi_direct); 3858c2ecf20Sopenharmony_ci rcu_read_unlock(); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci case MEM_TYPE_PAGE_SHARED: 3888c2ecf20Sopenharmony_ci page_frag_free(data); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case MEM_TYPE_PAGE_ORDER0: 3918c2ecf20Sopenharmony_ci page = virt_to_page(data); /* Assumes order0 page*/ 3928c2ecf20Sopenharmony_ci put_page(page); 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case MEM_TYPE_XSK_BUFF_POOL: 3958c2ecf20Sopenharmony_ci /* NB! Only valid from an xdp_buff! */ 3968c2ecf20Sopenharmony_ci xsk_buff_free(xdp); 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci default: 3998c2ecf20Sopenharmony_ci /* Not possible, checked in xdp_rxq_info_reg_mem_model() */ 4008c2ecf20Sopenharmony_ci WARN(1, "Incorrect XDP memory type (%d) usage", mem->type); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_civoid xdp_return_frame(struct xdp_frame *xdpf) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci __xdp_return(xdpf->data, &xdpf->mem, false, NULL); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_return_frame); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_civoid xdp_return_frame_rx_napi(struct xdp_frame *xdpf) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci __xdp_return(xdpf->data, &xdpf->mem, true, NULL); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_return_frame_rx_napi); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_civoid xdp_return_buff(struct xdp_buff *xdp) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci __xdp_return(xdp->data, &xdp->rxq->mem, true, xdp); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* Only called for MEM_TYPE_PAGE_POOL see xdp.h */ 4238c2ecf20Sopenharmony_civoid __xdp_release_frame(void *data, struct xdp_mem_info *mem) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct xdp_mem_allocator *xa; 4268c2ecf20Sopenharmony_ci struct page *page; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci rcu_read_lock(); 4298c2ecf20Sopenharmony_ci xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params); 4308c2ecf20Sopenharmony_ci page = virt_to_head_page(data); 4318c2ecf20Sopenharmony_ci if (xa) 4328c2ecf20Sopenharmony_ci page_pool_release_page(xa->page_pool, page); 4338c2ecf20Sopenharmony_ci rcu_read_unlock(); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__xdp_release_frame); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_civoid xdp_attachment_setup(struct xdp_attachment_info *info, 4388c2ecf20Sopenharmony_ci struct netdev_bpf *bpf) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci if (info->prog) 4418c2ecf20Sopenharmony_ci bpf_prog_put(info->prog); 4428c2ecf20Sopenharmony_ci info->prog = bpf->prog; 4438c2ecf20Sopenharmony_ci info->flags = bpf->flags; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_attachment_setup); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistruct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci unsigned int metasize, totsize; 4508c2ecf20Sopenharmony_ci void *addr, *data_to_copy; 4518c2ecf20Sopenharmony_ci struct xdp_frame *xdpf; 4528c2ecf20Sopenharmony_ci struct page *page; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Clone into a MEM_TYPE_PAGE_ORDER0 xdp_frame. */ 4558c2ecf20Sopenharmony_ci metasize = xdp_data_meta_unsupported(xdp) ? 0 : 4568c2ecf20Sopenharmony_ci xdp->data - xdp->data_meta; 4578c2ecf20Sopenharmony_ci totsize = xdp->data_end - xdp->data + metasize; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (sizeof(*xdpf) + totsize > PAGE_SIZE) 4608c2ecf20Sopenharmony_ci return NULL; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci page = dev_alloc_page(); 4638c2ecf20Sopenharmony_ci if (!page) 4648c2ecf20Sopenharmony_ci return NULL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci addr = page_to_virt(page); 4678c2ecf20Sopenharmony_ci xdpf = addr; 4688c2ecf20Sopenharmony_ci memset(xdpf, 0, sizeof(*xdpf)); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci addr += sizeof(*xdpf); 4718c2ecf20Sopenharmony_ci data_to_copy = metasize ? xdp->data_meta : xdp->data; 4728c2ecf20Sopenharmony_ci memcpy(addr, data_to_copy, totsize); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci xdpf->data = addr + metasize; 4758c2ecf20Sopenharmony_ci xdpf->len = totsize - metasize; 4768c2ecf20Sopenharmony_ci xdpf->headroom = 0; 4778c2ecf20Sopenharmony_ci xdpf->metasize = metasize; 4788c2ecf20Sopenharmony_ci xdpf->frame_sz = PAGE_SIZE; 4798c2ecf20Sopenharmony_ci xdpf->mem.type = MEM_TYPE_PAGE_ORDER0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci xsk_buff_free(xdp); 4828c2ecf20Sopenharmony_ci return xdpf; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_convert_zc_to_xdp_frame); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* Used by XDP_WARN macro, to avoid inlining WARN() in fast-path */ 4878c2ecf20Sopenharmony_civoid xdp_warn(const char *msg, const char *func, const int line) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci WARN(1, "XDP_WARN: %s(line:%d): %s\n", func, line, msg); 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdp_warn); 492