Files
linux/drivers/iommu/generic_pt/pt_log2.h
Jason Gunthorpe 7c5b184db7 genpt: Generic Page Table base API
The generic API is intended to be separated from the implementation of
page table algorithms. It contains only accessors for walking and
manipulating the table and helpers that are useful for building an
implementation. Memory management is not in the generic API, but part of
the implementation.

Using a multi-compilation approach the implementation module would include
headers in this order:

  common.h
  defs_FMT.h
  pt_defs.h
  FMT.h
  pt_common.h
  IMPLEMENTATION.h

Where each compilation unit would have a combination of FMT and
IMPLEMENTATION to produce a per-format per-implementation module.

The API is designed so that the format headers have minimal logic, and
default implementations are provided if the format doesn't include one.

Generally formats provide their code via an inline function using the
pattern:

  static inline FMTpt_XX(..) {}
  #define pt_XX FMTpt_XX

The common code then enforces a function signature so that there is no
drift in function arguments, or accidental polymorphic functions (as has
been slightly troublesome in mm). Use of function-like #defines are
avoided in the format even though many of the functions are small enough.

Provide kdocs for the API surface.

This is enough to implement the 8 initial format variations with all of
their features:
 * Entries comprised of contiguous blocks of IO PTEs for larger page
   sizes (AMDv1, ARMv8)
 * Multi-level tables, up to 6 levels. Runtime selected top level
 * The size of the top table level can be selected at runtime (ARM's
   concatenated tables)
 * The number of levels in the table can optionally increase dynamically
   during map (AMDv1)
 * Optional leaf entries at any level
 * 32 bit/64 bit virtual and output addresses, using every bit
 * Sign extended addressing (x86)
 * Dirty tracking

A basic simple format takes about 200 lines to declare the require inline
functions.

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
Tested-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Tested-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
2025-11-05 09:07:04 +01:00

123 lines
3.0 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
*
* Helper macros for working with log2 values
*
*/
#ifndef __GENERIC_PT_LOG2_H
#define __GENERIC_PT_LOG2_H
#include <linux/bitops.h>
#include <linux/limits.h>
/* Compute a */
#define log2_to_int_t(type, a_lg2) ((type)(((type)1) << (a_lg2)))
static_assert(log2_to_int_t(unsigned int, 0) == 1);
/* Compute a - 1 (aka all low bits set) */
#define log2_to_max_int_t(type, a_lg2) ((type)(log2_to_int_t(type, a_lg2) - 1))
/* Compute a / b */
#define log2_div_t(type, a, b_lg2) ((type)(((type)a) >> (b_lg2)))
static_assert(log2_div_t(unsigned int, 4, 2) == 1);
/*
* Compute:
* a / c == b / c
* aka the high bits are equal
*/
#define log2_div_eq_t(type, a, b, c_lg2) \
(log2_div_t(type, (a) ^ (b), c_lg2) == 0)
static_assert(log2_div_eq_t(unsigned int, 1, 1, 2));
/* Compute a % b */
#define log2_mod_t(type, a, b_lg2) \
((type)(((type)a) & log2_to_max_int_t(type, b_lg2)))
static_assert(log2_mod_t(unsigned int, 1, 2) == 1);
/*
* Compute:
* a % b == b - 1
* aka the low bits are all 1s
*/
#define log2_mod_eq_max_t(type, a, b_lg2) \
(log2_mod_t(type, a, b_lg2) == log2_to_max_int_t(type, b_lg2))
static_assert(log2_mod_eq_max_t(unsigned int, 3, 2));
/*
* Return a value such that:
* a / b == ret / b
* ret % b == val
* aka set the low bits to val. val must be < b
*/
#define log2_set_mod_t(type, a, val, b_lg2) \
((((type)(a)) & (~log2_to_max_int_t(type, b_lg2))) | ((type)(val)))
static_assert(log2_set_mod_t(unsigned int, 3, 1, 2) == 1);
/* Return a value such that:
* a / b == ret / b
* ret % b == b - 1
* aka set the low bits to all 1s
*/
#define log2_set_mod_max_t(type, a, b_lg2) \
(((type)(a)) | log2_to_max_int_t(type, b_lg2))
static_assert(log2_set_mod_max_t(unsigned int, 2, 2) == 3);
/* Compute a * b */
#define log2_mul_t(type, a, b_lg2) ((type)(((type)a) << (b_lg2)))
static_assert(log2_mul_t(unsigned int, 2, 2) == 8);
#define _dispatch_sz(type, fn, a) \
(sizeof(type) == 4 ? fn##32((u32)a) : fn##64(a))
/*
* Return the highest value such that:
* fls_t(u32, 0) == 0
* fls_t(u3, 1) == 1
* a >= log2_to_int(ret - 1)
* aka find last set bit
*/
static inline unsigned int fls32(u32 a)
{
return fls(a);
}
#define fls_t(type, a) _dispatch_sz(type, fls, a)
/*
* Return the highest value such that:
* ffs_t(u32, 0) == UNDEFINED
* ffs_t(u32, 1) == 0
* log_mod(a, ret) == 0
* aka find first set bit
*/
static inline unsigned int __ffs32(u32 a)
{
return __ffs(a);
}
#define ffs_t(type, a) _dispatch_sz(type, __ffs, a)
/*
* Return the highest value such that:
* ffz_t(u32, U32_MAX) == UNDEFINED
* ffz_t(u32, 0) == 0
* ffz_t(u32, 1) == 1
* log_mod(a, ret) == log_to_max_int(ret)
* aka find first zero bit
*/
static inline unsigned int ffz32(u32 a)
{
return ffz(a);
}
static inline unsigned int ffz64(u64 a)
{
if (sizeof(u64) == sizeof(unsigned long))
return ffz(a);
if ((u32)a == U32_MAX)
return ffz32(a >> 32) + 32;
return ffz32(a);
}
#define ffz_t(type, a) _dispatch_sz(type, ffz, a)
#endif