| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* zpdesc.h: zsmalloc pool memory descriptor | |
| * | |
| * Written by Alex Shi <alexs@kernel.org> | |
| * Hyeonggon Yoo <42.hyeyoo@gmail.com> | |
| */ | |
| #ifndef __MM_ZPDESC_H__ | |
| #define __MM_ZPDESC_H__ | |
| #include <linux/migrate.h> | |
| #include <linux/pagemap.h> | |
| /* | |
| * struct zpdesc - Memory descriptor for zsmalloc pool memory. | |
| * @flags: Page flags, mostly unused by zsmalloc. | |
| * @lru: Indirectly used by page migration. | |
| * @movable_ops: Used by page migration. | |
| * @next: Next zpdesc in a zspage in zsmalloc pool. | |
| * @handle: For huge zspage in zsmalloc pool. | |
| * @zspage: Points to the zspage this zpdesc is a part of. | |
| * @first_obj_offset: First object offset in zsmalloc pool. | |
| * @_refcount: The number of references to this zpdesc. | |
| * | |
| * This struct overlays struct page for now. Do not modify without a good | |
| * understanding of the issues. In particular, do not expand into the overlap | |
| * with memcg_data. | |
| * | |
| * Page flags used: | |
| * * PG_private identifies the first component page. | |
| * * PG_locked is used by page migration code. | |
| */ | |
| struct zpdesc { | |
| unsigned long flags; | |
| struct list_head lru; | |
| unsigned long movable_ops; | |
| union { | |
| struct zpdesc *next; | |
| unsigned long handle; | |
| }; | |
| struct zspage *zspage; | |
| /* | |
| * Only the lower 24 bits are available for offset, limiting a page | |
| * to 16 MiB. The upper 8 bits are reserved for PGTY_zsmalloc. | |
| * | |
| * Do not access this field directly. | |
| * Instead, use {get,set}_first_obj_offset() helpers. | |
| */ | |
| unsigned int first_obj_offset; | |
| atomic_t _refcount; | |
| }; | |
| #define ZPDESC_MATCH(pg, zp) \ | |
| static_assert(offsetof(struct page, pg) == offsetof(struct zpdesc, zp)) | |
| ZPDESC_MATCH(flags, flags); | |
| ZPDESC_MATCH(lru, lru); | |
| ZPDESC_MATCH(mapping, movable_ops); | |
| ZPDESC_MATCH(__folio_index, next); | |
| ZPDESC_MATCH(__folio_index, handle); | |
| ZPDESC_MATCH(private, zspage); | |
| ZPDESC_MATCH(page_type, first_obj_offset); | |
| ZPDESC_MATCH(_refcount, _refcount); | |
| #undef ZPDESC_MATCH | |
| static_assert(sizeof(struct zpdesc) <= sizeof(struct page)); | |
| /* | |
| * zpdesc_page - The first struct page allocated for a zpdesc | |
| * @zp: The zpdesc. | |
| * | |
| * A convenience wrapper for converting zpdesc to the first struct page of the | |
| * underlying folio, to communicate with code not yet converted to folio or | |
| * struct zpdesc. | |
| * | |
| */ | |
| #define zpdesc_page(zp) (_Generic((zp), \ | |
| const struct zpdesc *: (const struct page *)(zp), \ | |
| struct zpdesc *: (struct page *)(zp))) | |
| /** | |
| * zpdesc_folio - The folio allocated for a zpdesc | |
| * @zp: The zpdesc. | |
| * | |
| * Zpdescs are descriptors for zsmalloc memory. The memory itself is allocated | |
| * as folios that contain the zsmalloc objects, and zpdesc uses specific | |
| * fields in the first struct page of the folio - those fields are now accessed | |
| * by struct zpdesc. | |
| * | |
| * It is occasionally necessary convert to back to a folio in order to | |
| * communicate with the rest of the mm. Please use this helper function | |
| * instead of casting yourself, as the implementation may change in the future. | |
| */ | |
| #define zpdesc_folio(zp) (_Generic((zp), \ | |
| const struct zpdesc *: (const struct folio *)(zp), \ | |
| struct zpdesc *: (struct folio *)(zp))) | |
| /** | |
| * page_zpdesc - Converts from first struct page to zpdesc. | |
| * @p: The first (either head of compound or single) page of zpdesc. | |
| * | |
| * A temporary wrapper to convert struct page to struct zpdesc in situations | |
| * where we know the page is the compound head, or single order-0 page. | |
| * | |
| * Long-term ideally everything would work with struct zpdesc directly or go | |
| * through folio to struct zpdesc. | |
| * | |
| * Return: The zpdesc which contains this page | |
| */ | |
| #define page_zpdesc(p) (_Generic((p), \ | |
| const struct page *: (const struct zpdesc *)(p), \ | |
| struct page *: (struct zpdesc *)(p))) | |
| static inline void zpdesc_lock(struct zpdesc *zpdesc) | |
| { | |
| folio_lock(zpdesc_folio(zpdesc)); | |
| } | |
| static inline bool zpdesc_trylock(struct zpdesc *zpdesc) | |
| { | |
| return folio_trylock(zpdesc_folio(zpdesc)); | |
| } | |
| static inline void zpdesc_unlock(struct zpdesc *zpdesc) | |
| { | |
| folio_unlock(zpdesc_folio(zpdesc)); | |
| } | |
| static inline void zpdesc_wait_locked(struct zpdesc *zpdesc) | |
| { | |
| folio_wait_locked(zpdesc_folio(zpdesc)); | |
| } | |
| static inline void zpdesc_get(struct zpdesc *zpdesc) | |
| { | |
| folio_get(zpdesc_folio(zpdesc)); | |
| } | |
| static inline void zpdesc_put(struct zpdesc *zpdesc) | |
| { | |
| folio_put(zpdesc_folio(zpdesc)); | |
| } | |
| static inline void *kmap_local_zpdesc(struct zpdesc *zpdesc) | |
| { | |
| return kmap_local_page(zpdesc_page(zpdesc)); | |
| } | |
| static inline unsigned long zpdesc_pfn(struct zpdesc *zpdesc) | |
| { | |
| return page_to_pfn(zpdesc_page(zpdesc)); | |
| } | |
| static inline struct zpdesc *pfn_zpdesc(unsigned long pfn) | |
| { | |
| return page_zpdesc(pfn_to_page(pfn)); | |
| } | |
| static inline void __zpdesc_set_movable(struct zpdesc *zpdesc) | |
| { | |
| SetPageMovableOps(zpdesc_page(zpdesc)); | |
| } | |
| static inline void __zpdesc_set_zsmalloc(struct zpdesc *zpdesc) | |
| { | |
| __SetPageZsmalloc(zpdesc_page(zpdesc)); | |
| } | |
| static inline struct zone *zpdesc_zone(struct zpdesc *zpdesc) | |
| { | |
| return page_zone(zpdesc_page(zpdesc)); | |
| } | |
| static inline bool zpdesc_is_locked(struct zpdesc *zpdesc) | |
| { | |
| return folio_test_locked(zpdesc_folio(zpdesc)); | |
| } | |
| #endif |