History

net: devmem: allow rx-page-size > PAGE_SIZE per dmabuf binding

Every devmem dmabuf binding today hands the page_pool PAGE_SIZE niovs. This caps a single RX descriptor at PAGE_SIZE, burning CPU on buffer churn for large flows. Add a bind-time netlink attribute, NETDEV_A_DMABUF_RX_PAGE_SIZE, that lets userspace request a larger niov size. The value must be a power of two >= PAGE_SIZE. The TX path is changed to always pass PAGE_SIZE. Measurements: Setup: kperf in devmem RX/TX cuda mode, 4 flows, 64 MB messages, 60s, dctcp, num-rx-queues=4, dmabuf-rx/tx-size-mb=2048, 10 runs per niov size, mlx5. CPU Util: niov net sirq % net idle % app sys % app idle % ----- ---------------- ---------------- ---------------- ---------------- 4K 62.38 +/- 8.27 33.40 +/- 7.51 54.15 +/- 10.23 43.67 +/- 10.53 16K 58.91 +/- 5.35 35.23 +/- 5.88 41.05 +/- 8.87 56.42 +/- 9.24 32K 64.12 +/- 0.68 31.09 +/- 1.48 44.54 +/- 3.51 52.63 +/- 3.65 64K 54.69 +/- 5.54 39.67 +/- 5.81 35.47 +/- 3.11 61.97 +/- 3.27 RX app sys % drops ~19% from 4K to 64K. Throughput: niov RX dev Gbps RX flow avg Gbps ----- ---------------- ----------------- 4K 300.63 +/- 53.21 75.16 +/- 13.30 16K 321.35 +/- 28.20 80.34 +/- 7.05 32K 347.63 +/- 2.20 86.91 +/- 0.55 64K 332.11 +/- 14.26 83.03 +/- 3.56 Throughput seems to increase, but the stdev is pretty wide so could just be noise. kperf support (not yet merged): https://github.com/facebookexperimental/kperf/commit/8837577f920876bce6986ec18869ac04439ebcd2

Bobby Eshleman committed 3 months ago

Commit b27a8561 parent 55d20f58 files · +78 −23Browse files

Unified · Split

8 files
  1. Documentation/netlink/specs/netdev.yaml +19 −0
  2. include/uapi/linux/netdev.h +1 −0
  3. net/core/devmem.c +22 −15
  4. net/core/devmem.h +9 −4
  5. net/core/netdev-genl-gen.c +9 −2
  6. net/core/netdev-genl-gen.h +1 −0
  7. net/core/netdev-genl.c +16 −2
  8. tools/include/uapi/linux/netdev.h +1 −0
