iommupt: Fix the end_index calculation in __map_range_leaf()

Sashiko noticed a mismatch of units in this math: num_leaves is
actually the number of leaf *entries* (so a 16-item contiguous leaf
is one num_leaves), while index is in items. The mismatch in maths
causes __map_range_leaf() to exit early instead of efficiently
filling a larger range of contiguous PTEs.

The early exit is caught by the functions above and then
__map_range_leaf() is re-invoked, so there is no functional issue.

Correct the misuse of units by adjusting num_leaves with the leaf
size and avoid the performance cost of looping externally.

There are also some mismatched types for num_leaves; simplify
things to remove the duplicated calculations.

Fixes: d6c65b0fd6 ("iommupt: Avoid rewalking during map")
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
Reviewd-by: Pranjal Shrivastava <praan@google.com>
Tested-by: Josua Mayer <josua@solid-run.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
This commit is contained in:
Jason Gunthorpe
2026-05-12 13:46:17 -03:00
committed by Joerg Roedel
parent 8ef3f77c44
commit 58829512ad

View File

@@ -534,10 +534,12 @@ static int __map_range_leaf(struct pt_range *range, void *arg,
struct pt_state pts = pt_init(range, level, table);
struct pt_iommu_map_args *map = arg;
unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2;
unsigned int leaves_avail;
unsigned int start_index;
pt_oaddr_t oa = map->oa;
unsigned int num_leaves;
pt_vaddr_t num_leaves;
unsigned int orig_end;
unsigned int step_lg2;
pt_vaddr_t last_va;
unsigned int step;
bool need_contig;
@@ -546,21 +548,25 @@ static int __map_range_leaf(struct pt_range *range, void *arg,
PT_WARN_ON(map->leaf_level != level);
PT_WARN_ON(!pt_can_have_leaf(&pts));
step = log2_to_int_t(unsigned int,
leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts));
need_contig = leaf_pgsize_lg2 != pt_table_item_lg2sz(&pts);
step_lg2 = leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts);
step = log2_to_int_t(unsigned int, step_lg2);
need_contig = step_lg2 != 0;
_pt_iter_first(&pts);
start_index = pts.index;
orig_end = pts.end_index;
if (pts.index + map->num_leaves < pts.end_index) {
leaves_avail =
log2_div_t(unsigned int, pts.end_index - pts.index, step_lg2);
if (map->num_leaves <= leaves_avail) {
/* Need to stop in the middle of the table to change sizes */
pts.end_index = pts.index + map->num_leaves;
pts.end_index = pts.index + log2_mul(map->num_leaves, step_lg2);
num_leaves = 0;
} else {
num_leaves = map->num_leaves - (pts.end_index - pts.index);
num_leaves = map->num_leaves - leaves_avail;
}
PT_WARN_ON(
log2_mod_t(unsigned int, pts.end_index - pts.index, step_lg2));
do {
pts.type = pt_load_entry_raw(&pts);
if (pts.type != PT_ENTRY_EMPTY || need_contig) {