Skip to content

Commit cd5781d

Browse files
davidhildenbrandhcahca
authored andcommitted
s390/vmemmap: remember unused sub-pmd ranges
With a memmap size of 56 bytes or 72 bytes per page, the memmap for a 256 MB section won't span full PMDs. As we populate single sections and depopulate single sections, the depopulation step would not be able to free all vmemmap pmds anymore. Do it similarly to x86, marking the unused memmap ranges in a special way (pad it with 0xFD). This allows us to add/remove sections, cleaning up all allocated vmemmap pages even if the memmap size is not multiple of 16 bytes per page. A 56 byte memmap can, for example, be created with !CONFIG_MEMCG and !CONFIG_SLUB. Cc: Vasily Gorbik <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Gerald Schaefer <[email protected]> Signed-off-by: David Hildenbrand <[email protected]> Message-Id: <[email protected]> Signed-off-by: Heiko Carstens <[email protected]>
1 parent f2057b4 commit cd5781d

File tree

1 file changed

+50
-1
lines changed

1 file changed

+50
-1
lines changed

arch/s390/mm/vmem.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,42 @@ static void vmem_pte_free(unsigned long *table)
7272
page_table_free(&init_mm, table);
7373
}
7474

75+
#define PAGE_UNUSED 0xFD
76+
77+
static void vmemmap_use_sub_pmd(unsigned long start, unsigned long end)
78+
{
79+
/*
80+
* As we expect to add in the same granularity as we remove, it's
81+
* sufficient to mark only some piece used to block the memmap page from
82+
* getting removed (just in case the memmap never gets initialized,
83+
* e.g., because the memory block never gets onlined).
84+
*/
85+
memset(__va(start), 0, sizeof(struct page));
86+
}
87+
88+
static void vmemmap_use_new_sub_pmd(unsigned long start, unsigned long end)
89+
{
90+
void *page = __va(ALIGN_DOWN(start, PMD_SIZE));
91+
92+
/* Could be our memmap page is filled with PAGE_UNUSED already ... */
93+
vmemmap_use_sub_pmd(start, end);
94+
95+
/* Mark the unused parts of the new memmap page PAGE_UNUSED. */
96+
if (!IS_ALIGNED(start, PMD_SIZE))
97+
memset(page, PAGE_UNUSED, start - __pa(page));
98+
if (!IS_ALIGNED(end, PMD_SIZE))
99+
memset(__va(end), PAGE_UNUSED, __pa(page) + PMD_SIZE - end);
100+
}
101+
102+
/* Returns true if the PMD is completely unused and can be freed. */
103+
static bool vmemmap_unuse_sub_pmd(unsigned long start, unsigned long end)
104+
{
105+
void *page = __va(ALIGN_DOWN(start, PMD_SIZE));
106+
107+
memset(__va(start), PAGE_UNUSED, end - start);
108+
return !memchr_inv(page, PAGE_UNUSED, PMD_SIZE);
109+
}
110+
75111
/* __ref: we'll only call vmemmap_alloc_block() via vmemmap_populate() */
76112
static int __ref modify_pte_table(pmd_t *pmd, unsigned long addr,
77113
unsigned long end, bool add, bool direct)
@@ -157,6 +193,11 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr,
157193
get_order(PMD_SIZE));
158194
pmd_clear(pmd);
159195
pages++;
196+
} else if (!direct &&
197+
vmemmap_unuse_sub_pmd(addr, next)) {
198+
vmem_free_pages(pmd_deref(*pmd),
199+
get_order(PMD_SIZE));
200+
pmd_clear(pmd);
160201
}
161202
continue;
162203
}
@@ -182,15 +223,23 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr,
182223
NUMA_NO_NODE);
183224
if (new_page) {
184225
pmd_val(*pmd) = __pa(new_page) | prot;
226+
if (!IS_ALIGNED(addr, PMD_SIZE) ||
227+
!IS_ALIGNED(next, PMD_SIZE)) {
228+
vmemmap_use_new_sub_pmd(addr,
229+
next);
230+
}
185231
continue;
186232
}
187233
}
188234
pte = vmem_pte_alloc();
189235
if (!pte)
190236
goto out;
191237
pmd_populate(&init_mm, pmd, pte);
192-
} else if (pmd_large(*pmd))
238+
} else if (pmd_large(*pmd)) {
239+
if (!direct)
240+
vmemmap_use_sub_pmd(addr, next);
193241
continue;
242+
}
194243

195244
ret = modify_pte_table(pmd, addr, next, add, direct);
196245
if (ret)

0 commit comments

Comments
 (0)