iommupt: Add basic support for SW bits in the page table

SW bits can be placed on items, including table entries, single OA's and
individual items within a contiguous OA. They are guaranteed to be ignored
by the HW. The API is very basic since the only use case so far is a
single bit.

Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
This commit is contained in:
Jason Gunthorpe 2025-10-23 15:22:30 -03:00 committed by Joerg Roedel
parent 36ae67b139
commit bcc64b57b4
2 changed files with 91 additions and 0 deletions

View File

@ -342,6 +342,35 @@ static inline struct pt_table_p *pt_table_ptr(const struct pt_state *pts)
return __va(pt_table_pa(pts));
}
/**
* pt_max_sw_bit() - Return the maximum software bit usable for any level and
* entry
* @common: Page table
*
* The swbit can be passed as bitnr to the other sw_bit functions.
*/
static inline unsigned int pt_max_sw_bit(struct pt_common *common);
/**
* pt_test_sw_bit_acquire() - Read a software bit in an item
* @pts: Entry to set
*
* Software bits are ignored by HW and can be used for any purpose by the
* software. This does a test bit and acquire operation.
*/
static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
unsigned int bitnr);
/**
* pt_set_sw_bit_release() - Set a software bit in an item
* @pts: Entry to set
*
* Software bits are ignored by HW and can be used for any purpose by the
* software. This does a set bit and release operation.
*/
static inline void pt_set_sw_bit_release(struct pt_state *pts,
unsigned int bitnr);
/**
* pt_load_entry() - Read from the location pts points at into the pts
* @pts: Table index to load

View File

@ -202,6 +202,68 @@ static inline void pt_clear_entries(struct pt_state *pts,
#define pt_clear_entries pt_clear_entries
#endif
/* If not supplied then SW bits are not supported */
#ifdef pt_sw_bit
static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
unsigned int bitnr)
{
/* Acquire, pairs with pt_set_sw_bit_release() */
smp_mb();
/* For a contiguous entry the sw bit is only stored in the first item. */
return pts->entry & pt_sw_bit(bitnr);
}
#define pt_test_sw_bit_acquire pt_test_sw_bit_acquire
static inline void pt_set_sw_bit_release(struct pt_state *pts,
unsigned int bitnr)
{
#if !IS_ENABLED(CONFIG_GENERIC_ATOMIC64)
if (PT_ITEM_WORD_SIZE == sizeof(u64)) {
u64 *entryp = pt_cur_table(pts, u64) + pts->index;
u64 old_entry = pts->entry;
u64 new_entry;
do {
new_entry = old_entry | pt_sw_bit(bitnr);
} while (!try_cmpxchg64_release(entryp, &old_entry, new_entry));
pts->entry = new_entry;
return;
}
#endif
if (PT_ITEM_WORD_SIZE == sizeof(u32)) {
u32 *entryp = pt_cur_table(pts, u32) + pts->index;
u32 old_entry = pts->entry;
u32 new_entry;
do {
new_entry = old_entry | pt_sw_bit(bitnr);
} while (!try_cmpxchg_release(entryp, &old_entry, new_entry));
pts->entry = new_entry;
} else
BUILD_BUG();
}
#define pt_set_sw_bit_release pt_set_sw_bit_release
#else
static inline unsigned int pt_max_sw_bit(struct pt_common *common)
{
return 0;
}
extern void __pt_no_sw_bit(void);
static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
unsigned int bitnr)
{
__pt_no_sw_bit();
return false;
}
static inline void pt_set_sw_bit_release(struct pt_state *pts,
unsigned int bitnr)
{
__pt_no_sw_bit();
}
#endif
/*
* Format can call in the pt_install_leaf_entry() to check the arguments are all
* aligned correctly.