18578e0c0SKairui Song /* SPDX-License-Identifier: GPL-2.0 */ 28578e0c0SKairui Song #ifndef _MM_SWAP_TABLE_H 38578e0c0SKairui Song #define _MM_SWAP_TABLE_H 48578e0c0SKairui Song 5*07adc4cfSKairui Song #include <linux/rcupdate.h> 6*07adc4cfSKairui Song #include <linux/atomic.h> 78578e0c0SKairui Song #include "swap.h" 88578e0c0SKairui Song 9*07adc4cfSKairui Song /* A typical flat array in each cluster as swap table */ 10*07adc4cfSKairui Song struct swap_table { 11*07adc4cfSKairui Song atomic_long_t entries[SWAPFILE_CLUSTER]; 12*07adc4cfSKairui Song }; 13*07adc4cfSKairui Song 148578e0c0SKairui Song /* 158578e0c0SKairui Song * A swap table entry represents the status of a swap slot on a swap 168578e0c0SKairui Song * (physical or virtual) device. The swap table in each cluster is a 178578e0c0SKairui Song * 1:1 map of the swap slots in this cluster. 188578e0c0SKairui Song * 198578e0c0SKairui Song * Each swap table entry could be a pointer (folio), a XA_VALUE 208578e0c0SKairui Song * (shadow), or NULL. 218578e0c0SKairui Song */ 228578e0c0SKairui Song 238578e0c0SKairui Song /* 248578e0c0SKairui Song * Helpers for casting one type of info into a swap table entry. 258578e0c0SKairui Song */ 268578e0c0SKairui Song static inline unsigned long null_to_swp_tb(void) 278578e0c0SKairui Song { 288578e0c0SKairui Song BUILD_BUG_ON(sizeof(unsigned long) != sizeof(atomic_long_t)); 298578e0c0SKairui Song return 0; 308578e0c0SKairui Song } 318578e0c0SKairui Song 328578e0c0SKairui Song static inline unsigned long folio_to_swp_tb(struct folio *folio) 338578e0c0SKairui Song { 348578e0c0SKairui Song BUILD_BUG_ON(sizeof(unsigned long) != sizeof(void *)); 358578e0c0SKairui Song return (unsigned long)folio; 368578e0c0SKairui Song } 378578e0c0SKairui Song 388578e0c0SKairui Song static inline unsigned long shadow_swp_to_tb(void *shadow) 398578e0c0SKairui Song { 408578e0c0SKairui Song BUILD_BUG_ON((BITS_PER_XA_VALUE + 1) != 418578e0c0SKairui Song BITS_PER_BYTE * sizeof(unsigned long)); 428578e0c0SKairui Song VM_WARN_ON_ONCE(shadow && !xa_is_value(shadow)); 438578e0c0SKairui Song return (unsigned long)shadow; 448578e0c0SKairui Song } 458578e0c0SKairui Song 468578e0c0SKairui Song /* 478578e0c0SKairui Song * Helpers for swap table entry type checking. 488578e0c0SKairui Song */ 498578e0c0SKairui Song static inline bool swp_tb_is_null(unsigned long swp_tb) 508578e0c0SKairui Song { 518578e0c0SKairui Song return !swp_tb; 528578e0c0SKairui Song } 538578e0c0SKairui Song 548578e0c0SKairui Song static inline bool swp_tb_is_folio(unsigned long swp_tb) 558578e0c0SKairui Song { 568578e0c0SKairui Song return !xa_is_value((void *)swp_tb) && !swp_tb_is_null(swp_tb); 578578e0c0SKairui Song } 588578e0c0SKairui Song 598578e0c0SKairui Song static inline bool swp_tb_is_shadow(unsigned long swp_tb) 608578e0c0SKairui Song { 618578e0c0SKairui Song return xa_is_value((void *)swp_tb); 628578e0c0SKairui Song } 638578e0c0SKairui Song 648578e0c0SKairui Song /* 658578e0c0SKairui Song * Helpers for retrieving info from swap table. 668578e0c0SKairui Song */ 678578e0c0SKairui Song static inline struct folio *swp_tb_to_folio(unsigned long swp_tb) 688578e0c0SKairui Song { 698578e0c0SKairui Song VM_WARN_ON(!swp_tb_is_folio(swp_tb)); 708578e0c0SKairui Song return (void *)swp_tb; 718578e0c0SKairui Song } 728578e0c0SKairui Song 738578e0c0SKairui Song static inline void *swp_tb_to_shadow(unsigned long swp_tb) 748578e0c0SKairui Song { 758578e0c0SKairui Song VM_WARN_ON(!swp_tb_is_shadow(swp_tb)); 768578e0c0SKairui Song return (void *)swp_tb; 778578e0c0SKairui Song } 788578e0c0SKairui Song 798578e0c0SKairui Song /* 808578e0c0SKairui Song * Helpers for accessing or modifying the swap table of a cluster, 818578e0c0SKairui Song * the swap cluster must be locked. 828578e0c0SKairui Song */ 838578e0c0SKairui Song static inline void __swap_table_set(struct swap_cluster_info *ci, 848578e0c0SKairui Song unsigned int off, unsigned long swp_tb) 858578e0c0SKairui Song { 86*07adc4cfSKairui Song atomic_long_t *table = rcu_dereference_protected(ci->table, true); 87*07adc4cfSKairui Song 88*07adc4cfSKairui Song lockdep_assert_held(&ci->lock); 898578e0c0SKairui Song VM_WARN_ON_ONCE(off >= SWAPFILE_CLUSTER); 90*07adc4cfSKairui Song atomic_long_set(&table[off], swp_tb); 918578e0c0SKairui Song } 928578e0c0SKairui Song 938578e0c0SKairui Song static inline unsigned long __swap_table_xchg(struct swap_cluster_info *ci, 948578e0c0SKairui Song unsigned int off, unsigned long swp_tb) 958578e0c0SKairui Song { 96*07adc4cfSKairui Song atomic_long_t *table = rcu_dereference_protected(ci->table, true); 97*07adc4cfSKairui Song 98*07adc4cfSKairui Song lockdep_assert_held(&ci->lock); 998578e0c0SKairui Song VM_WARN_ON_ONCE(off >= SWAPFILE_CLUSTER); 1008578e0c0SKairui Song /* Ordering is guaranteed by cluster lock, relax */ 101*07adc4cfSKairui Song return atomic_long_xchg_relaxed(&table[off], swp_tb); 1028578e0c0SKairui Song } 1038578e0c0SKairui Song 1048578e0c0SKairui Song static inline unsigned long __swap_table_get(struct swap_cluster_info *ci, 1058578e0c0SKairui Song unsigned int off) 1068578e0c0SKairui Song { 107*07adc4cfSKairui Song atomic_long_t *table; 108*07adc4cfSKairui Song 1098578e0c0SKairui Song VM_WARN_ON_ONCE(off >= SWAPFILE_CLUSTER); 110*07adc4cfSKairui Song table = rcu_dereference_check(ci->table, lockdep_is_held(&ci->lock)); 111*07adc4cfSKairui Song 112*07adc4cfSKairui Song return atomic_long_read(&table[off]); 113*07adc4cfSKairui Song } 114*07adc4cfSKairui Song 115*07adc4cfSKairui Song static inline unsigned long swap_table_get(struct swap_cluster_info *ci, 116*07adc4cfSKairui Song unsigned int off) 117*07adc4cfSKairui Song { 118*07adc4cfSKairui Song atomic_long_t *table; 119*07adc4cfSKairui Song unsigned long swp_tb; 120*07adc4cfSKairui Song 121*07adc4cfSKairui Song rcu_read_lock(); 122*07adc4cfSKairui Song table = rcu_dereference(ci->table); 123*07adc4cfSKairui Song swp_tb = table ? atomic_long_read(&table[off]) : null_to_swp_tb(); 124*07adc4cfSKairui Song rcu_read_unlock(); 125*07adc4cfSKairui Song 126*07adc4cfSKairui Song return swp_tb; 1278578e0c0SKairui Song } 1288578e0c0SKairui Song #endif 129