/* SPDX-License-Identifier: GPL-2.0 * * Network memory * * Author: Mina Almasry */ #ifndef _NET_NETMEM_H #define _NET_NETMEM_H #include #include #include /* These fields in struct page are used by the page_pool and net stack: * * struct { * unsigned long pp_magic; * struct page_pool *pp; * unsigned long _pp_mapping_pad; * unsigned long dma_addr; * atomic_long_t pp_ref_count; * }; * * We mirror the page_pool fields here so the page_pool can access these * fields without worrying whether the underlying fields belong to a * page or netmem_desc. * * CAUTION: Do not update the fields in netmem_desc without also * updating the anonymous aliasing union in struct net_iov. */ struct netmem_desc { unsigned long _flags; unsigned long pp_magic; struct page_pool *pp; unsigned long _pp_mapping_pad; unsigned long dma_addr; atomic_long_t pp_ref_count; }; #define NETMEM_DESC_ASSERT_OFFSET(pg, desc) \ static_assert(offsetof(struct page, pg) == \ offsetof(struct netmem_desc, desc)) NETMEM_DESC_ASSERT_OFFSET(flags, _flags); NETMEM_DESC_ASSERT_OFFSET(pp_magic, pp_magic); NETMEM_DESC_ASSERT_OFFSET(pp, pp); NETMEM_DESC_ASSERT_OFFSET(_pp_mapping_pad, _pp_mapping_pad); NETMEM_DESC_ASSERT_OFFSET(dma_addr, dma_addr); NETMEM_DESC_ASSERT_OFFSET(pp_ref_count, pp_ref_count); #undef NETMEM_DESC_ASSERT_OFFSET /* * Since struct netmem_desc uses the space in struct page, the size * should be checked, until struct netmem_desc has its own instance from * slab, to avoid conflicting with other members within struct page. */ static_assert(sizeof(struct netmem_desc) <= offsetof(struct page, _refcount)); /* net_iov */ DECLARE_STATIC_KEY_FALSE(page_pool_mem_providers); /* We overload the LSB of the struct page pointer to indicate whether it's * a page or net_iov. */ #define NET_IOV 0x01UL enum net_iov_type { NET_IOV_DMABUF, NET_IOV_IOURING, /* Force size to unsigned long to make the NET_IOV_ASSERTS below pass. */ NET_IOV_MAX = ULONG_MAX }; /* A memory descriptor representing abstract networking I/O vectors, * generally for non-pages memory that doesn't have its corresponding * struct page and needs to be explicitly allocated through slab. * * net_iovs are allocated and used by networking code, and the size of * the chunk is PAGE_SIZE. * * This memory can be any form of non-struct paged memory. Examples * include imported dmabuf memory and imported io_uring memory. See * net_iov_type for all the supported types. * * @pp_magic: pp field, similar to the one in struct page/struct * netmem_desc. * @pp: the pp this net_iov belongs to, if any. * @dma_addr: the dma addrs of the net_iov. Needed for the network * card to send/receive this net_iov. * @pp_ref_count: the pp ref count of this net_iov, exactly the same * usage as struct page/struct netmem_desc. * @owner: the net_iov_area this net_iov belongs to, if any. * @type: the type of the memory. Different types of net_iovs are * supported. */ struct net_iov { union { struct netmem_desc desc; /* XXX: The following part should be removed once all * the references to them are converted so as to be * accessed via netmem_desc e.g. niov->desc.pp instead * of niov->pp. */ struct { unsigned long _flags; unsigned long pp_magic; struct page_pool *pp; unsigned long _pp_mapping_pad; unsigned long dma_addr; atomic_long_t pp_ref_count; }; }; struct net_iov_area *owner; enum net_iov_type type; }; struct net_iov_area { /* Array of net_iovs for this area. */ struct net_iov *niovs; size_t num_niovs; /* Offset into the dma-buf where this chunk starts. */ unsigned long base_virtual; }; /* net_iov is union'ed with struct netmem_desc mirroring struct page, so * the page_pool can access these fields without worrying whether the * underlying fields are accessed via netmem_desc or directly via * net_iov, until all the references to them are converted so as to be * accessed via netmem_desc e.g. niov->desc.pp instead of niov->pp. * * The non-net stack fields of struct page are private to the mm stack * and must never be mirrored to net_iov. */ #define NET_IOV_ASSERT_OFFSET(desc, iov) \ static_assert(offsetof(struct netmem_desc, desc) == \ offsetof(struct net_iov, iov)) NET_IOV_ASSERT_OFFSET(_flags, _flags); NET_IOV_ASSERT_OFFSET(pp_magic, pp_magic); NET_IOV_ASSERT_OFFSET(pp, pp); NET_IOV_ASSERT_OFFSET(_pp_mapping_pad, _pp_mapping_pad); NET_IOV_ASSERT_OFFSET(dma_addr, dma_addr); NET_IOV_ASSERT_OFFSET(pp_ref_count, pp_ref_count); #undef NET_IOV_ASSERT_OFFSET static inline struct net_iov_area *net_iov_owner(const struct net_iov *niov) { return niov->owner; } static inline unsigned int net_iov_idx(const struct net_iov *niov) { return niov - net_iov_owner(niov)->niovs; } /* netmem */ /** * typedef netmem_ref - a nonexistent type marking a reference to generic * network memory. * * A netmem_ref can be a struct page* or a struct net_iov* underneath. * * Use the supplied helpers to obtain the underlying memory pointer and fields. */ typedef unsigned long __bitwise netmem_ref; static inline bool netmem_is_net_iov(const netmem_ref netmem) { return (__force unsigned long)netmem & NET_IOV; } /** * __netmem_to_page - unsafely get pointer to the &page backing @netmem * @netmem: netmem reference to convert * * Unsafe version of netmem_to_page(). When @netmem is always page-backed, * e.g. when it's a header buffer, performs faster and generates smaller * object code (no check for the LSB, no WARN). When @netmem points to IOV, * provokes undefined behaviour. * * Return: pointer to the &page (garbage if @netmem is not page-backed). */ static inline struct page *__netmem_to_page(netmem_ref netmem) { return (__force struct page *)netmem; } static inline struct page *netmem_to_page(netmem_ref netmem) { if (WARN_ON_ONCE(netmem_is_net_iov(netmem))) return NULL; return __netmem_to_page(netmem); } static inline struct net_iov *netmem_to_net_iov(netmem_ref netmem) { if (netmem_is_net_iov(netmem)) return (struct net_iov *)((__force unsigned long)netmem & ~NET_IOV); DEBUG_NET_WARN_ON_ONCE(true); return NULL; } static inline netmem_ref net_iov_to_netmem(struct net_iov *niov) { return (__force netmem_ref)((unsigned long)niov | NET_IOV); } #define page_to_netmem(p) (_Generic((p), \ const struct page * : (__force const netmem_ref)(p), \ struct page * : (__force netmem_ref)(p))) /** * virt_to_netmem - convert virtual memory pointer to a netmem reference * @data: host memory pointer to convert * * Return: netmem reference to the &page backing this virtual address. */ static inline netmem_ref virt_to_netmem(const void *data) { return page_to_netmem(virt_to_page(data)); } static inline int netmem_ref_count(netmem_ref netmem) { /* The non-pp refcount of net_iov is always 1. On net_iov, we only * support pp refcounting which uses the pp_ref_count field. */ if (netmem_is_net_iov(netmem)) return 1; return page_ref_count(netmem_to_page(netmem)); } static inline unsigned long netmem_pfn_trace(netmem_ref netmem) { if (netmem_is_net_iov(netmem)) return 0; return page_to_pfn(netmem_to_page(netmem)); } /** * __netmem_to_nmdesc - unsafely get pointer to the &netmem_desc backing * @netmem * @netmem: netmem reference to convert * * Unsafe version that can be used only when @netmem is always backed by * system memory, performs faster and generates smaller object code (no * check for the LSB, no WARN). When @netmem points to IOV, provokes * undefined behaviour. * * Return: pointer to the &netmem_desc (garbage if @netmem is not backed * by system memory). */ static inline struct netmem_desc *__netmem_to_nmdesc(netmem_ref netmem) { return (__force struct netmem_desc *)netmem; } /* __netmem_clear_lsb - convert netmem_ref to struct net_iov * for access to * common fields. * @netmem: netmem reference to extract as net_iov. * * All the sub types of netmem_ref (page, net_iov) have the same pp, pp_magic, * dma_addr, and pp_ref_count fields at the same offsets. Thus, we can access * these fields without a type check to make sure that the underlying mem is * net_iov or page. * * The resulting value of this function can only be used to access the fields * that are NET_IOV_ASSERT_OFFSET'd. Accessing any other fields will result in * undefined behavior. * * Return: the netmem_ref cast to net_iov* regardless of its underlying type. */ static inline struct net_iov *__netmem_clear_lsb(netmem_ref netmem) { return (struct net_iov *)((__force unsigned long)netmem & ~NET_IOV); } /* XXX: How to extract netmem_desc from page must be changed, once * netmem_desc no longer overlays on page and will be allocated through * slab. */ #define __pp_page_to_nmdesc(p) (_Generic((p), \ const struct page * : (const struct netmem_desc *)(p), \ struct page * : (struct netmem_desc *)(p))) /* CAUTION: Check if the page is a pp page before calling this helper or * know it's a pp page. */ #define pp_page_to_nmdesc(p) \ ({ \ DEBUG_NET_WARN_ON_ONCE(!page_pool_page_is_pp(p)); \ __pp_page_to_nmdesc(p); \ }) /** * __netmem_get_pp - unsafely get pointer to the &page_pool backing @netmem * @netmem: netmem reference to get the pointer from * * Unsafe version of netmem_get_pp(). When @netmem is always page-backed, * e.g. when it's a header buffer, performs faster and generates smaller * object code (avoids clearing the LSB). When @netmem points to IOV, * provokes invalid memory access. * * Return: pointer to the &page_pool (garbage if @netmem is not page-backed). */ static inline struct page_pool *__netmem_get_pp(netmem_ref netmem) { return __netmem_to_nmdesc(netmem)->pp; } static inline struct page_pool *netmem_get_pp(netmem_ref netmem) { return __netmem_clear_lsb(netmem)->pp; } static inline atomic_long_t *netmem_get_pp_ref_count_ref(netmem_ref netmem) { return &__netmem_clear_lsb(netmem)->pp_ref_count; } static inline bool netmem_is_pref_nid(netmem_ref netmem, int pref_nid) { /* NUMA node preference only makes sense if we're allocating * system memory. Memory providers (which give us net_iovs) * choose for us. */ if (netmem_is_net_iov(netmem)) return true; return page_to_nid(netmem_to_page(netmem)) == pref_nid; } static inline netmem_ref netmem_compound_head(netmem_ref netmem) { /* niov are never compounded */ if (netmem_is_net_iov(netmem)) return netmem; return page_to_netmem(compound_head(netmem_to_page(netmem))); } /** * __netmem_address - unsafely get pointer to the memory backing @netmem * @netmem: netmem reference to get the pointer for * * Unsafe version of netmem_address(). When @netmem is always page-backed, * e.g. when it's a header buffer, performs faster and generates smaller * object code (no check for the LSB). When @netmem points to IOV, provokes * undefined behaviour. * * Return: pointer to the memory (garbage if @netmem is not page-backed). */ static inline void *__netmem_address(netmem_ref netmem) { return page_address(__netmem_to_page(netmem)); } static inline void *netmem_address(netmem_ref netmem) { if (netmem_is_net_iov(netmem)) return NULL; return __netmem_address(netmem); } /** * netmem_is_pfmemalloc - check if @netmem was allocated under memory pressure * @netmem: netmem reference to check * * Return: true if @netmem is page-backed and the page was allocated under * memory pressure, false otherwise. */ static inline bool netmem_is_pfmemalloc(netmem_ref netmem) { if (netmem_is_net_iov(netmem)) return false; return page_is_pfmemalloc(netmem_to_page(netmem)); } static inline unsigned long netmem_get_dma_addr(netmem_ref netmem) { return __netmem_clear_lsb(netmem)->dma_addr; } void get_netmem(netmem_ref netmem); void put_netmem(netmem_ref netmem); #define netmem_dma_unmap_addr_set(NETMEM, PTR, ADDR_NAME, VAL) \ do { \ if (!netmem_is_net_iov(NETMEM)) \ dma_unmap_addr_set(PTR, ADDR_NAME, VAL); \ else \ dma_unmap_addr_set(PTR, ADDR_NAME, 0); \ } while (0) static inline void netmem_dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { if (!addr) return; dma_unmap_page_attrs(dev, addr, size, dir, attrs); } #endif /* _NET_NETMEM_H */