Documentation/netlink/specs/netdev.yaml+19−0
@@ -6,6 +6,14 @@ doc: >- · 5 unmodified lines
6 netdev configuration over generic netlink.6 netdev configuration over generic netlink.
7 7
8definitions:8definitions:
9 -
10 type: const
11 name: page-size
12 # Dummy value, codegen needs a number. The real value comes from
13 # the PAGE_SIZE macro in the header below.
14 value: 0
15 header: asm/page.h
16 scope: kernel
9 -17 -
10 type: flags18 type: flags
11 name: xdp-act19 name: xdp-act
@@ -598,6 +606,16 @@ attribute-sets: · 586 unmodified lines
598 type: u32606 type: u32
599 checks:607 checks:
600 min: 1608 min: 1
609 -
610 name: rx-page-size
611 doc: |
612 Size in bytes of each device page the NIC writes into from the bound
613 dmabuf. Must be a power of two and >= PAGE_SIZE; defaults to
614 PAGE_SIZE.
615 type: u32
616 checks:
617 min: page-size
618 max: u32-max
601 619
602operations:620operations:
603 list:621 list:
@@ -812,6 +830,7 @@ operations: · 208 unmodified lines
812 - ifindex830 - ifindex
813 - fd831 - fd
814 - queues832 - queues
833 - rx-page-size
815 reply:834 reply:
816 attributes:835 attributes:
817 - id836 - id
include/uapi/linux/netdev.h+1−0
@@ -219,6 +219,7 @@ enum { · 218 unmodified lines
219 NETDEV_A_DMABUF_QUEUES,219 NETDEV_A_DMABUF_QUEUES,
220 NETDEV_A_DMABUF_FD,220 NETDEV_A_DMABUF_FD,
221 NETDEV_A_DMABUF_ID,221 NETDEV_A_DMABUF_ID,
222 NETDEV_A_DMABUF_RX_PAGE_SIZE,
222 223
223 __NETDEV_A_DMABUF_MAX,224 __NETDEV_A_DMABUF_MAX,
224 NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_MAX - 1)225 NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_MAX - 1)
net/core/devmem.c+22−15
@@ -46,7 +46,7 @@ static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov) · 45 unmodified lines
46 46
47 owner = net_devmem_iov_to_chunk_owner(niov);47 owner = net_devmem_iov_to_chunk_owner(niov);
48 return owner->base_dma_addr +48 return owner->base_dma_addr +
49 ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT);49 ((dma_addr_t)net_iov_idx(niov) << owner->binding->niov_shift);
50}50}
51 51
52static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)52static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)
@@ -93,13 +93,14 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) · 40 unmodified lines
93 ssize_t offset;93 ssize_t offset;
94 ssize_t index;94 ssize_t index;
95 95
96 dma_addr = gen_pool_alloc_owner(binding->chunk_pool, PAGE_SIZE,96 dma_addr = gen_pool_alloc_owner(binding->chunk_pool,
97 1UL << binding->niov_shift,
97 (void **)&owner);98 (void **)&owner);
98 if (!dma_addr)99 if (!dma_addr)
99 return NULL;100 return NULL;
100 101
101 offset = dma_addr - owner->base_dma_addr;102 offset = dma_addr - owner->base_dma_addr;
102 index = offset / PAGE_SIZE;103 index = offset >> binding->niov_shift;
103 niov = &owner->area.niovs[index];104 niov = &owner->area.niovs[index];
104 105
105 niov->desc.pp_magic = 0;106 niov->desc.pp_magic = 0;
@@ -113,12 +114,13 @@ void net_devmem_free_dmabuf(struct net_iov *niov) · 7 unmodified lines
113{114{
114 struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);115 struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);
115 unsigned long dma_addr = net_devmem_get_dma_addr(niov);116 unsigned long dma_addr = net_devmem_get_dma_addr(niov);
117 size_t niov_size = 1UL << binding->niov_shift;
116 118
117 if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,119 if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,
118 PAGE_SIZE)))120 niov_size)))
119 return;121 return;
120 122
121 gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE);123 gen_pool_free(binding->chunk_pool, dma_addr, niov_size);
122}124}
123 125
124void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)126void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
@@ -163,6 +165,9 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, · 38 unmodified lines
163 u32 xa_idx;165 u32 xa_idx;
164 int err;166 int err;
165 167
168 if (binding->niov_shift != PAGE_SHIFT)
169 mp_params.rx_page_size = 1U << binding->niov_shift;
170
166 err = netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack);171 err = netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack);
167 if (err)172 if (err)
168 return err;173 return err;
@@ -184,10 +189,12 @@ struct net_devmem_dmabuf_binding * · 15 unmodified lines
184net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,189net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
185 struct device *dma_dev,190 struct device *dma_dev,
186 enum dma_data_direction direction,191 enum dma_data_direction direction,
187 unsigned int dmabuf_fd, struct netdev_nl_sock *priv,192 unsigned int dmabuf_fd, unsigned int niov_shift,
193 struct netdev_nl_sock *priv,
188 struct netlink_ext_ack *extack)194 struct netlink_ext_ack *extack)
189{195{
190 struct net_devmem_dmabuf_binding *binding;196 struct net_devmem_dmabuf_binding *binding;
197 size_t niov_size = 1UL << niov_shift;
191 static u32 id_alloc_next;198 static u32 id_alloc_next;
192 struct scatterlist *sg;199 struct scatterlist *sg;
193 struct dma_buf *dmabuf;200 struct dma_buf *dmabuf;
@@ -213,6 +220,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, · 19 unmodified lines
213 220
214 binding->dev = dev;221 binding->dev = dev;
215 binding->vdev = vdev;222 binding->vdev = vdev;
223 binding->niov_shift = niov_shift;
216 xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);224 xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);
217 225
218 err = percpu_ref_init(&binding->ref,226 err = percpu_ref_init(&binding->ref,
@@ -255,11 +263,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, · 36 unmodified lines
255 }263 }
256 }264 }
257 265
258 /* For simplicity we expect to make PAGE_SIZE allocations, but the266 binding->chunk_pool = gen_pool_create(niov_shift,
259 * binding can be much more flexible than that. We may be able to
260 * allocate MTU sized chunks here. Leave that for future work...
261 */
262 binding->chunk_pool = gen_pool_create(PAGE_SHIFT,
263 dev_to_node(&dev->dev));267 dev_to_node(&dev->dev));
264 if (!binding->chunk_pool) {268 if (!binding->chunk_pool) {
265 err = -ENOMEM;269 err = -ENOMEM;
@@ -273,9 +277,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, · 7 unmodified lines
273 size_t len = sg_dma_len(sg);277 size_t len = sg_dma_len(sg);
274 struct net_iov *niov;278 struct net_iov *niov;
275 279
276 if (!IS_ALIGNED(len, PAGE_SIZE)) {280 if (!IS_ALIGNED(dma_addr, niov_size) ||
281 !IS_ALIGNED(len, niov_size)) {
277 err = -EINVAL;282 err = -EINVAL;
278 NL_SET_ERR_MSG(extack, "dma-buf SG length must be PAGE_SIZE aligned");283 NL_SET_ERR_MSG_FMT(extack,
284 "dmabuf sg entry (addr=%pad, len=%zu) not aligned to niov size %zu",
285 &dma_addr, len, niov_size);
279 goto err_free_chunks;286 goto err_free_chunks;
280 }287 }
281 288
@@ -288,7 +295,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, · 6 unmodified lines
288 295
289 owner->area.base_virtual = virtual;296 owner->area.base_virtual = virtual;
290 owner->base_dma_addr = dma_addr;297 owner->base_dma_addr = dma_addr;
291 owner->area.num_niovs = len / PAGE_SIZE;298 owner->area.num_niovs = len >> niov_shift;
292 owner->binding = binding;299 owner->binding = binding;
293 300
294 err = gen_pool_add_owner(binding->chunk_pool, dma_addr,301 err = gen_pool_add_owner(binding->chunk_pool, dma_addr,
@@ -454,7 +461,7 @@ int mp_dmabuf_devmem_init(struct page_pool *pool) · 159 unmodified lines
454 pool->dma_sync = false;461 pool->dma_sync = false;
455 pool->dma_sync_for_cpu = false;462 pool->dma_sync_for_cpu = false;
456 463
457 if (pool->p.order != 0)464 if (pool->p.order != binding->niov_shift - PAGE_SHIFT)
458 return -E2BIG;465 return -E2BIG;
459 466
460 net_devmem_dmabuf_binding_get(binding);467 net_devmem_dmabuf_binding_get(binding);
net/core/devmem.h+9−4
@@ -71,6 +71,8 @@ struct net_devmem_dmabuf_binding { · 70 unmodified lines
71 */71 */
72 struct net_iov **tx_vec;72 struct net_iov **tx_vec;
73 73
74 unsigned int niov_shift;
75
74 struct work_struct unbind_w;76 struct work_struct unbind_w;
75};77};
76 78
@@ -93,7 +95,8 @@ struct net_devmem_dmabuf_binding * · 16 unmodified lines
93net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,95net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
94 struct device *dma_dev,96 struct device *dma_dev,
95 enum dma_data_direction direction,97 enum dma_data_direction direction,
96 unsigned int dmabuf_fd, struct netdev_nl_sock *priv,98 unsigned int dmabuf_fd, unsigned int niov_shift,
99 struct netdev_nl_sock *priv,
97 struct netlink_ext_ack *extack);100 struct netlink_ext_ack *extack);
98struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id);101struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id);
99void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding);102void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding);
@@ -122,10 +125,11 @@ static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov) · 22 unmodified lines
122 125
123static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)126static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)
124{127{
125 struct net_iov_area *owner = net_iov_owner(niov);128 struct dmabuf_genpool_chunk_owner *co =
129 net_devmem_iov_to_chunk_owner(niov);
126 130
127 return owner->base_virtual +131 return net_iov_owner(niov)->base_virtual +
128 ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT);132 ((unsigned long)net_iov_idx(niov) << co->binding->niov_shift);
129}133}
130 134
131static inline bool135static inline bool
@@ -175,6 +179,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, · 43 unmodified lines
175 struct device *dma_dev,179 struct device *dma_dev,
176 enum dma_data_direction direction,180 enum dma_data_direction direction,
177 unsigned int dmabuf_fd,181 unsigned int dmabuf_fd,
182 unsigned int niov_shift,
178 struct netdev_nl_sock *priv,183 struct netdev_nl_sock *priv,
179 struct netlink_ext_ack *extack)184 struct netlink_ext_ack *extack)
180{185{
net/core/netdev-genl-gen.c+9−2
@@ -11,6 +11,7 @@ · 10 unmodified lines
11 11
12#include <uapi/linux/netdev.h>12#include <uapi/linux/netdev.h>
13#include <net/netdev_netlink.h>13#include <net/netdev_netlink.h>
14#include <asm/page.h>
14 15
15/* Integer value ranges */16/* Integer value ranges */
16static const struct netlink_range_validation netdev_a_page_pool_id_range = {17static const struct netlink_range_validation netdev_a_page_pool_id_range = {
@@ -27,6 +28,11 @@ static const struct netlink_range_validation netdev_a_napi_defer_hard_irqs_range · 10 unmodified lines
27 .max = S32_MAX,28 .max = S32_MAX,
28};29};
29 30
31static const struct netlink_range_validation netdev_a_dmabuf_rx_page_size_range = {
32 .min = PAGE_SIZE,
33 .max = U32_MAX,
34};
35
30/* Common nested types */36/* Common nested types */
31const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1] = {37const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1] = {
32 [NETDEV_A_LEASE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),38 [NETDEV_A_LEASE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
@@ -106,10 +112,11 @@ static const struct nla_policy netdev_qstats_get_nl_policy[NETDEV_A_QSTATS_SCOPE · 73 unmodified lines
106};112};
107 113
108/* NETDEV_CMD_BIND_RX - do */114/* NETDEV_CMD_BIND_RX - do */
109static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_FD + 1] = {115static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_RX_PAGE_SIZE + 1] = {
110 [NETDEV_A_DMABUF_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),116 [NETDEV_A_DMABUF_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
111 [NETDEV_A_DMABUF_FD] = { .type = NLA_U32, },117 [NETDEV_A_DMABUF_FD] = { .type = NLA_U32, },
112 [NETDEV_A_DMABUF_QUEUES] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy),118 [NETDEV_A_DMABUF_QUEUES] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy),
119 [NETDEV_A_DMABUF_RX_PAGE_SIZE] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_dmabuf_rx_page_size_range),
113};120};
114 121
115/* NETDEV_CMD_NAPI_SET - do */122/* NETDEV_CMD_NAPI_SET - do */
@@ -219,7 +226,7 @@ static const struct genl_split_ops netdev_nl_ops[] = { · 103 unmodified lines
219 .cmd = NETDEV_CMD_BIND_RX,226 .cmd = NETDEV_CMD_BIND_RX,
220 .doit = netdev_nl_bind_rx_doit,227 .doit = netdev_nl_bind_rx_doit,
221 .policy = netdev_bind_rx_nl_policy,228 .policy = netdev_bind_rx_nl_policy,
222 .maxattr = NETDEV_A_DMABUF_FD,229 .maxattr = NETDEV_A_DMABUF_RX_PAGE_SIZE,
223 .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,230 .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
224 },231 },
225 {232 {
net/core/netdev-genl-gen.h+1−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
12 12
13#include <uapi/linux/netdev.h>13#include <uapi/linux/netdev.h>
14#include <net/netdev_netlink.h>14#include <net/netdev_netlink.h>
15#include <asm/page.h>
15 16
16/* Common nested types */17/* Common nested types */
17extern const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1];18extern const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1];
net/core/netdev-genl.c+16−2
@@ -1013,6 +1013,7 @@ netdev_nl_get_dma_dev(struct net_device *netdev, unsigned long *rxq_bitmap, · 1012 unmodified lines
1013int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)1013int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
1014{1014{
1015 struct net_devmem_dmabuf_binding *binding;1015 struct net_devmem_dmabuf_binding *binding;
1016 unsigned int niov_shift = PAGE_SHIFT;
1016 u32 ifindex, dmabuf_fd, rxq_idx;1017 u32 ifindex, dmabuf_fd, rxq_idx;
1017 struct netdev_nl_sock *priv;1018 struct netdev_nl_sock *priv;
1018 struct net_device *netdev;1019 struct net_device *netdev;
@@ -1030,6 +1031,18 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) · 11 unmodified lines
1030 ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);1031 ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
1031 dmabuf_fd = nla_get_u32(info->attrs[NETDEV_A_DMABUF_FD]);1032 dmabuf_fd = nla_get_u32(info->attrs[NETDEV_A_DMABUF_FD]);
1032 1033
1034 if (info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]) {
1035 u32 rx_page_size = nla_get_u32(info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]);
1036
1037 if (!is_power_of_2(rx_page_size)) {
1038 NL_SET_ERR_MSG_ATTR(info->extack,
1039 info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE],
1040 "rx-page-size must be a power of 2");
1041 return -EINVAL;
1042 }
1043 niov_shift = ilog2(rx_page_size);
1044 }
1045
1033 priv = genl_sk_priv_get(&netdev_nl_family, NETLINK_CB(skb).sk);1046 priv = genl_sk_priv_get(&netdev_nl_family, NETLINK_CB(skb).sk);
1034 if (IS_ERR(priv))1047 if (IS_ERR(priv))
1035 return PTR_ERR(priv);1048 return PTR_ERR(priv);
@@ -1080,7 +1093,8 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) · 44 unmodified lines
1080 }1093 }
1081 1094
1082 binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE,1095 binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE,
1083 dmabuf_fd, priv, info->extack);1096 dmabuf_fd, niov_shift, priv,
1097 info->extack);
1084 if (IS_ERR(binding)) {1098 if (IS_ERR(binding)) {
1085 err = PTR_ERR(binding);1099 err = PTR_ERR(binding);
1086 goto err_rxq_bitmap;1100 goto err_rxq_bitmap;
@@ -1221,7 +1235,7 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) · 134 unmodified lines
1221 binding = net_devmem_bind_dmabuf(bind_dev,1235 binding = net_devmem_bind_dmabuf(bind_dev,
1222 bind_dev != netdev ? netdev : NULL,1236 bind_dev != netdev ? netdev : NULL,
1223 dma_dev, DMA_TO_DEVICE, dmabuf_fd,1237 dma_dev, DMA_TO_DEVICE, dmabuf_fd,
1224 priv, info->extack);1238 PAGE_SHIFT, priv, info->extack);
1225 if (IS_ERR(binding)) {1239 if (IS_ERR(binding)) {
1226 err = PTR_ERR(binding);1240 err = PTR_ERR(binding);
1227 goto err_unlock_bind_dev;1241 goto err_unlock_bind_dev;
tools/include/uapi/linux/netdev.h+1−0
@@ -219,6 +219,7 @@ enum { · 218 unmodified lines
219 NETDEV_A_DMABUF_QUEUES,219 NETDEV_A_DMABUF_QUEUES,
220 NETDEV_A_DMABUF_FD,220 NETDEV_A_DMABUF_FD,
221 NETDEV_A_DMABUF_ID,221 NETDEV_A_DMABUF_ID,
222 NETDEV_A_DMABUF_RX_PAGE_SIZE,
222 223
223 __NETDEV_A_DMABUF_MAX,224 __NETDEV_A_DMABUF_MAX,
224 NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_MAX - 1)225 NETDEV_A_DMABUF_MAX = (__NETDEV_A_DMABUF_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