Skip to content

Commit 083772c

Browse files
kuba-mooPaolo Abeni
authored andcommitted
net: page_pool: record pools per netdev
Link the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work. Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose. Reviewed-by: Mina Almasry <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Acked-by: Jesper Dangaard Brouer <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent f17c696 commit 083772c

File tree

5 files changed

+114
-0
lines changed

5 files changed

+114
-0
lines changed

include/linux/list.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,26 @@ static inline void hlist_move_list(struct hlist_head *old,
11191119
old->first = NULL;
11201120
}
11211121

1122+
/**
1123+
* hlist_splice_init() - move all entries from one list to another
1124+
* @from: hlist_head from which entries will be moved
1125+
* @last: last entry on the @from list
1126+
* @to: hlist_head to which entries will be moved
1127+
*
1128+
* @to can be empty, @from must contain at least @last.
1129+
*/
1130+
static inline void hlist_splice_init(struct hlist_head *from,
1131+
struct hlist_node *last,
1132+
struct hlist_head *to)
1133+
{
1134+
if (to->first)
1135+
to->first->pprev = &last->next;
1136+
last->next = to->first;
1137+
to->first = from->first;
1138+
from->first->pprev = &to->first;
1139+
from->first = NULL;
1140+
}
1141+
11221142
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
11231143

11241144
#define hlist_for_each(pos, head) \

include/linux/netdevice.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,6 +2447,10 @@ struct net_device {
24472447
#if IS_ENABLED(CONFIG_DPLL)
24482448
struct dpll_pin *dpll_pin;
24492449
#endif
2450+
#if IS_ENABLED(CONFIG_PAGE_POOL)
2451+
/** @page_pools: page pools created for this netdevice */
2452+
struct hlist_head page_pools;
2453+
#endif
24502454
};
24512455
#define to_net_dev(d) container_of(d, struct net_device, dev)
24522456

include/linux/poison.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@
8383

8484
/********** net/core/skbuff.c **********/
8585
#define SKB_LIST_POISON_NEXT ((void *)(0x800 + POISON_POINTER_DELTA))
86+
/********** net/ **********/
87+
#define NET_PTR_POISON ((void *)(0x801 + POISON_POINTER_DELTA))
8688

8789
/********** kernel/bpf/ **********/
8890
#define BPF_PTR_POISON ((void *)(0xeB9FUL + POISON_POINTER_DELTA))

include/net/page_pool/types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <linux/dma-direction.h>
77
#include <linux/ptr_ring.h>
8+
#include <linux/types.h>
89

