History

Merge branch 'io_uring-zero-copy-rx'

David Wei says: ==================== io_uring zero copy rx This patchset contains net/ patches needed by a new io_uring request implementing zero copy rx into userspace pages, eliminating a kernel to user copy. We configure a page pool that a driver uses to fill a hw rx queue to hand out user pages instead of kernel pages. Any data that ends up hitting this hw rx queue will thus be dma'd into userspace memory directly, without needing to be bounced through kernel memory. 'Reading' data out of a socket instead becomes a _notification_ mechanism, where the kernel tells userspace where the data is. The overall approach is similar to the devmem TCP proposal. This relies on hw header/data split, flow steering and RSS to ensure packet headers remain in kernel memory and only desired flows hit a hw rx queue configured for zero copy. Configuring this is outside of the scope of this patchset. We share netdev core infra with devmem TCP. The main difference is that io_uring is used for the uAPI and the lifetime of all objects are bound to an io_uring instance. Data is 'read' using a new io_uring request type. When done, data is returned via a new shared refill queue. A zero copy page pool refills a hw rx queue from this refill queue directly. Of course, the lifetime of these data buffers are managed by io_uring rather than the networking stack, with different refcounting rules. This patchset is the first step adding basic zero copy support. We will extend this iteratively with new features e.g. dynamically allocated zero copy areas, THP support, dmabuf support, improved copy fallback, general optimisations and more. In terms of netdev support, we're first targeting Broadcom bnxt. Patches aren't included since Taehee Yoo has already sent a more comprehensive patchset adding support in [1]. Google gve should already support this, and Mellanox mlx5 support is WIP pending driver changes. =========== Performance =========== Note: Comparison with epoll + TCP_ZEROCOPY_RECEIVE isn't done yet. Test setup: * AMD EPYC 9454 * Broadcom BCM957508 200G * Kernel v6.11 base [2] * liburing fork [3] * kperf fork [4] * 4K MTU * Single TCP flow With application thread + net rx softirq pinned to _different_ cores: +-------------------------------+ | epoll | io_uring | |-----------|-------------------| | 82.2 Gbps | 116.2 Gbps (+41%) | +-------------------------------+ Pinned to _same_ core: +-------------------------------+ | epoll | io_uring | |-----------|-------------------| | 62.6 Gbps | 80.9 Gbps (+29%) | +-------------------------------+ ===== Links ===== Broadcom bnxt support: [1]: https://lore.kernel.org/20241003160620.1521626-8-ap420073@gmail.com Linux kernel branch including io_uring bits: [2]: https://github.com/isilence/linux.git zcrx/v13 liburing for testing: [3]: https://github.com/isilence/liburing.git zcrx/next kperf for testing: [4]: https://git.kernel.dk/kperf.git ====================

Jakub Kicinski committed 1 year ago

Commit 71f0dd52 parents ba6ec09 6e18ed914 files · +321 −81Browse files

Unified · Split

14 files
  1. Documentation/netlink/specs/netdev.yaml +15 −0
  2. include/net/netmem.h +20 −1
  3. include/net/page_pool/memory_provider.h +45 −0
  4. include/net/page_pool/types.h +4 −0
  5. include/uapi/linux/netdev.h +7 −0
  6. net/core/dev.c +15 −1
  7. net/core/devmem.c +59 −34
  8. net/core/devmem.h +22 −27
  9. net/core/netdev-genl.c +6 −5
  10. net/core/netdev_rx_queue.c +69 −0
  11. net/core/page_pool.c +43 −8
  12. net/core/page_pool_user.c +3 −4
  13. net/ipv4/tcp.c +6 −1
  14. tools/include/uapi/linux/netdev.h +7 −0
