Skip to content

Commit 4c88bb9

Browse files
Alexander Gordeevhcahca
authored andcommitted
s390/mm: check 2KB-fragment page on release
When CONFIG_DEBUG_VM is defined check that pending remove and tracking nibbles (bits 31-24 of the page refcount) are cleared. Should the earlier stages of the page lifespan have a race or logical error, such check could help in exposing the issue. Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com> Reviewed-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
1 parent 1194372 commit 4c88bb9

1 file changed

Lines changed: 30 additions & 11 deletions

File tree

arch/s390/mm/pgalloc.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -311,10 +311,23 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
311311
return table;
312312
}
313313

314+
static void page_table_release_check(struct page *page, void *table,
315+
unsigned int half, unsigned int mask)
316+
{
317+
char msg[128];
318+
319+
if (!IS_ENABLED(CONFIG_DEBUG_VM) || !mask)
320+
return;
321+
snprintf(msg, sizeof(msg),
322+
"Invalid pgtable %p release half 0x%02x mask 0x%02x",
323+
table, half, mask);
324+
dump_page(page, msg);
325+
}
326+
314327
void page_table_free(struct mm_struct *mm, unsigned long *table)
315328
{
329+
unsigned int mask, bit, half;
316330
struct page *page;
317-
unsigned int bit, mask;
318331

319332
page = virt_to_page(table);
320333
if (!mm_alloc_pgste(mm)) {
@@ -337,10 +350,14 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
337350
mask >>= 24;
338351
if (mask != 0x00U)
339352
return;
353+
half = 0x01U << bit;
340354
} else {
341-
atomic_xor_bits(&page->_refcount, 0x03U << 24);
355+
half = 0x03U;
356+
mask = atomic_xor_bits(&page->_refcount, 0x03U << 24);
357+
mask >>= 24;
342358
}
343359

360+
page_table_release_check(page, table, half, mask);
344361
pgtable_pte_page_dtor(page);
345362
__free_page(page);
346363
}
@@ -380,28 +397,30 @@ void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table,
380397

381398
void __tlb_remove_table(void *_table)
382399
{
383-
unsigned int mask = (unsigned long) _table & 0x03U;
400+
unsigned int mask = (unsigned long) _table & 0x03U, half = mask;
384401
void *table = (void *)((unsigned long) _table ^ mask);
385402
struct page *page = virt_to_page(table);
386403

387-
switch (mask) {
404+
switch (half) {
388405
case 0x00U: /* pmd, pud, or p4d */
389406
free_pages((unsigned long) table, 2);
390-
break;
407+
return;
391408
case 0x01U: /* lower 2K of a 4K page table */
392409
case 0x02U: /* higher 2K of a 4K page table */
393410
mask = atomic_xor_bits(&page->_refcount, mask << (4 + 24));
394411
mask >>= 24;
395412
if (mask != 0x00U)
396-
break;
397-
fallthrough;
413+
return;
414+
break;
398415
case 0x03U: /* 4K page table with pgstes */
399-
if (mask & 0x03U)
400-
atomic_xor_bits(&page->_refcount, 0x03U << 24);
401-
pgtable_pte_page_dtor(page);
402-
__free_page(page);
416+
mask = atomic_xor_bits(&page->_refcount, 0x03U << 24);
417+
mask >>= 24;
403418
break;
404419
}
420+
421+
page_table_release_check(page, table, half, mask);
422+
pgtable_pte_page_dtor(page);
423+
__free_page(page);
405424
}
406425

407426
/*

0 commit comments

Comments
 (0)