910
#define PP_FLAG_DMA_MAP BIT(0) /* Should page_pool do the DMA
1011
* map/unmap
@@ -48,6 +49,7 @@ struct pp_alloc_cache {
4849
* @pool_size: size of the ptr_ring
4950
* @nid: NUMA node id to allocate from pages from
5051
* @dev: device, for DMA pre-mapping purposes
52+
* @netdev: netdev this pool will serve (leave as NULL if none or multiple)
5153
* @napi: NAPI which is the sole consumer of pages, otherwise NULL
5254
* @dma_dir: DMA mapping direction
5355
* @max_len: max DMA sync memory size for PP_FLAG_DMA_SYNC_DEV
@@ -66,6 +68,7 @@ struct page_pool_params {
6668
unsigned int offset;
6769
);
6870
struct_group_tagged(page_pool_params_slow, slow,
71+
struct net_device *netdev;
6972
/* private: used by test code only */
7073
void (*init_callback)(struct page *page, void *arg);
7174
void *init_arg;
@@ -189,6 +192,7 @@ struct page_pool {
189192
struct page_pool_params_slow slow;
190193
/* User-facing fields, protected by page_pools_lock */
191194
struct {
195+
struct hlist_node list;
192196
u32 id;
193197
} user;
194198
};

net/core/page_pool_user.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <linux/mutex.h>
4+
#include <linux/netdevice.h>
45
#include <linux/xarray.h>
6+
#include <net/net_debug.h>
57
#include <net/page_pool/types.h>
68

79
#include "page_pool_priv.h"
810

911
static DEFINE_XARRAY_FLAGS(page_pools, XA_FLAGS_ALLOC1);
12+
/* Protects: page_pools, netdevice->page_pools, pool->slow.netdev, pool->user.
13+
* Ordering: inside rtnl_lock
14+
*/
1015
static DEFINE_MUTEX(page_pools_lock);
1116

17+
/* Page pools are only reachable from user space (via netlink) if they are
18+
* linked to a netdev at creation time. Following page pool "visibility"
19+
* states are possible:
20+
* - normal
21+
* - user.list: linked to real netdev, netdev: real netdev
22+
* - orphaned - real netdev has disappeared
23+
* - user.list: linked to lo, netdev: lo
24+
* - invisible - either (a) created without netdev linking, (b) unlisted due
25+
* to error, or (c) the entire namespace which owned this pool disappeared
26+
* - user.list: unhashed, netdev: unknown
27+
*/
28+
1229
int page_pool_list(struct page_pool *pool)
1330
{
1431
static u32 id_alloc_next;
@@ -20,6 +37,10 @@ int page_pool_list(struct page_pool *pool)
2037
if (err < 0)
2138
goto err_unlock;
2239

40+
if (pool->slow.netdev)
41+
hlist_add_head(&pool->user.list,
42+
&pool->slow.netdev->page_pools);
43+
2344
mutex_unlock(&page_pools_lock);
2445
return 0;
2546

@@ -32,5 +53,68 @@ void page_pool_unlist(struct page_pool *pool)
3253
{
3354
mutex_lock(&page_pools_lock);
3455
xa_erase(&page_pools, pool->user.id);
56+
hlist_del(&pool->user.list);
57+
mutex_unlock(&page_pools_lock);
58+
}
59+
60+
static void page_pool_unreg_netdev_wipe(struct net_device *netdev)
61+
{
62+
struct page_pool *pool;
63+
struct hlist_node *n;
64+
65+
mutex_lock(&page_pools_lock);
66+
hlist_for_each_entry_safe(pool, n, &netdev->page_pools, user.list) {
67+
hlist_del_init(&pool->user.list);
68+
pool->slow.netdev = NET_PTR_POISON;
69+
}
70+
mutex_unlock(&page_pools_lock);
71+
}
72+
73+
static void page_pool_unreg_netdev(struct net_device *netdev)
74+
{
75+
struct page_pool *pool, *last;
76+
struct net_device *lo;
77+
78+
lo = dev_net(netdev)->loopback_dev;
79+
80+
mutex_lock(&page_pools_lock);
81+
last = NULL;
82+
hlist_for_each_entry(pool, &netdev->page_pools, user.list) {
83+
pool->slow.netdev = lo;
84+
last = pool;
85+
}
86+
if (last)
87+
hlist_splice_init(&netdev->page_pools, &last->user.list,
88+
&lo->page_pools);
3589
mutex_unlock(&page_pools_lock);
3690
}
91+
92+
static int
93+
page_pool_netdevice_event(struct notifier_block *nb,
94+
unsigned long event, void *ptr)
95+
{
96+
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
97+
98+
if (event != NETDEV_UNREGISTER)
99+
return NOTIFY_DONE;
100+
101+
if (hlist_empty(&netdev->page_pools))
102+
return NOTIFY_OK;
103+
104+
if (netdev->ifindex != LOOPBACK_IFINDEX)
105+
page_pool_unreg_netdev(netdev);
106+
else
107+
page_pool_unreg_netdev_wipe(netdev);
108+
return NOTIFY_OK;
109+
}
110+
111+
static struct notifier_block page_pool_netdevice_nb = {
112+
.notifier_call = page_pool_netdevice_event,
113+
};
114+
115+
static int __init page_pool_user_init(void)
116+
{
117+
return register_netdevice_notifier(&page_pool_netdevice_nb);
118+
}
119+
120+
subsys_initcall(page_pool_user_init);

0 commit comments

Comments
 (0)