Skip to content

Commit 58c5d0d

Browse files
howlettakpm00
authored andcommitted
mm/mmap: regression fix for unmapped_area{_topdown}
The maple tree limits the gap returned to a window that specifically fits what was asked. This may not be optimal in the case of switching search directions or a gap that does not satisfy the requested space for other reasons. Fix the search by retrying the operation and limiting the search window in the rare occasion that a conflict occurs. Link: https://lkml.kernel.org/r/[email protected] Fixes: 3499a13 ("mm/mmap: use maple tree for unmapped_area{_topdown}") Signed-off-by: Liam R. Howlett <[email protected]> Reported-by: Rick Edgecombe <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 06e8fd9 commit 58c5d0d

File tree

1 file changed

+43
-5
lines changed

1 file changed

+43
-5
lines changed

mm/mmap.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,8 @@ static inline int accountable_mapping(struct file *file, vm_flags_t vm_flags)
15181518
*/
15191519
static unsigned long unmapped_area(struct vm_unmapped_area_info *info)
15201520
{
1521-
unsigned long length, gap;
1521+
unsigned long length, gap, low_limit;
1522+
struct vm_area_struct *tmp;
15221523

15231524
MA_STATE(mas, &current->mm->mm_mt, 0, 0);
15241525

@@ -1527,12 +1528,29 @@ static unsigned long unmapped_area(struct vm_unmapped_area_info *info)
15271528
if (length < info->length)
15281529
return -ENOMEM;
15291530

1530-
if (mas_empty_area(&mas, info->low_limit, info->high_limit - 1,
1531-
length))
1531+
low_limit = info->low_limit;
1532+
retry:
1533+
if (mas_empty_area(&mas, low_limit, info->high_limit - 1, length))
15321534
return -ENOMEM;
15331535

15341536
gap = mas.index;
15351537
gap += (info->align_offset - gap) & info->align_mask;
1538+
tmp = mas_next(&mas, ULONG_MAX);
1539+
if (tmp && (tmp->vm_flags & VM_GROWSDOWN)) { /* Avoid prev check if possible */
1540+
if (vm_start_gap(tmp) < gap + length - 1) {
1541+
low_limit = tmp->vm_end;
1542+
mas_reset(&mas);
1543+
goto retry;
1544+
}
1545+
} else {
1546+
tmp = mas_prev(&mas, 0);
1547+
if (tmp && vm_end_gap(tmp) > gap) {
1548+
low_limit = vm_end_gap(tmp);
1549+
mas_reset(&mas);
1550+
goto retry;
1551+
}
1552+
}
1553+
15361554
return gap;
15371555
}
15381556

@@ -1548,20 +1566,40 @@ static unsigned long unmapped_area(struct vm_unmapped_area_info *info)
15481566
*/
15491567
static unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info)
15501568
{
1551-
unsigned long length, gap;
1569+
unsigned long length, gap, high_limit, gap_end;
1570+
struct vm_area_struct *tmp;
15521571

15531572
MA_STATE(mas, &current->mm->mm_mt, 0, 0);
15541573
/* Adjust search length to account for worst case alignment overhead */
15551574
length = info->length + info->align_mask;
15561575
if (length < info->length)
15571576
return -ENOMEM;
15581577

1559-
if (mas_empty_area_rev(&mas, info->low_limit, info->high_limit - 1,
1578+
high_limit = info->high_limit;
1579+
retry:
1580+
if (mas_empty_area_rev(&mas, info->low_limit, high_limit - 1,
15601581
length))
15611582
return -ENOMEM;
15621583

15631584
gap = mas.last + 1 - info->length;
15641585
gap -= (gap - info->align_offset) & info->align_mask;
1586+
gap_end = mas.last;
1587+
tmp = mas_next(&mas, ULONG_MAX);
1588+
if (tmp && (tmp->vm_flags & VM_GROWSDOWN)) { /* Avoid prev check if possible */
1589+
if (vm_start_gap(tmp) <= gap_end) {
1590+
high_limit = vm_start_gap(tmp);
1591+
mas_reset(&mas);
1592+
goto retry;
1593+
}
1594+
} else {
1595+
tmp = mas_prev(&mas, 0);
1596+
if (tmp && vm_end_gap(tmp) > gap) {
1597+
high_limit = tmp->vm_start;
1598+
mas_reset(&mas);
1599+
goto retry;
1600+
}
1601+
}
1602+
15651603
return gap;
15661604
}
15671605

0 commit comments

Comments
 (0)