Documentation/netlink/specs/netdev.yaml+15−0
@@ -114,6 +114,9 @@ attribute-sets: · 113 unmodified lines
114 doc: Bitmask of enabled AF_XDP features.114 doc: Bitmask of enabled AF_XDP features.
115 type: u64115 type: u64
116 enum: xsk-flags116 enum: xsk-flags
117 -
118 name: io-uring-provider-info
119 attributes: []
117 -120 -
118 name: page-pool121 name: page-pool
119 attributes:122 attributes:
@@ -171,6 +174,11 @@ attribute-sets: · 51 unmodified lines
171 name: dmabuf174 name: dmabuf
172 doc: ID of the dmabuf this page-pool is attached to.175 doc: ID of the dmabuf this page-pool is attached to.
173 type: u32176 type: u32
177 -
178 name: io-uring
179 doc: io-uring memory provider information.
180 type: nest
181 nested-attributes: io-uring-provider-info
174 -182 -
175 name: page-pool-info183 name: page-pool-info
176 subset-of: page-pool184 subset-of: page-pool
@@ -296,6 +304,11 @@ attribute-sets: · 119 unmodified lines
296 name: dmabuf304 name: dmabuf
297 doc: ID of the dmabuf attached to this queue, if any.305 doc: ID of the dmabuf attached to this queue, if any.
298 type: u32306 type: u32
307 -
308 name: io-uring
309 doc: io_uring memory provider information.
310 type: nest
311 nested-attributes: io-uring-provider-info
299 312
300 -313 -
301 name: qstats314 name: qstats
@@ -572,6 +585,7 @@ operations: · 270 unmodified lines
572 - inflight-mem585 - inflight-mem
573 - detach-time586 - detach-time
574 - dmabuf587 - dmabuf
588 - io-uring
575 dump:589 dump:
576 reply: *pp-reply590 reply: *pp-reply
577 config-cond: page-pool591 config-cond: page-pool
@@ -637,6 +651,7 @@ operations: · 59 unmodified lines
637 - napi-id651 - napi-id
638 - ifindex652 - ifindex
639 - dmabuf653 - dmabuf
654 - io-uring
640 dump:655 dump:
641 request:656 request:
642 attributes:657 attributes:
include/net/netmem.h+20−1
@@ -24,11 +24,20 @@ struct net_iov { · 23 unmodified lines
24 unsigned long __unused_padding;24 unsigned long __unused_padding;
25 unsigned long pp_magic;25 unsigned long pp_magic;
26 struct page_pool *pp;26 struct page_pool *pp;
27 struct dmabuf_genpool_chunk_owner *owner;27 struct net_iov_area *owner;
28 unsigned long dma_addr;28 unsigned long dma_addr;
29 atomic_long_t pp_ref_count;29 atomic_long_t pp_ref_count;
30};30};
31 31
32struct net_iov_area {
33 /* Array of net_iovs for this area. */
34 struct net_iov *niovs;
35 size_t num_niovs;
36
37 /* Offset into the dma-buf where this chunk starts. */
38 unsigned long base_virtual;
39};
40
32/* These fields in struct page are used by the page_pool and net stack:41/* These fields in struct page are used by the page_pool and net stack:
33 *42 *
34 * struct {43 * struct {
@@ -54,6 +63,16 @@ NET_IOV_ASSERT_OFFSET(dma_addr, dma_addr); · 19 unmodified lines
54NET_IOV_ASSERT_OFFSET(pp_ref_count, pp_ref_count);63NET_IOV_ASSERT_OFFSET(pp_ref_count, pp_ref_count);
55#undef NET_IOV_ASSERT_OFFSET64#undef NET_IOV_ASSERT_OFFSET
56 65
66static inline struct net_iov_area *net_iov_owner(const struct net_iov *niov)
67{
68 return niov->owner;
69}
70
71static inline unsigned int net_iov_idx(const struct net_iov *niov)
72{
73 return niov - net_iov_owner(niov)->niovs;
74}
75
57/* netmem */76/* netmem */
58 77
59/**78/**
include/net/page_pool/memory_provider.h+45−0
@@ -0,0 +1,45 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _NET_PAGE_POOL_MEMORY_PROVIDER_H
3#define _NET_PAGE_POOL_MEMORY_PROVIDER_H
4
5#include <net/netmem.h>
6#include <net/page_pool/types.h>
7
8struct netdev_rx_queue;
9struct sk_buff;
10
11struct memory_provider_ops {
12 netmem_ref (*alloc_netmems)(struct page_pool *pool, gfp_t gfp);
13 bool (*release_netmem)(struct page_pool *pool, netmem_ref netmem);
14 int (*init)(struct page_pool *pool);
15 void (*destroy)(struct page_pool *pool);
16 int (*nl_fill)(void *mp_priv, struct sk_buff *rsp,
17 struct netdev_rx_queue *rxq);
18 void (*uninstall)(void *mp_priv, struct netdev_rx_queue *rxq);
19};
20
21bool net_mp_niov_set_dma_addr(struct net_iov *niov, dma_addr_t addr);
22void net_mp_niov_set_page_pool(struct page_pool *pool, struct net_iov *niov);
23void net_mp_niov_clear_page_pool(struct net_iov *niov);
24
25int net_mp_open_rxq(struct net_device *dev, unsigned ifq_idx,
26 struct pp_memory_provider_params *p);
27void net_mp_close_rxq(struct net_device *dev, unsigned ifq_idx,
28 struct pp_memory_provider_params *old_p);
29
30/**
31 * net_mp_netmem_place_in_cache() - give a netmem to a page pool
32 * @pool: the page pool to place the netmem into
33 * @netmem: netmem to give
34 *
35 * Push an accounted netmem into the page pool's allocation cache. The caller
36 * must ensure that there is space in the cache. It should only be called off
37 * the mp_ops->alloc_netmems() path.
38 */
39static inline void net_mp_netmem_place_in_cache(struct page_pool *pool,
40 netmem_ref netmem)
41{
42 pool->alloc.cache[pool->alloc.count++] = netmem;
43}
44
45#endif
include/net/page_pool/types.h+4−0
@@ -152,8 +152,11 @@ struct page_pool_stats { · 151 unmodified lines
152 */152 */
153#define PAGE_POOL_FRAG_GROUP_ALIGN (4 * sizeof(long))153#define PAGE_POOL_FRAG_GROUP_ALIGN (4 * sizeof(long))
154 154
155struct memory_provider_ops;
156
155struct pp_memory_provider_params {157struct pp_memory_provider_params {
156 void *mp_priv;158 void *mp_priv;
159 const struct memory_provider_ops *mp_ops;
157};160};
158 161
159struct page_pool {162struct page_pool {
@@ -216,6 +219,7 @@ struct page_pool { · 56 unmodified lines
216 struct ptr_ring ring;219 struct ptr_ring ring;
217 220
218 void *mp_priv;221 void *mp_priv;
222 const struct memory_provider_ops *mp_ops;
219 223
220#ifdef CONFIG_PAGE_POOL_STATS224#ifdef CONFIG_PAGE_POOL_STATS
221 /* recycle stats are per-cpu to avoid locking */225 /* recycle stats are per-cpu to avoid locking */
include/uapi/linux/netdev.h+7−0
@@ -86,6 +86,11 @@ enum { · 85 unmodified lines
86 NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)86 NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
87};87};
88 88
89enum {
90 __NETDEV_A_IO_URING_PROVIDER_INFO_MAX,
91 NETDEV_A_IO_URING_PROVIDER_INFO_MAX = (__NETDEV_A_IO_URING_PROVIDER_INFO_MAX - 1)
92};
93
89enum {94enum {
90 NETDEV_A_PAGE_POOL_ID = 1,95 NETDEV_A_PAGE_POOL_ID = 1,
91 NETDEV_A_PAGE_POOL_IFINDEX,96 NETDEV_A_PAGE_POOL_IFINDEX,
@@ -94,6 +99,7 @@ enum { · 2 unmodified lines
94 NETDEV_A_PAGE_POOL_INFLIGHT_MEM,99 NETDEV_A_PAGE_POOL_INFLIGHT_MEM,
95 NETDEV_A_PAGE_POOL_DETACH_TIME,100 NETDEV_A_PAGE_POOL_DETACH_TIME,
96 NETDEV_A_PAGE_POOL_DMABUF,101 NETDEV_A_PAGE_POOL_DMABUF,
102 NETDEV_A_PAGE_POOL_IO_URING,
97 103
98 __NETDEV_A_PAGE_POOL_MAX,104 __NETDEV_A_PAGE_POOL_MAX,
99 NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX - 1)105 NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX - 1)
@@ -136,6 +142,7 @@ enum { · 36 unmodified lines
136 NETDEV_A_QUEUE_TYPE,142 NETDEV_A_QUEUE_TYPE,
137 NETDEV_A_QUEUE_NAPI_ID,143 NETDEV_A_QUEUE_NAPI_ID,
138 NETDEV_A_QUEUE_DMABUF,144 NETDEV_A_QUEUE_DMABUF,
145 NETDEV_A_QUEUE_IO_URING,
139 146
140 __NETDEV_A_QUEUE_MAX,147 __NETDEV_A_QUEUE_MAX,
141 NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)148 NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)
net/core/dev.c+15−1
@@ -159,6 +159,7 @@ · 158 unmodified lines
159#include <net/netdev_rx_queue.h>159#include <net/netdev_rx_queue.h>
160#include <net/page_pool/types.h>160#include <net/page_pool/types.h>
161#include <net/page_pool/helpers.h>161#include <net/page_pool/helpers.h>
162#include <net/page_pool/memory_provider.h>
162#include <net/rps.h>163#include <net/rps.h>
163#include <linux/phy_link_topology.h>164#include <linux/phy_link_topology.h>
164 165
@@ -11745,6 +11746,19 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head) · 11580 unmodified lines
11745}11746}
11746EXPORT_SYMBOL(unregister_netdevice_queue);11747EXPORT_SYMBOL(unregister_netdevice_queue);
11747 11748
11749static void dev_memory_provider_uninstall(struct net_device *dev)
11750{
11751 unsigned int i;
11752
11753 for (i = 0; i < dev->real_num_rx_queues; i++) {
11754 struct netdev_rx_queue *rxq = &dev->_rx[i];
11755 struct pp_memory_provider_params *p = &rxq->mp_params;
11756
11757 if (p->mp_ops && p->mp_ops->uninstall)
11758 p->mp_ops->uninstall(rxq->mp_params.mp_priv, rxq);
11759 }
11760}
11761
11748void unregister_netdevice_many_notify(struct list_head *head,11762void unregister_netdevice_many_notify(struct list_head *head,
11749 u32 portid, const struct nlmsghdr *nlh)11763 u32 portid, const struct nlmsghdr *nlh)
11750{11764{
@@ -11799,7 +11813,7 @@ void unregister_netdevice_many_notify(struct list_head *head, · 48 unmodified lines
11799 dev_tcx_uninstall(dev);11813 dev_tcx_uninstall(dev);
11800 dev_xdp_uninstall(dev);11814 dev_xdp_uninstall(dev);
11801 bpf_dev_bound_netdev_unregister(dev);11815 bpf_dev_bound_netdev_unregister(dev);
11802 dev_dmabuf_uninstall(dev);11816 dev_memory_provider_uninstall(dev);
11803 11817
11804 netdev_offload_xstats_disable_all(dev);11818 netdev_offload_xstats_disable_all(dev);
11805 11819
net/core/devmem.c+59−34
@@ -16,6 +16,7 @@ · 15 unmodified lines
16#include <net/netdev_queues.h>16#include <net/netdev_queues.h>
17#include <net/netdev_rx_queue.h>17#include <net/netdev_rx_queue.h>
18#include <net/page_pool/helpers.h>18#include <net/page_pool/helpers.h>
19#include <net/page_pool/memory_provider.h>
19#include <trace/events/page_pool.h>20#include <trace/events/page_pool.h>
20 21
21#include "devmem.h"22#include "devmem.h"
@@ -27,20 +28,28 @@ · 5 unmodified lines
27/* Protected by rtnl_lock() */28/* Protected by rtnl_lock() */
28static DEFINE_XARRAY_FLAGS(net_devmem_dmabuf_bindings, XA_FLAGS_ALLOC1);29static DEFINE_XARRAY_FLAGS(net_devmem_dmabuf_bindings, XA_FLAGS_ALLOC1);
29 30
31static const struct memory_provider_ops dmabuf_devmem_ops;
32
33bool net_is_devmem_iov(struct net_iov *niov)
34{
35 return niov->pp->mp_ops == &dmabuf_devmem_ops;
36}
37
30static void net_devmem_dmabuf_free_chunk_owner(struct gen_pool *genpool,38static void net_devmem_dmabuf_free_chunk_owner(struct gen_pool *genpool,
31 struct gen_pool_chunk *chunk,39 struct gen_pool_chunk *chunk,
32 void *not_used)40 void *not_used)
33{41{
34 struct dmabuf_genpool_chunk_owner *owner = chunk->owner;42 struct dmabuf_genpool_chunk_owner *owner = chunk->owner;
35 43
36 kvfree(owner->niovs);44 kvfree(owner->area.niovs);
37 kfree(owner);45 kfree(owner);
38}46}
39 47
40static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov)48static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov)
41{49{
42 struct dmabuf_genpool_chunk_owner *owner = net_iov_owner(niov);50 struct dmabuf_genpool_chunk_owner *owner;
43 51
52 owner = net_devmem_iov_to_chunk_owner(niov);
44 return owner->base_dma_addr +53 return owner->base_dma_addr +
45 ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT);54 ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT);
46}55}
@@ -83,7 +92,7 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) · 36 unmodified lines
83 92
84 offset = dma_addr - owner->base_dma_addr;93 offset = dma_addr - owner->base_dma_addr;
85 index = offset / PAGE_SIZE;94 index = offset / PAGE_SIZE;
86 niov = &owner->niovs[index];95 niov = &owner->area.niovs[index];
87 96
88 niov->pp_magic = 0;97 niov->pp_magic = 0;
89 niov->pp = NULL;98 niov->pp = NULL;
@@ -94,7 +103,7 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) · 4 unmodified lines
94 103
95void net_devmem_free_dmabuf(struct net_iov *niov)104void net_devmem_free_dmabuf(struct net_iov *niov)
96{105{
97 struct net_devmem_dmabuf_binding *binding = net_iov_binding(niov);106 struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);
98 unsigned long dma_addr = net_devmem_get_dma_addr(niov);107 unsigned long dma_addr = net_devmem_get_dma_addr(niov);
99 108
100 if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,109 if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,
@@ -117,6 +126,7 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding) · 16 unmodified lines
117 WARN_ON(rxq->mp_params.mp_priv != binding);126 WARN_ON(rxq->mp_params.mp_priv != binding);
118 127
119 rxq->mp_params.mp_priv = NULL;128 rxq->mp_params.mp_priv = NULL;
129 rxq->mp_params.mp_ops = NULL;
120 130
121 rxq_idx = get_netdev_rx_queue_index(rxq);131 rxq_idx = get_netdev_rx_queue_index(rxq);
122 132
@@ -152,7 +162,7 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, · 29 unmodified lines
152 }162 }
153 163
154 rxq = __netif_get_rx_queue(dev, rxq_idx);164 rxq = __netif_get_rx_queue(dev, rxq_idx);
155 if (rxq->mp_params.mp_priv) {165 if (rxq->mp_params.mp_ops) {
156 NL_SET_ERR_MSG(extack, "designated queue already memory provider bound");166 NL_SET_ERR_MSG(extack, "designated queue already memory provider bound");
157 return -EEXIST;167 return -EEXIST;
158 }168 }
@@ -170,6 +180,7 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, · 11 unmodified lines
170 return err;180 return err;
171 181
172 rxq->mp_params.mp_priv = binding;182 rxq->mp_params.mp_priv = binding;
183 rxq->mp_params.mp_ops = &dmabuf_devmem_ops;
173 184
174 err = netdev_rx_queue_restart(dev, rxq_idx);185 err = netdev_rx_queue_restart(dev, rxq_idx);
175 if (err)186 if (err)
@@ -179,6 +190,7 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, · 3 unmodified lines
179 190
180err_xa_erase:191err_xa_erase:
181 rxq->mp_params.mp_priv = NULL;192 rxq->mp_params.mp_priv = NULL;
193 rxq->mp_params.mp_ops = NULL;
182 xa_erase(&binding->bound_rxqs, xa_idx);194 xa_erase(&binding->bound_rxqs, xa_idx);
183 195
184 return err;196 return err;
@@ -261,9 +273,9 @@ net_devmem_bind_dmabuf(struct net_device *dev, unsigned int dmabuf_fd, · 76 unmodified lines
261 goto err_free_chunks;273 goto err_free_chunks;
262 }274 }
263 275
264 owner->base_virtual = virtual;276 owner->area.base_virtual = virtual;
265 owner->base_dma_addr = dma_addr;277 owner->base_dma_addr = dma_addr;
266 owner->num_niovs = len / PAGE_SIZE;278 owner->area.num_niovs = len / PAGE_SIZE;
267 owner->binding = binding;279 owner->binding = binding;
268 280
269 err = gen_pool_add_owner(binding->chunk_pool, dma_addr,281 err = gen_pool_add_owner(binding->chunk_pool, dma_addr,
@@ -275,17 +287,17 @@ net_devmem_bind_dmabuf(struct net_device *dev, unsigned int dmabuf_fd, · 5 unmodified lines
275 goto err_free_chunks;287 goto err_free_chunks;
276 }288 }
277 289
278 owner->niovs = kvmalloc_array(owner->num_niovs,290 owner->area.niovs = kvmalloc_array(owner->area.num_niovs,
279 sizeof(*owner->niovs),291 sizeof(*owner->area.niovs),
280 GFP_KERNEL);292 GFP_KERNEL);
281 if (!owner->niovs) {293 if (!owner->area.niovs) {
282 err = -ENOMEM;294 err = -ENOMEM;
283 goto err_free_chunks;295 goto err_free_chunks;
284 }296 }
285 297
286 for (i = 0; i < owner->num_niovs; i++) {298 for (i = 0; i < owner->area.num_niovs; i++) {
287 niov = &owner->niovs[i];299 niov = &owner->area.niovs[i];
288 niov->owner = owner;300 niov->owner = &owner->area;
289 page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov),301 page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov),
290 net_devmem_get_dma_addr(niov));302 net_devmem_get_dma_addr(niov));
291 }303 }
@@ -313,26 +325,6 @@ err_put_dmabuf: · 21 unmodified lines
313 return ERR_PTR(err);325 return ERR_PTR(err);
314}326}
315 327
316void dev_dmabuf_uninstall(struct net_device *dev)
317{
318 struct net_devmem_dmabuf_binding *binding;
319 struct netdev_rx_queue *rxq;
320 unsigned long xa_idx;
321 unsigned int i;
322
323 for (i = 0; i < dev->real_num_rx_queues; i++) {
324 binding = dev->_rx[i].mp_params.mp_priv;
325 if (!binding)
326 continue;
327
328 xa_for_each(&binding->bound_rxqs, xa_idx, rxq)
329 if (rxq == &dev->_rx[i]) {
330 xa_erase(&binding->bound_rxqs, xa_idx);
331 break;
332 }
333 }
334}
335
336/*** "Dmabuf devmem memory provider" ***/328/*** "Dmabuf devmem memory provider" ***/
337 329
338int mp_dmabuf_devmem_init(struct page_pool *pool)330int mp_dmabuf_devmem_init(struct page_pool *pool)
@@ -398,3 +390,36 @@ bool mp_dmabuf_devmem_release_page(struct page_pool *pool, netmem_ref netmem) · 59 unmodified lines
398 /* We don't want the page pool put_page()ing our net_iovs. */390 /* We don't want the page pool put_page()ing our net_iovs. */
399 return false;391 return false;
400}392}
393
394static int mp_dmabuf_devmem_nl_fill(void *mp_priv, struct sk_buff *rsp,
395 struct netdev_rx_queue *rxq)
396{
397 const struct net_devmem_dmabuf_binding *binding = mp_priv;
398 int type = rxq ? NETDEV_A_QUEUE_DMABUF : NETDEV_A_PAGE_POOL_DMABUF;
399
400 return nla_put_u32(rsp, type, binding->id);
401}
402
403static void mp_dmabuf_devmem_uninstall(void *mp_priv,
404 struct netdev_rx_queue *rxq)
405{
406 struct net_devmem_dmabuf_binding *binding = mp_priv;
407 struct netdev_rx_queue *bound_rxq;
408 unsigned long xa_idx;
409
410 xa_for_each(&binding->bound_rxqs, xa_idx, bound_rxq) {
411 if (bound_rxq == rxq) {
412 xa_erase(&binding->bound_rxqs, xa_idx);
413 break;
414 }
415 }
416}
417
418static const struct memory_provider_ops dmabuf_devmem_ops = {
419 .init = mp_dmabuf_devmem_init,
420 .destroy = mp_dmabuf_devmem_destroy,
421 .alloc_netmems = mp_dmabuf_devmem_alloc_netmems,
422 .release_netmem = mp_dmabuf_devmem_release_page,
423 .nl_fill = mp_dmabuf_devmem_nl_fill,
424 .uninstall = mp_dmabuf_devmem_uninstall,
425};
net/core/devmem.h+22−27
@@ -10,6 +10,8 @@ · 9 unmodified lines
10#ifndef _NET_DEVMEM_H10#ifndef _NET_DEVMEM_H
11#define _NET_DEVMEM_H11#define _NET_DEVMEM_H
12 12
13#include <net/netmem.h>
14
13struct netlink_ext_ack;15struct netlink_ext_ack;
14 16
15struct net_devmem_dmabuf_binding {17struct net_devmem_dmabuf_binding {
@@ -51,17 +53,11 @@ struct net_devmem_dmabuf_binding { · 35 unmodified lines
51 * allocations from this chunk.53 * allocations from this chunk.
52 */54 */
53struct dmabuf_genpool_chunk_owner {55struct dmabuf_genpool_chunk_owner {
54 /* Offset into the dma-buf where this chunk starts. */56 struct net_iov_area area;
55 unsigned long base_virtual;57 struct net_devmem_dmabuf_binding *binding;
56 58
57 /* dma_addr of the start of the chunk. */59 /* dma_addr of the start of the chunk. */
58 dma_addr_t base_dma_addr;60 dma_addr_t base_dma_addr;
59
60 /* Array of net_iovs for this chunk. */
61 struct net_iov *niovs;
62 size_t num_niovs;
63
64 struct net_devmem_dmabuf_binding *binding;
65};61};
66 62
67void __net_devmem_dmabuf_binding_free(struct net_devmem_dmabuf_binding *binding);63void __net_devmem_dmabuf_binding_free(struct net_devmem_dmabuf_binding *binding);
@@ -72,38 +68,34 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding); · 4 unmodified lines
72int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,68int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
73 struct net_devmem_dmabuf_binding *binding,69 struct net_devmem_dmabuf_binding *binding,
74 struct netlink_ext_ack *extack);70 struct netlink_ext_ack *extack);
75void dev_dmabuf_uninstall(struct net_device *dev);
76 71
77static inline struct dmabuf_genpool_chunk_owner *72static inline struct dmabuf_genpool_chunk_owner *
78net_iov_owner(const struct net_iov *niov)73net_devmem_iov_to_chunk_owner(const struct net_iov *niov)
79{74{
80 return niov->owner;75 struct net_iov_area *owner = net_iov_owner(niov);
76
77 return container_of(owner, struct dmabuf_genpool_chunk_owner, area);
81}78}
82 79
83static inline unsigned int net_iov_idx(const struct net_iov *niov)80static inline struct net_devmem_dmabuf_binding *
81net_devmem_iov_binding(const struct net_iov *niov)
84{82{
85 return niov - net_iov_owner(niov)->niovs;83 return net_devmem_iov_to_chunk_owner(niov)->binding;
86}84}
87 85
88static inline struct net_devmem_dmabuf_binding *86static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov)
89net_iov_binding(const struct net_iov *niov)
90{87{
91 return net_iov_owner(niov)->binding;88 return net_devmem_iov_binding(niov)->id;
92}89}
93 90
94static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)91static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)
95{92{
96 struct dmabuf_genpool_chunk_owner *owner = net_iov_owner(niov);93 struct net_iov_area *owner = net_iov_owner(niov);
97 94
98 return owner->base_virtual +95 return owner->base_virtual +
99 ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT);96 ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT);
100}97}
101 98
102static inline u32 net_iov_binding_id(const struct net_iov *niov)
103{
104 return net_iov_owner(niov)->binding->id;
105}
106
107static inline void99static inline void
108net_devmem_dmabuf_binding_get(struct net_devmem_dmabuf_binding *binding)100net_devmem_dmabuf_binding_get(struct net_devmem_dmabuf_binding *binding)
109{101{
@@ -123,6 +115,8 @@ struct net_iov * · 13 unmodified lines
123net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding);115net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding);
124void net_devmem_free_dmabuf(struct net_iov *ppiov);116void net_devmem_free_dmabuf(struct net_iov *ppiov);
125 117
118bool net_is_devmem_iov(struct net_iov *niov);
119
126#else120#else
127struct net_devmem_dmabuf_binding;121struct net_devmem_dmabuf_binding;
128 122
@@ -152,10 +146,6 @@ net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, · 23 unmodified lines
152 return -EOPNOTSUPP;146 return -EOPNOTSUPP;
153}147}
154 148
155static inline void dev_dmabuf_uninstall(struct net_device *dev)
156{
157}
158
159static inline struct net_iov *149static inline struct net_iov *
160net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)150net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)
161{151{
@@ -171,10 +161,15 @@ static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov) · 9 unmodified lines
171 return 0;161 return 0;
172}162}
173 163
174static inline u32 net_iov_binding_id(const struct net_iov *niov)164static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov)
175{165{
176 return 0;166 return 0;
177}167}
168
169static inline bool net_is_devmem_iov(struct net_iov *niov)
170{
171 return false;
172}
178#endif173#endif
179 174
180#endif /* _NET_DEVMEM_H */175#endif /* _NET_DEVMEM_H */
net/core/netdev-genl.c+6−5
@@ -10,6 +10,7 @@ · 9 unmodified lines
10#include <net/sock.h>10#include <net/sock.h>
11#include <net/xdp.h>11#include <net/xdp.h>
12#include <net/xdp_sock.h>12#include <net/xdp_sock.h>
13#include <net/page_pool/memory_provider.h>
13 14
14#include "dev.h"15#include "dev.h"
15#include "devmem.h"16#include "devmem.h"
@@ -368,7 +369,7 @@ static int · 352 unmodified lines
368netdev_nl_queue_fill_one(struct sk_buff *rsp, struct net_device *netdev,369netdev_nl_queue_fill_one(struct sk_buff *rsp, struct net_device *netdev,
369 u32 q_idx, u32 q_type, const struct genl_info *info)370 u32 q_idx, u32 q_type, const struct genl_info *info)
370{371{
371 struct net_devmem_dmabuf_binding *binding;372 struct pp_memory_provider_params *params;
372 struct netdev_rx_queue *rxq;373 struct netdev_rx_queue *rxq;
373 struct netdev_queue *txq;374 struct netdev_queue *txq;
374 void *hdr;375 void *hdr;
@@ -385,15 +386,15 @@ netdev_nl_queue_fill_one(struct sk_buff *rsp, struct net_device *netdev, · 10 unmodified lines
385 switch (q_type) {386 switch (q_type) {
386 case NETDEV_QUEUE_TYPE_RX:387 case NETDEV_QUEUE_TYPE_RX:
387 rxq = __netif_get_rx_queue(netdev, q_idx);388 rxq = __netif_get_rx_queue(netdev, q_idx);
389
388 if (rxq->napi && nla_put_u32(rsp, NETDEV_A_QUEUE_NAPI_ID,390 if (rxq->napi && nla_put_u32(rsp, NETDEV_A_QUEUE_NAPI_ID,
389 rxq->napi->napi_id))391 rxq->napi->napi_id))
390 goto nla_put_failure;392 goto nla_put_failure;
391 393
392 binding = rxq->mp_params.mp_priv;394 params = &rxq->mp_params;
393 if (binding &&395 if (params->mp_ops &&
394 nla_put_u32(rsp, NETDEV_A_QUEUE_DMABUF, binding->id))396 params->mp_ops->nl_fill(params->mp_priv, rsp, rxq))
395 goto nla_put_failure;397 goto nla_put_failure;
396
397 break;398 break;
398 case NETDEV_QUEUE_TYPE_TX:399 case NETDEV_QUEUE_TYPE_TX:
399 txq = netdev_get_tx_queue(netdev, q_idx);400 txq = netdev_get_tx_queue(netdev, q_idx);
net/core/netdev_rx_queue.c+69−0
@@ -3,6 +3,7 @@ · 2 unmodified lines
3#include <linux/netdevice.h>3#include <linux/netdevice.h>
4#include <net/netdev_queues.h>4#include <net/netdev_queues.h>
5#include <net/netdev_rx_queue.h>5#include <net/netdev_rx_queue.h>
6#include <net/page_pool/memory_provider.h>
6 7
7#include "page_pool_priv.h"8#include "page_pool_priv.h"
8 9
@@ -80,3 +81,71 @@ err_free_new_mem: · 71 unmodified lines
80 return err;81 return err;
81}82}
82EXPORT_SYMBOL_NS_GPL(netdev_rx_queue_restart, "NETDEV_INTERNAL");83EXPORT_SYMBOL_NS_GPL(netdev_rx_queue_restart, "NETDEV_INTERNAL");
84
85static int __net_mp_open_rxq(struct net_device *dev, unsigned ifq_idx,
86 struct pp_memory_provider_params *p)
87{
88 struct netdev_rx_queue *rxq;
89 int ret;
90
91 if (ifq_idx >= dev->real_num_rx_queues)
92 return -EINVAL;
93 ifq_idx = array_index_nospec(ifq_idx, dev->real_num_rx_queues);
94
95 rxq = __netif_get_rx_queue(dev, ifq_idx);
96 if (rxq->mp_params.mp_ops)
97 return -EEXIST;
98
99 rxq->mp_params = *p;
100 ret = netdev_rx_queue_restart(dev, ifq_idx);
101 if (ret) {
102 rxq->mp_params.mp_ops = NULL;
103 rxq->mp_params.mp_priv = NULL;
104 }
105 return ret;
106}
107
108int net_mp_open_rxq(struct net_device *dev, unsigned ifq_idx,
109 struct pp_memory_provider_params *p)
110{
111 int ret;
112
113 rtnl_lock();
114 ret = __net_mp_open_rxq(dev, ifq_idx, p);
115 rtnl_unlock();
116 return ret;
117}
118
119static void __net_mp_close_rxq(struct net_device *dev, unsigned ifq_idx,
120 struct pp_memory_provider_params *old_p)
121{
122 struct netdev_rx_queue *rxq;
123
124 if (WARN_ON_ONCE(ifq_idx >= dev->real_num_rx_queues))
125 return;
126
127 rxq = __netif_get_rx_queue(dev, ifq_idx);
128
129 /* Callers holding a netdev ref may get here after we already
130 * went thru shutdown via dev_memory_provider_uninstall().
131 */
132 if (dev->reg_state > NETREG_REGISTERED &&
133 !rxq->mp_params.mp_ops)
134 return;
135
136 if (WARN_ON_ONCE(rxq->mp_params.mp_ops != old_p->mp_ops ||
137 rxq->mp_params.mp_priv != old_p->mp_priv))
138 return;
139
140 rxq->mp_params.mp_ops = NULL;
141 rxq->mp_params.mp_priv = NULL;
142 WARN_ON(netdev_rx_queue_restart(dev, ifq_idx));
143}
144
145void net_mp_close_rxq(struct net_device *dev, unsigned ifq_idx,
146 struct pp_memory_provider_params *old_p)
147{
148 rtnl_lock();
149 __net_mp_close_rxq(dev, ifq_idx, old_p);
150 rtnl_unlock();
151}
net/core/page_pool.c+43−8
@@ -13,6 +13,7 @@ · 12 unmodified lines
13 13
14#include <net/netdev_rx_queue.h>14#include <net/netdev_rx_queue.h>
15#include <net/page_pool/helpers.h>15#include <net/page_pool/helpers.h>
16#include <net/page_pool/memory_provider.h>
16#include <net/xdp.h>17#include <net/xdp.h>
17 18
18#include <linux/dma-direction.h>19#include <linux/dma-direction.h>
@@ -285,13 +286,19 @@ static int page_pool_init(struct page_pool *pool, · 266 unmodified lines
285 rxq = __netif_get_rx_queue(pool->slow.netdev,286 rxq = __netif_get_rx_queue(pool->slow.netdev,
286 pool->slow.queue_idx);287 pool->slow.queue_idx);
287 pool->mp_priv = rxq->mp_params.mp_priv;288 pool->mp_priv = rxq->mp_params.mp_priv;
289 pool->mp_ops = rxq->mp_params.mp_ops;
288 }290 }
289 291
290 if (pool->mp_priv) {292 if (pool->mp_ops) {
291 if (!pool->dma_map || !pool->dma_sync)293 if (!pool->dma_map || !pool->dma_sync)
292 return -EOPNOTSUPP;294 return -EOPNOTSUPP;
293 295
294 err = mp_dmabuf_devmem_init(pool);296 if (WARN_ON(!is_kernel_rodata((unsigned long)pool->mp_ops))) {
297 err = -EFAULT;
298 goto free_ptr_ring;
299 }
300
301 err = pool->mp_ops->init(pool);
295 if (err) {302 if (err) {
296 pr_warn("%s() mem-provider init failed %d\n", __func__,303 pr_warn("%s() mem-provider init failed %d\n", __func__,
297 err);304 err);
@@ -587,8 +594,8 @@ netmem_ref page_pool_alloc_netmems(struct page_pool *pool, gfp_t gfp) · 289 unmodified lines
587 return netmem;594 return netmem;
588 595
589 /* Slow-path: cache empty, do real allocation */596 /* Slow-path: cache empty, do real allocation */
590 if (static_branch_unlikely(&page_pool_mem_providers) && pool->mp_priv)597 if (static_branch_unlikely(&page_pool_mem_providers) && pool->mp_ops)
591 netmem = mp_dmabuf_devmem_alloc_netmems(pool, gfp);598 netmem = pool->mp_ops->alloc_netmems(pool, gfp);
592 else599 else
593 netmem = __page_pool_alloc_pages_slow(pool, gfp);600 netmem = __page_pool_alloc_pages_slow(pool, gfp);
594 return netmem;601 return netmem;
@@ -679,8 +686,8 @@ void page_pool_return_page(struct page_pool *pool, netmem_ref netmem) · 84 unmodified lines
679 bool put;686 bool put;
680 687
681 put = true;688 put = true;
682 if (static_branch_unlikely(&page_pool_mem_providers) && pool->mp_priv)689 if (static_branch_unlikely(&page_pool_mem_providers) && pool->mp_ops)
683 put = mp_dmabuf_devmem_release_page(pool, netmem);690 put = pool->mp_ops->release_netmem(pool, netmem);
684 else691 else
685 __page_pool_release_page_dma(pool, netmem);692 __page_pool_release_page_dma(pool, netmem);
686 693
@@ -1048,8 +1055,8 @@ static void __page_pool_destroy(struct page_pool *pool) · 361 unmodified lines
1048 page_pool_unlist(pool);1055 page_pool_unlist(pool);
1049 page_pool_uninit(pool);1056 page_pool_uninit(pool);
1050 1057
1051 if (pool->mp_priv) {1058 if (pool->mp_ops) {
1052 mp_dmabuf_devmem_destroy(pool);1059 pool->mp_ops->destroy(pool);
1053 static_branch_dec(&page_pool_mem_providers);1060 static_branch_dec(&page_pool_mem_providers);
1054 }1061 }
1055 1062
@@ -1190,3 +1197,31 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid) · 134 unmodified lines
1190 }1197 }
1191}1198}
1192EXPORT_SYMBOL(page_pool_update_nid);1199EXPORT_SYMBOL(page_pool_update_nid);
1200
1201bool net_mp_niov_set_dma_addr(struct net_iov *niov, dma_addr_t addr)
1202{
1203 return page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov), addr);
1204}
1205
1206/* Associate a niov with a page pool. Should follow with a matching
1207 * net_mp_niov_clear_page_pool()
1208 */
1209void net_mp_niov_set_page_pool(struct page_pool *pool, struct net_iov *niov)
1210{
1211 netmem_ref netmem = net_iov_to_netmem(niov);
1212
1213 page_pool_set_pp_info(pool, netmem);
1214
1215 pool->pages_state_hold_cnt++;
1216 trace_page_pool_state_hold(pool, netmem, pool->pages_state_hold_cnt);
1217}
1218
1219/* Disassociate a niov from a page pool. Should only be used in the
1220 * ->release_netmem() path.
1221 */
1222void net_mp_niov_clear_page_pool(struct net_iov *niov)
1223{
1224 netmem_ref netmem = net_iov_to_netmem(niov);
1225
1226 page_pool_clear_pp_info(netmem);
1227}
net/core/page_pool_user.c+3−4
@@ -8,9 +8,9 @@ · 7 unmodified lines
8#include <net/netdev_rx_queue.h>8#include <net/netdev_rx_queue.h>
9#include <net/page_pool/helpers.h>9#include <net/page_pool/helpers.h>
10#include <net/page_pool/types.h>10#include <net/page_pool/types.h>
11#include <net/page_pool/memory_provider.h>
11#include <net/sock.h>12#include <net/sock.h>
12 13
13#include "devmem.h"
14#include "page_pool_priv.h"14#include "page_pool_priv.h"
15#include "netdev-genl-gen.h"15#include "netdev-genl-gen.h"
16 16
@@ -216,7 +216,6 @@ static int · 199 unmodified lines
216page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool,216page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool,
217 const struct genl_info *info)217 const struct genl_info *info)
218{218{
219 struct net_devmem_dmabuf_binding *binding = pool->mp_priv;
220 size_t inflight, refsz;219 size_t inflight, refsz;
221 unsigned int napi_id;220 unsigned int napi_id;
222 void *hdr;221 void *hdr;
@@ -249,7 +248,7 @@ page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool, · 26 unmodified lines
249 pool->user.detach_time))248 pool->user.detach_time))
250 goto err_cancel;249 goto err_cancel;
251 250
252 if (binding && nla_put_u32(rsp, NETDEV_A_PAGE_POOL_DMABUF, binding->id))251 if (pool->mp_ops && pool->mp_ops->nl_fill(pool->mp_priv, rsp, NULL))
253 goto err_cancel;252 goto err_cancel;
254 253
255 genlmsg_end(rsp, hdr);254 genlmsg_end(rsp, hdr);
@@ -356,7 +355,7 @@ void page_pool_unlist(struct page_pool *pool) · 100 unmodified lines
356int page_pool_check_memory_provider(struct net_device *dev,355int page_pool_check_memory_provider(struct net_device *dev,
357 struct netdev_rx_queue *rxq)356 struct netdev_rx_queue *rxq)
358{357{
359 struct net_devmem_dmabuf_binding *binding = rxq->mp_params.mp_priv;358 void *binding = rxq->mp_params.mp_priv;
360 struct page_pool *pool;359 struct page_pool *pool;
361 struct hlist_node *n;360 struct hlist_node *n;
362 361
net/ipv4/tcp.c+6−1
@@ -2476,6 +2476,11 @@ static int tcp_recvmsg_dmabuf(struct sock *sk, const struct sk_buff *skb, · 2475 unmodified lines
2476 }2476 }
2477 2477
2478 niov = skb_frag_net_iov(frag);2478 niov = skb_frag_net_iov(frag);
2479 if (!net_is_devmem_iov(niov)) {
2480 err = -ENODEV;
2481 goto out;
2482 }
2483
2479 end = start + skb_frag_size(frag);2484 end = start + skb_frag_size(frag);
2480 copy = end - offset;2485 copy = end - offset;
2481 2486
@@ -2494,7 +2499,7 @@ static int tcp_recvmsg_dmabuf(struct sock *sk, const struct sk_buff *skb, · 12 unmodified lines
2494 2499
2495 /* Will perform the exchange later */2500 /* Will perform the exchange later */
2496 dmabuf_cmsg.frag_token = tcp_xa_pool.tokens[tcp_xa_pool.idx];2501 dmabuf_cmsg.frag_token = tcp_xa_pool.tokens[tcp_xa_pool.idx];
2497 dmabuf_cmsg.dmabuf_id = net_iov_binding_id(niov);2502 dmabuf_cmsg.dmabuf_id = net_devmem_iov_binding_id(niov);
2498 2503
2499 offset += copy;2504 offset += copy;
2500 remaining_len -= copy;2505 remaining_len -= copy;
tools/include/uapi/linux/netdev.h+7−0
@@ -86,6 +86,11 @@ enum { · 85 unmodified lines
86 NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)86 NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
87};87};
88 88
89enum {
90 __NETDEV_A_IO_URING_PROVIDER_INFO_MAX,
91 NETDEV_A_IO_URING_PROVIDER_INFO_MAX = (__NETDEV_A_IO_URING_PROVIDER_INFO_MAX - 1)
92};
93
89enum {94enum {
90 NETDEV_A_PAGE_POOL_ID = 1,95 NETDEV_A_PAGE_POOL_ID = 1,
91 NETDEV_A_PAGE_POOL_IFINDEX,96 NETDEV_A_PAGE_POOL_IFINDEX,
@@ -94,6 +99,7 @@ enum { · 2 unmodified lines
94 NETDEV_A_PAGE_POOL_INFLIGHT_MEM,99 NETDEV_A_PAGE_POOL_INFLIGHT_MEM,
95 NETDEV_A_PAGE_POOL_DETACH_TIME,100 NETDEV_A_PAGE_POOL_DETACH_TIME,
96 NETDEV_A_PAGE_POOL_DMABUF,101 NETDEV_A_PAGE_POOL_DMABUF,
102 NETDEV_A_PAGE_POOL_IO_URING,
97 103
98 __NETDEV_A_PAGE_POOL_MAX,104 __NETDEV_A_PAGE_POOL_MAX,
99 NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX - 1)105 NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX - 1)
@@ -136,6 +142,7 @@ enum { · 36 unmodified lines
136 NETDEV_A_QUEUE_TYPE,142 NETDEV_A_QUEUE_TYPE,
137 NETDEV_A_QUEUE_NAPI_ID,143 NETDEV_A_QUEUE_NAPI_ID,
138 NETDEV_A_QUEUE_DMABUF,144 NETDEV_A_QUEUE_DMABUF,
145 NETDEV_A_QUEUE_IO_URING,
139 146
140 __NETDEV_A_QUEUE_MAX,147 __NETDEV_A_QUEUE_MAX,
141 NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)148 NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t