|  | .. SPDX-License-Identifier: GPL-2.0 | 
|  |  | 
|  | ============= | 
|  | Page Pool API | 
|  | ============= | 
|  |  | 
|  | The page_pool allocator is optimized for the XDP mode that uses one frame | 
|  | per-page, but it can fallback on the regular page allocator APIs. | 
|  |  | 
|  | Basic use involves replacing alloc_pages() calls with the | 
|  | page_pool_alloc_pages() call.  Drivers should use page_pool_dev_alloc_pages() | 
|  | replacing dev_alloc_pages(). | 
|  |  | 
|  | API keeps track of inflight pages, in order to let API user know | 
|  | when it is safe to free a page_pool object.  Thus, API users | 
|  | must run page_pool_release_page() when a page is leaving the page_pool or | 
|  | call page_pool_put_page() where appropriate in order to maintain correct | 
|  | accounting. | 
|  |  | 
|  | API user must call page_pool_put_page() once on a page, as it | 
|  | will either recycle the page, or in case of refcnt > 1, it will | 
|  | release the DMA mapping and inflight state accounting. | 
|  |  | 
|  | 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  | | 
|  | +-----------------+     +------------------+ | 
|  |  | 
|  | 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. | 
|  |  | 
|  | * page_pool_create(): Create a pool. | 
|  | * flags:      PP_FLAG_DMA_MAP, PP_FLAG_DMA_SYNC_DEV | 
|  | * order:      2^order pages on allocation | 
|  | * pool_size:  size of the ptr_ring | 
|  | * nid:        preferred NUMA node for allocation | 
|  | * dev:        struct device. Used on DMA operations | 
|  | * dma_dir:    DMA direction | 
|  | * max_len:    max DMA sync memory size | 
|  | * offset:     DMA address offset | 
|  |  | 
|  | * page_pool_put_page(): The outcome of this depends on the page refcnt. If the | 
|  | driver bumps the refcnt > 1 this will unmap the page. If the page refcnt is 1 | 
|  | the allocator owns the page and will try to recycle it in one of the pool | 
|  | caches. If PP_FLAG_DMA_SYNC_DEV is set, the page will be synced for_device | 
|  | using dma_sync_single_range_for_device(). | 
|  |  | 
|  | * page_pool_put_full_page(): Similar to page_pool_put_page(), but will DMA sync | 
|  | for the entire memory area configured in area pool->max_len. | 
|  |  | 
|  | * page_pool_recycle_direct(): Similar to page_pool_put_full_page() but caller | 
|  | must guarantee safe context (e.g NAPI), since it will recycle the page | 
|  | directly into the pool fast cache. | 
|  |  | 
|  | * page_pool_release_page(): Unmap the page (if mapped) and account for it on | 
|  | inflight counters. | 
|  |  | 
|  | * page_pool_dev_alloc_pages(): Get a page from the page allocator or page_pool | 
|  | caches. | 
|  |  | 
|  | * page_pool_get_dma_addr(): Retrieve the stored DMA address. | 
|  |  | 
|  | * page_pool_get_dma_dir(): Retrieve the stored DMA direction. | 
|  |  | 
|  | * page_pool_put_page_bulk(): Tries to refill a number of pages into the | 
|  | ptr_ring cache holding ptr_ring producer lock. If the ptr_ring is full, | 
|  | page_pool_put_page_bulk() will release leftover pages to the page allocator. | 
|  | page_pool_put_page_bulk() is suitable to be run inside the driver NAPI tx | 
|  | completion loop for the XDP_REDIRECT use case. | 
|  | Please note the caller must not use data area after running | 
|  | page_pool_put_page_bulk(), as this function overwrites it. | 
|  |  | 
|  | 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.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) { | 
|  | page_pool_release_page(page_pool, page); | 
|  | new_page = page_pool_dev_alloc_pages(page_pool); | 
|  | } | 
|  | } | 
|  |  | 
|  | Driver unload | 
|  | ------------- | 
|  |  | 
|  | .. code-block:: c | 
|  |  | 
|  | /* Driver unload */ | 
|  | page_pool_put_full_page(page_pool, page, false); | 
|  | xdp_rxq_info_unreg(&xdp_rxq); |