The NUMA interleave index was computed as two separate terms:
*ilx += vma->vm_pgoff >> order;
*ilx += (addr - vma->vm_start) >> (PAGE_SHIFT + order);
This has two problems:
1. When vm_start is not aligned to the folio size, the
subtraction before the shift lets low bits affect the
result via borrows.
2. For file-backed VMAs, shifting vm_pgoff and the VMA
offset independently loses carries between them, giving
wrong chunk indices when vm_pgoff is not aligned to order.
Combine into a single expression that adds vm_pgoff and
the page-granularity VMA offset first, then shifts once:
*ilx += (vma->vm_pgoff +
(addr >> PAGE_SHIFT) -
(vma->vm_start >> PAGE_SHIFT)) >> order;
For anonymous VMAs, vm_pgoff equals vm_start >> PAGE_SHIFT,
so the vm_pgoff and vm_start terms cancel and the result
reduces to addr >> (PAGE_SHIFT + order), same as before.
For file-backed VMAs, the sum vm_pgoff + (addr >> PAGE_SHIFT)
- (vm_start >> PAGE_SHIFT) gives the file page offset of addr.
Shifting by order gives the correct file chunk index.
Signed-off-by: Michael S. Tsirkin <[email protected]>
Assisted-by: Claude:claude-opus-4-6
Reviewed-by: Gregory Price <[email protected]>
---
mm/mempolicy.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 4e4421b22b59..d139b074a599 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2048,8 +2048,9 @@ struct mempolicy *get_vma_policy(struct vm_area_struct
*vma,
pol = get_task_policy(current);
if (pol->mode == MPOL_INTERLEAVE ||
pol->mode == MPOL_WEIGHTED_INTERLEAVE) {
- *ilx += vma->vm_pgoff >> order;
- *ilx += (addr - vma->vm_start) >> (PAGE_SHIFT + order);
+ *ilx += (vma->vm_pgoff +
+ (addr >> PAGE_SHIFT) -
+ (vma->vm_start >> PAGE_SHIFT)) >> order;
}
return pol;
}
--
MST