drgn.helpers.linux.mm: add simple PageFlag() getters

There are a bunch of page flag getters in the kernel like
PageUptodate(), PageLocked(), etc., that kernel developers are
accustomed to using. Most of them are simple bit tests. Let's add
helpers for all of those. These are generated from
include/linux/page-flags.h in the Linux kernel source tree as of Linux
v6.0-rc1.

More complicated getters that need to do more than a simple flag check
(e.g., PageCompound()) will need to be added manually.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2022-08-18 14:47:03 -07:00
parent c0ed1a3203
commit d14f751475
3 changed files with 582 additions and 6 deletions

View File

@ -36,18 +36,526 @@ __all__ = (
"virt_to_page",
"virt_to_pfn",
"virt_to_phys",
# Generated by scripts/generate_page_flag_getters.py.
"PageActive",
"PageChecked",
"PageDirty",
"PageDoubleMap",
"PageError",
"PageForeign",
"PageHWPoison",
"PageHasHWPoisoned",
"PageIdle",
"PageIsolated",
"PageLRU",
"PageLocked",
"PageMappedToDisk",
"PageMlocked",
"PageOwnerPriv1",
"PagePinned",
"PagePrivate",
"PagePrivate2",
"PageReadahead",
"PageReclaim",
"PageReferenced",
"PageReported",
"PageReserved",
"PageSavePinned",
"PageSkipKASanPoison",
"PageSlab",
"PageSlobFree",
"PageSwapBacked",
"PageUncached",
"PageUnevictable",
"PageUptodate",
"PageVmemmapSelfHosted",
"PageWaiters",
"PageWorkingset",
"PageWriteback",
"PageXenRemapped",
"PageYoung",
)
def for_each_page(prog: Program) -> Iterator[Object]:
def PageActive(page: Object) -> bool:
"""
Iterate over all pages in the system.
Return whether the ``PG_active`` flag is set on a page.
:return: Iterator of ``struct page *`` objects.
:param page: ``struct page *``
"""
vmemmap = prog["vmemmap"]
for i in range(prog["min_low_pfn"], prog["max_pfn"]):
yield vmemmap + i
try:
flag = page.prog_["PG_active"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageChecked(page: Object) -> bool:
"""
Return whether the ``PG_checked`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_checked"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageDirty(page: Object) -> bool:
"""
Return whether the ``PG_dirty`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_dirty"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageDoubleMap(page: Object) -> bool:
"""
Return whether the ``PG_double_map`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_double_map"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageError(page: Object) -> bool:
"""
Return whether the ``PG_error`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_error"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageForeign(page: Object) -> bool:
"""
Return whether the ``PG_foreign`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_foreign"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageHWPoison(page: Object) -> bool:
"""
Return whether the ``PG_hwpoison`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_hwpoison"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageHasHWPoisoned(page: Object) -> bool:
"""
Return whether the ``PG_has_hwpoisoned`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_has_hwpoisoned"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageIdle(page: Object) -> bool:
"""
Return whether the ``PG_idle`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_idle"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageIsolated(page: Object) -> bool:
"""
Return whether the ``PG_isolated`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_isolated"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageLRU(page: Object) -> bool:
"""
Return whether the ``PG_lru`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_lru"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageLocked(page: Object) -> bool:
"""
Return whether the ``PG_locked`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_locked"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageMappedToDisk(page: Object) -> bool:
"""
Return whether the ``PG_mappedtodisk`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_mappedtodisk"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageMlocked(page: Object) -> bool:
"""
Return whether the ``PG_mlocked`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_mlocked"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageOwnerPriv1(page: Object) -> bool:
"""
Return whether the ``PG_owner_priv_1`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_owner_priv_1"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PagePinned(page: Object) -> bool:
"""
Return whether the ``PG_pinned`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_pinned"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PagePrivate(page: Object) -> bool:
"""
Return whether the ``PG_private`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_private"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PagePrivate2(page: Object) -> bool:
"""
Return whether the ``PG_private_2`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_private_2"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageReadahead(page: Object) -> bool:
"""
Return whether the ``PG_readahead`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_readahead"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageReclaim(page: Object) -> bool:
"""
Return whether the ``PG_reclaim`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_reclaim"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageReferenced(page: Object) -> bool:
"""
Return whether the ``PG_referenced`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_referenced"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageReported(page: Object) -> bool:
"""
Return whether the ``PG_reported`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_reported"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageReserved(page: Object) -> bool:
"""
Return whether the ``PG_reserved`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_reserved"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageSavePinned(page: Object) -> bool:
"""
Return whether the ``PG_savepinned`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_savepinned"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageSkipKASanPoison(page: Object) -> bool:
"""
Return whether the ``PG_skip_kasan_poison`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_skip_kasan_poison"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageSlab(page: Object) -> bool:
"""
Return whether the ``PG_slab`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_slab"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageSlobFree(page: Object) -> bool:
"""
Return whether the ``PG_slob_free`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_slob_free"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageSwapBacked(page: Object) -> bool:
"""
Return whether the ``PG_swapbacked`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_swapbacked"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageUncached(page: Object) -> bool:
"""
Return whether the ``PG_uncached`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_uncached"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageUnevictable(page: Object) -> bool:
"""
Return whether the ``PG_unevictable`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_unevictable"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageUptodate(page: Object) -> bool:
"""
Return whether the ``PG_uptodate`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_uptodate"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageVmemmapSelfHosted(page: Object) -> bool:
"""
Return whether the ``PG_vmemmap_self_hosted`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_vmemmap_self_hosted"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageWaiters(page: Object) -> bool:
"""
Return whether the ``PG_waiters`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_waiters"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageWorkingset(page: Object) -> bool:
"""
Return whether the ``PG_workingset`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_workingset"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageWriteback(page: Object) -> bool:
"""
Return whether the ``PG_writeback`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_writeback"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageXenRemapped(page: Object) -> bool:
"""
Return whether the ``PG_xen_remapped`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_xen_remapped"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def PageYoung(page: Object) -> bool:
"""
Return whether the ``PG_young`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_young"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
def decode_page_flags(page: Object) -> str:
@ -66,6 +574,17 @@ def decode_page_flags(page: Object) -> str:
)
def for_each_page(prog: Program) -> Iterator[Object]:
"""
Iterate over all pages in the system.
:return: Iterator of ``struct page *`` objects.
"""
vmemmap = prog["vmemmap"]
for i in range(prog["min_low_pfn"], prog["max_pfn"]):
yield vmemmap + i
@overload
def PFN_PHYS(pfn: Object) -> Object:
"""

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: GPL-3.0-or-later
import argparse
import re
import sys
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate PageFlag() helpers from include/linux/page-flags.h"
)
args = parser.parse_args()
flags = {
# PageUptodate() isn't defined with PAGEFLAG because it needs
# additional memory barriers, but other than that it's the same.
"Uptodate": "uptodate",
}
for match in re.finditer(
r"\b(?:|__|TEST)PAGEFLAG\s*\(\s*(\w+)(?<!uname)\s*,\s*(\w+)\s*,\s*\w+\s*\)",
sys.stdin.read(),
):
if flags.setdefault(match.group(1), match.group(2)) != match.group(2):
sys.exit(f"{match.group('uname')} has multiple lowercase names?")
print(" # Generated by scripts/generate_page_flag_getters.py.")
for uname, lname in sorted(flags.items()):
print(f' "Page{uname}",')
print(")")
print()
for uname, lname in sorted(flags.items()):
print(
f'''
def Page{uname}(page: Object) -> bool:
"""
Return whether the ``PG_{lname}`` flag is set on a page.
:param page: ``struct page *``
"""
try:
flag = page.prog_["PG_{lname}"]
except KeyError:
return False
return bool(page.flags & (1 << flag))
'''
)

View File

@ -14,6 +14,8 @@ from drgn import FaultError
from drgn.helpers.linux.mm import (
PFN_PHYS,
PHYS_PFN,
PageSwapBacked,
PageWriteback,
access_process_vm,
access_remote_vm,
cmdline,
@ -70,6 +72,14 @@ class TestMm(LinuxKernelTestCase):
]
yield map, address, pfns
def test_page_flag_getters(self):
with self._pages() as (map, _, pfns):
page = pfn_to_page(self.prog, pfns[0])
# The page flag getters are generated, so just pick a positive case
# and a negative case to cover all of them.
self.assertTrue(PageSwapBacked(page))
self.assertFalse(PageWriteback(page))
@skip_unless_have_full_mm_support
def test_decode_page_flags(self):
with self._pages() as (map, _, pfns):