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 Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Gregory Price --- 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