요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=============
Page Pool API
=============
.. kernel-doc:: include/net/page_pool/helpers.h
:doc: page_pool allocator
Architecture overview
=====================
.. code-block:: none
+------------------+
| Driver |
+------------------+
^
|
|
|
v
+--------------------------------------------+
| request memory |
+--------------------------------------------+
^ ^
| |
| Pool empty | Pool has entries
| |
v v
+-----------------------+ +------------------------+
| alloc (and map) pages | | get page from cache |
+-----------------------+ +------------------------+
^ ^
| |
| cache available | No entries, refill
| | from ptr-ring
| |
v v
+-----------------+ +------------------+
| Fast cache | | ptr-ring cache |
+-----------------+ +------------------+
Monitoring
==========
Information about page pools on the system can be accessed via the netdev
genetlink family (see Documentation/netlink/specs/netdev.yaml).
API interface
=============
The number of pools created **must** match the number of hardware queues
unless hardware restrictions make that impossible. This would otherwise beat the
purpose of page pool, which is allocate pages fast from cache without locking.
This lockless guarantee naturally comes from running under a NAPI softirq.
The protection doesn't strictly have to be NAPI, any guarantee that allocating
a page will cause no race conditions is enough.
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_create
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_params
.. kernel-doc:: include/net/page_pool/helpers.h
:identifiers: page_pool_put_page page_pool_put_full_page
page_pool_recycle_direct page_pool_free_va
page_pool_dev_alloc_pages page_pool_dev_alloc_frag
page_pool_dev_alloc page_pool_dev_alloc_va
page_pool_get_dma_addr page_pool_get_dma_dir
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_put_page_bulk page_pool_get_stats
DMA sync
--------
Driver is always responsible for syncing the pages for the CPU.
Drivers may choose to take care of syncing for the device as well
or set the ``PP_FLAG_DMA_SYNC_DEV`` flag to request that pages
allocated from the page pool are already synced for the device.
If ``PP_FLAG_DMA_SYNC_DEV`` is set, the driver must inform the core what portion
of the buffer has to be synced. This allows the core to avoid syncing the entire
page when the drivers knows that the device only accessed a portion of the page.
Most drivers will reserve headroom in front of the frame. This part
of the buffer is not touched by the device, so to avoid syncing
it drivers can set the ``offset`` field in struct page_pool_params
appropriately.
For pages recycled on the XDP xmit and skb paths the page pool will
use the ``max_len`` member of struct page_pool_params to decide how
much of the page needs to be synced (starting at ``offset``).
When directly freeing pages in the driver (page_pool_put_page())
the ``dma_sync_size`` argument specifies how much of the buffer needs
to be synced.
If in doubt set ``offset`` to 0, ``max_len`` to ``PAGE_SIZE`` and
pass -1 as ``dma_sync_size``. That combination of arguments is always
correct.
Note that the syncing parameters are for the entire page.
This is important to remember when using fragments (``PP_FLAG_PAGE_FRAG``),
where allocated buffers may be smaller than a full page.
Unless the driver author really understands page pool internals
it's recommended to always use ``offset = 0``, ``max_len = PAGE_SIZE``
with fragmented page pools.
Stats API and structures
------------------------
If the kernel is configured with ``CONFIG_PAGE_POOL_STATS=y``, the API
page_pool_get_stats() and structures described below are available.
It takes a pointer to a ``struct page_pool`` and a pointer to a struct
page_pool_stats allocated by the caller.
Older drivers expose page pool statistics via ethtool or debugfs.
The same statistics are accessible via the netlink netdev family
in a driver-independent fashion.
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_recycle_stats
struct page_pool_alloc_stats
struct page_pool_stats
Coding examples
===============
Registration
------------
.. code-block:: c
/* Page pool registration */
struct page_pool_params pp_params = { 0 };
struct xdp_rxq_info xdp_rxq;
int err;
pp_params.order = 0;
/* internal DMA mapping in page_pool */
pp_params.flags = PP_FLAG_DMA_MAP;
pp_params.pool_size = DESC_NUM;
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = priv->dev;
pp_params.napi = napi; /* only if locking is tied to NAPI */
pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
page_pool = page_pool_create(&pp_params);
err = xdp_rxq_info_reg(&xdp_rxq, ndev, 0);
if (err)
goto err_out;
err = xdp_rxq_info_reg_mem_model(&xdp_rxq, MEM_TYPE_PAGE_POOL, page_pool);
if (err)
goto err_out;
NAPI poller
-----------
.. code-block:: c
/* NAPI Rx poller */
enum dma_data_direction dma_dir;
dma_dir = page_pool_get_dma_dir(dring->page_pool);
while (done < budget) {
if (some error)
page_pool_recycle_direct(page_pool, page);
if (packet_is_xdp) {
if XDP_DROP:
page_pool_recycle_direct(page_pool, page);
} else (packet_is_skb) {
skb_mark_for_recycle(skb);
new_page = page_pool_dev_alloc_pages(page_pool);
}
}
Stats
-----
.. code-block:: c
#ifdef CONFIG_PAGE_POOL_STATS
/* retrieve stats */
struct page_pool_stats stats = { 0 };
if (page_pool_get_stats(page_pool, &stats)) {
/* perhaps the driver reports statistics with ethool */
ethtool_print_allocation_stats(&stats.alloc_stats);
ethtool_print_recycle_stats(&stats.recycle_stats);
}
#endif
Driver unload
-------------
.. code-block:: c
/* Driver unload */
page_pool_put_full_page(page_pool, page, false);
xdp_rxq_info_unreg(&xdp_rxq);
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Allocator architecture와 두 cache
1-43`include/net/page_pool/helpers.h`의 kernel-doc는 page-pool allocator 자체를 설명합니다. Page Pool은 network driver가 page 또는 fragment를 빠르게 재사용하도록 설계된 allocator입니다.
Driver가 memory를 요청했을 때 pool이 비어 있으면 새 page를 할당하고 필요하면 DMA mapping도 수행합니다. Pool에 entry가 있으면 cache에서 page를 가져옵니다. Fast cache에 entry가 있으면 바로 반환하고, 비어 있으면 ptr-ring cache에서 refill합니다.
이 구조의 목적은 hot allocation path를 fast cache에서 처리하고, 더 큰 ptr-ring을 refill source로 두며, 둘 다 비었을 때만 일반 page allocation·mapping 비용을 지불하는 것입니다.
원문의 ASCII allocator 구조를 hit·refill·miss 경로로 재구성했습니다.
.. SPDX-License-Identifier: GPL-2.0
=============
Page Pool API
=============
.. kernel-doc:: include/net/page_pool/helpers.h
:doc: page_pool allocator
Architecture overview
=====================
.. code-block:: none
+------------------+
| Driver |
+------------------+
^
|
|
|
v
+--------------------------------------------+
| request memory |
+--------------------------------------------+
^ ^
| |
| Pool empty | Pool has entries
| |
v v
+-----------------------+ +------------------------+
| alloc (and map) pages | | get page from cache |
+-----------------------+ +------------------------+
^ ^
| |
| cache available | No entries, refill
| | from ptr-ring
| |
v v
+-----------------+ +------------------+
| Fast cache | | ptr-ring cache |
+-----------------+ +------------------+
Monitoring과 lockless queue 규칙
44-73System의 page-pool 정보는 netdev Generic Netlink family로 조회할 수 있습니다. Protocol 정의는 `Documentation/netlink/specs/netdev.yaml`에 있습니다.
Hardware 제약 때문에 불가능한 경우를 제외하면 생성한 pool 수는 hardware queue 수와 반드시 같아야 합니다. 그렇지 않으면 locking 없이 cache에서 page를 빠르게 할당하려는 page-pool의 목적을 훼손합니다.
이 lockless 보장은 자연스럽게 NAPI softirq context에서 나옵니다. 반드시 NAPI여야 하는 것은 아니며 page allocation에 race가 없다는 다른 보장이 있어도 충분합니다.
문서는 `page_pool_create`, `struct page_pool_params`, page 반환·재활용 함수, page·fragment·VA allocation helper, DMA address·direction helper, bulk 반환과 stats API의 kernel-doc를 연결합니다. 함수명과 source path는 원문 directive에 그대로 보존되어 있습니다.
Kernel-doc에서 연결하는 역할별 interface입니다.
Monitoring
==========
Information about page pools on the system can be accessed via the netdev
genetlink family (see Documentation/netlink/specs/netdev.yaml).
API interface
=============
The number of pools created **must** match the number of hardware queues
unless hardware restrictions make that impossible. This would otherwise beat the
purpose of page pool, which is allocate pages fast from cache without locking.
This lockless guarantee naturally comes from running under a NAPI softirq.
The protection doesn't strictly have to be NAPI, any guarantee that allocating
a page will cause no race conditions is enough.
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_create
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_params
.. kernel-doc:: include/net/page_pool/helpers.h
:identifiers: page_pool_put_page page_pool_put_full_page
page_pool_recycle_direct page_pool_free_va
page_pool_dev_alloc_pages page_pool_dev_alloc_frag
page_pool_dev_alloc page_pool_dev_alloc_va
page_pool_get_dma_addr page_pool_get_dma_dir
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_put_page_bulk page_pool_get_stats
DMA sync 범위와 fragment 주의
74-107Page를 CPU용으로 sync하는 책임은 항상 driver에 있습니다. Device용 sync도 driver가 직접 할 수 있고, `PP_FLAG_DMA_SYNC_DEV`를 설정해 page pool에서 할당한 page가 이미 device용으로 sync되어 나오도록 요청할 수도 있습니다.
`PP_FLAG_DMA_SYNC_DEV`를 쓰면 buffer에서 sync해야 할 범위를 core에 알려야 합니다. Device가 page 일부만 접근하는 것을 driver가 알 때 전체 page sync를 피할 수 있습니다.
대부분의 driver는 frame 앞에 headroom을 예약하며 device는 이 부분을 건드리지 않습니다. 따라서 `struct page_pool_params.offset`을 적절히 설정하면 headroom sync를 생략할 수 있습니다.
XDP xmit과 skb path에서 재활용하는 page는 `offset`부터 `struct page_pool_params.max_len`만큼 sync합니다. Driver가 `page_pool_put_page()`로 직접 page를 해제할 때는 `dma_sync_size` argument가 sync할 buffer 길이를 정합니다.
확신이 없으면 `offset=0`, `max_len=PAGE_SIZE`, `dma_sync_size=-1`을 사용하면 항상 정확합니다. Sync parameter는 fragment 크기가 아니라 전체 page 기준입니다. `PP_FLAG_PAGE_FRAG`로 full page보다 작은 buffer를 할당하는 경우 특히 중요합니다. Page-pool 내부를 충분히 이해하지 못했다면 fragmented pool에서도 `offset=0`, `max_len=PAGE_SIZE`를 권장합니다.
Page Pool에서 core와 driver가 사용하는 범위 설정입니다.
DMA sync
--------
Driver is always responsible for syncing the pages for the CPU.
Drivers may choose to take care of syncing for the device as well
or set the ``PP_FLAG_DMA_SYNC_DEV`` flag to request that pages
allocated from the page pool are already synced for the device.
If ``PP_FLAG_DMA_SYNC_DEV`` is set, the driver must inform the core what portion
of the buffer has to be synced. This allows the core to avoid syncing the entire
page when the drivers knows that the device only accessed a portion of the page.
Most drivers will reserve headroom in front of the frame. This part
of the buffer is not touched by the device, so to avoid syncing
it drivers can set the ``offset`` field in struct page_pool_params
appropriately.
For pages recycled on the XDP xmit and skb paths the page pool will
use the ``max_len`` member of struct page_pool_params to decide how
much of the page needs to be synced (starting at ``offset``).
When directly freeing pages in the driver (page_pool_put_page())
the ``dma_sync_size`` argument specifies how much of the buffer needs
to be synced.
If in doubt set ``offset`` to 0, ``max_len`` to ``PAGE_SIZE`` and
pass -1 as ``dma_sync_size``. That combination of arguments is always
correct.
Note that the syncing parameters are for the entire page.
This is important to remember when using fragments (``PP_FLAG_PAGE_FRAG``),
where allocated buffers may be smaller than a full page.
Unless the driver author really understands page pool internals
it's recommended to always use ``offset = 0``, ``max_len = PAGE_SIZE``
with fragmented page pools.
Stats 설정과 driver-independent Netlink
108-123Kernel이 `CONFIG_PAGE_POOL_STATS=y`로 구성되면 `page_pool_get_stats()`와 관련 구조체를 사용할 수 있습니다. Caller가 할당한 `struct page_pool_stats`와 대상 `struct page_pool` pointer를 함수에 전달합니다.
예전 driver는 page-pool 통계를 ethtool 또는 debugfs로 노출했습니다. 같은 통계를 이제 driver와 무관한 방식으로 netlink netdev family에서 조회할 수 있습니다.
문서는 `struct page_pool_recycle_stats`, `struct page_pool_alloc_stats`, `struct page_pool_stats`의 kernel-doc를 포함합니다.
Stats API and structures
------------------------
If the kernel is configured with ``CONFIG_PAGE_POOL_STATS=y``, the API
page_pool_get_stats() and structures described below are available.
It takes a pointer to a ``struct page_pool`` and a pointer to a struct
page_pool_stats allocated by the caller.
Older drivers expose page pool statistics via ethtool or debugfs.
The same statistics are accessible via the netlink netdev family
in a driver-independent fashion.
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_recycle_stats
struct page_pool_alloc_stats
struct page_pool_stats
Pool과 XDP RX queue 등록
124-154등록 예제는 `struct page_pool_params`와 `struct xdp_rxq_info`를 준비합니다. `order=0`, internal DMA mapping을 위한 `PP_FLAG_DMA_MAP`, descriptor 수와 같은 `pool_size`, `NUMA_NO_NODE`, device, NAPI pointer를 지정합니다. Locking 보장이 NAPI에 묶일 때만 `napi`를 설정합니다.
DMA direction은 XDP program이 있으면 `DMA_BIDIRECTIONAL`, 없으면 `DMA_FROM_DEVICE`를 선택하고 `page_pool_create()`로 pool을 만듭니다.
그 뒤 `xdp_rxq_info_reg()`로 RX queue를 network device에 등록하고, `xdp_rxq_info_reg_mem_model(..., MEM_TYPE_PAGE_POOL, page_pool)`로 memory model과 pool을 연결합니다. 각 단계의 error는 `err_out`으로 이동합니다. 전체 C code를 그대로 보존했습니다.
Driver setup의 호출 순서입니다.
Coding examples
===============
Registration
------------
.. code-block:: c
/* Page pool registration */
struct page_pool_params pp_params = { 0 };
struct xdp_rxq_info xdp_rxq;
int err;
pp_params.order = 0;
/* internal DMA mapping in page_pool */
pp_params.flags = PP_FLAG_DMA_MAP;
pp_params.pool_size = DESC_NUM;
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = priv->dev;
pp_params.napi = napi; /* only if locking is tied to NAPI */
pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
page_pool = page_pool_create(&pp_params);
err = xdp_rxq_info_reg(&xdp_rxq, ndev, 0);
if (err)
goto err_out;
err = xdp_rxq_info_reg_mem_model(&xdp_rxq, MEM_TYPE_PAGE_POOL, page_pool);
if (err)
goto err_out;
NAPI RX poller의 recycle·allocation
155-176NAPI RX poller 예제는 `page_pool_get_dma_dir()`로 pool의 DMA direction을 얻고 budget까지 packet을 처리합니다.
오류가 나거나 XDP packet이 `XDP_DROP` 결과를 받으면 `page_pool_recycle_direct(page_pool, page)`로 현재 NAPI context에서 즉시 recycle합니다. SKB path에서는 `skb_mark_for_recycle(skb)`로 page가 나중에 pool로 돌아오게 표시하고 `page_pool_dev_alloc_pages(page_pool)`로 새 RX page를 얻습니다.
Packet 경로별 page lifetime입니다.
NAPI poller
-----------
.. code-block:: c
/* NAPI Rx poller */
enum dma_data_direction dma_dir;
dma_dir = page_pool_get_dma_dir(dring->page_pool);
while (done < budget) {
if (some error)
page_pool_recycle_direct(page_pool, page);
if (packet_is_xdp) {
if XDP_DROP:
page_pool_recycle_direct(page_pool, page);
} else (packet_is_skb) {
skb_mark_for_recycle(skb);
new_page = page_pool_dev_alloc_pages(page_pool);
}
}
Allocation·recycle 통계 조회
177-191Stats 예제는 `CONFIG_PAGE_POOL_STATS`일 때 `struct page_pool_stats`를 0으로 초기화하고 `page_pool_get_stats(page_pool, &stats)`를 호출합니다.
통계를 얻으면 driver가 ethtool로 노출하는 경우처럼 `alloc_stats`와 `recycle_stats`를 각각 출력합니다. Compile-time guard와 example symbol은 원문 code에 보존되어 있습니다.
Stats
-----
.. code-block:: c
#ifdef CONFIG_PAGE_POOL_STATS
/* retrieve stats */
struct page_pool_stats stats = { 0 };
if (page_pool_get_stats(page_pool, &stats)) {
/* perhaps the driver reports statistics with ethool */
ethtool_print_allocation_stats(&stats.alloc_stats);
ethtool_print_recycle_stats(&stats.recycle_stats);
}
#endif
Driver unload와 resource 해제
192-199Driver unload 예제는 남아 있는 page를 `page_pool_put_full_page(page_pool, page, false)`로 pool에 반환하고 `xdp_rxq_info_unreg(&xdp_rxq)`로 XDP RX queue 정보를 해제합니다.
등록·운영·해제의 대응 관계입니다.
Driver unload
-------------
.. code-block:: c
/* Driver unload */
page_pool_put_full_page(page_pool, page, false);
xdp_rxq_info_unreg(&xdp_rxq);
요약·해설
page_pool.rst:1-199Page Pool은 hardware queue마다 독립 pool을 두고 NAPI의 직렬화 보장을 이용해 RX page를 lock 없이 빠르게 재사용합니다. DMA sync 범위는 전체 page 기준으로 지정하며 XDP·skb lifetime과 driver teardown에서 올바른 반환 helper를 선택해야 합니다.
할당부터 재활용·unload까지입니다.