1*2f060190SKuan-Ying Lee# SPDX-License-Identifier: GPL-2.0 2*2f060190SKuan-Ying Lee# 3*2f060190SKuan-Ying Lee# Copyright (c) 2023 MediaTek Inc. 4*2f060190SKuan-Ying Lee# 5*2f060190SKuan-Ying Lee# Authors: 6*2f060190SKuan-Ying Lee# Kuan-Ying Lee <[email protected]> 7*2f060190SKuan-Ying Lee# 8*2f060190SKuan-Ying Lee 9*2f060190SKuan-Ying Leeimport gdb 10*2f060190SKuan-Ying Leefrom linux import utils, stackdepot, constants, mm 11*2f060190SKuan-Ying Lee 12*2f060190SKuan-Ying Leeif constants.LX_CONFIG_PAGE_OWNER: 13*2f060190SKuan-Ying Lee page_ext_t = utils.CachedType('struct page_ext') 14*2f060190SKuan-Ying Lee page_owner_t = utils.CachedType('struct page_owner') 15*2f060190SKuan-Ying Lee 16*2f060190SKuan-Ying Lee PAGE_OWNER_STACK_DEPTH = 16 17*2f060190SKuan-Ying Lee PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER 18*2f060190SKuan-Ying Lee PAGE_EXT_INVALID = 0x1 19*2f060190SKuan-Ying Lee PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED 20*2f060190SKuan-Ying Lee 21*2f060190SKuan-Ying Leedef help(): 22*2f060190SKuan-Ying Lee t = """Usage: lx-dump-page-owner [Option] 23*2f060190SKuan-Ying Lee Option: 24*2f060190SKuan-Ying Lee --pfn [Decimal pfn] 25*2f060190SKuan-Ying Lee Example: 26*2f060190SKuan-Ying Lee lx-dump-page-owner --pfn 655360\n""" 27*2f060190SKuan-Ying Lee gdb.write("Unrecognized command\n") 28*2f060190SKuan-Ying Lee raise gdb.GdbError(t) 29*2f060190SKuan-Ying Lee 30*2f060190SKuan-Ying Leeclass DumpPageOwner(gdb.Command): 31*2f060190SKuan-Ying Lee """Dump page owner""" 32*2f060190SKuan-Ying Lee 33*2f060190SKuan-Ying Lee min_pfn = None 34*2f060190SKuan-Ying Lee max_pfn = None 35*2f060190SKuan-Ying Lee p_ops = None 36*2f060190SKuan-Ying Lee migrate_reason_names = None 37*2f060190SKuan-Ying Lee 38*2f060190SKuan-Ying Lee def __init__(self): 39*2f060190SKuan-Ying Lee super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT) 40*2f060190SKuan-Ying Lee 41*2f060190SKuan-Ying Lee def invoke(self, args, from_tty): 42*2f060190SKuan-Ying Lee if not constants.LX_CONFIG_PAGE_OWNER: 43*2f060190SKuan-Ying Lee raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable') 44*2f060190SKuan-Ying Lee 45*2f060190SKuan-Ying Lee page_owner_inited = gdb.parse_and_eval('page_owner_inited') 46*2f060190SKuan-Ying Lee if page_owner_inited['key']['enabled']['counter'] != 0x1: 47*2f060190SKuan-Ying Lee raise gdb.GdbError('page_owner_inited is not enabled') 48*2f060190SKuan-Ying Lee 49*2f060190SKuan-Ying Lee self.p_ops = mm.page_ops().ops 50*2f060190SKuan-Ying Lee self.get_page_owner_info() 51*2f060190SKuan-Ying Lee argv = gdb.string_to_argv(args) 52*2f060190SKuan-Ying Lee if len(argv) == 0: 53*2f060190SKuan-Ying Lee self.read_page_owner() 54*2f060190SKuan-Ying Lee elif len(argv) == 2: 55*2f060190SKuan-Ying Lee if argv[0] == "--pfn": 56*2f060190SKuan-Ying Lee pfn = int(argv[1]) 57*2f060190SKuan-Ying Lee self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn)) 58*2f060190SKuan-Ying Lee else: 59*2f060190SKuan-Ying Lee help() 60*2f060190SKuan-Ying Lee else: 61*2f060190SKuan-Ying Lee help() 62*2f060190SKuan-Ying Lee 63*2f060190SKuan-Ying Lee def get_page_owner_info(self): 64*2f060190SKuan-Ying Lee self.min_pfn = int(gdb.parse_and_eval("min_low_pfn")) 65*2f060190SKuan-Ying Lee self.max_pfn = int(gdb.parse_and_eval("max_pfn")) 66*2f060190SKuan-Ying Lee self.page_ext_size = int(gdb.parse_and_eval("page_ext_size")) 67*2f060190SKuan-Ying Lee self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names') 68*2f060190SKuan-Ying Lee 69*2f060190SKuan-Ying Lee def page_ext_invalid(self, page_ext): 70*2f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 71*2f060190SKuan-Ying Lee return True 72*2f060190SKuan-Ying Lee if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID: 73*2f060190SKuan-Ying Lee return True 74*2f060190SKuan-Ying Lee return False 75*2f060190SKuan-Ying Lee 76*2f060190SKuan-Ying Lee def get_entry(self, base, index): 77*2f060190SKuan-Ying Lee return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer()) 78*2f060190SKuan-Ying Lee 79*2f060190SKuan-Ying Lee def lookup_page_ext(self, page): 80*2f060190SKuan-Ying Lee pfn = self.p_ops.page_to_pfn(page) 81*2f060190SKuan-Ying Lee section = self.p_ops.pfn_to_section(pfn) 82*2f060190SKuan-Ying Lee page_ext = section["page_ext"] 83*2f060190SKuan-Ying Lee if self.page_ext_invalid(page_ext): 84*2f060190SKuan-Ying Lee return gdb.Value(0) 85*2f060190SKuan-Ying Lee return self.get_entry(page_ext, pfn) 86*2f060190SKuan-Ying Lee 87*2f060190SKuan-Ying Lee def page_ext_get(self, page): 88*2f060190SKuan-Ying Lee page_ext = self.lookup_page_ext(page) 89*2f060190SKuan-Ying Lee if page_ext != gdb.Value(0): 90*2f060190SKuan-Ying Lee return page_ext 91*2f060190SKuan-Ying Lee else: 92*2f060190SKuan-Ying Lee return gdb.Value(0) 93*2f060190SKuan-Ying Lee 94*2f060190SKuan-Ying Lee def get_page_owner(self, page_ext): 95*2f060190SKuan-Ying Lee addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type()) 96*2f060190SKuan-Ying Lee return addr.cast(page_owner_t.get_type().pointer()) 97*2f060190SKuan-Ying Lee 98*2f060190SKuan-Ying Lee def read_page_owner_by_addr(self, struct_page_addr): 99*2f060190SKuan-Ying Lee page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) 100*2f060190SKuan-Ying Lee pfn = self.p_ops.page_to_pfn(page) 101*2f060190SKuan-Ying Lee 102*2f060190SKuan-Ying Lee if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)): 103*2f060190SKuan-Ying Lee gdb.write("pfn is invalid\n") 104*2f060190SKuan-Ying Lee return 105*2f060190SKuan-Ying Lee 106*2f060190SKuan-Ying Lee page = self.p_ops.pfn_to_page(pfn) 107*2f060190SKuan-Ying Lee page_ext = self.page_ext_get(page) 108*2f060190SKuan-Ying Lee 109*2f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 110*2f060190SKuan-Ying Lee gdb.write("page_ext is null\n") 111*2f060190SKuan-Ying Lee return 112*2f060190SKuan-Ying Lee 113*2f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 114*2f060190SKuan-Ying Lee gdb.write("page_owner flag is invalid\n") 115*2f060190SKuan-Ying Lee raise gdb.GdbError('page_owner info is not present (never set?)\n') 116*2f060190SKuan-Ying Lee 117*2f060190SKuan-Ying Lee if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address): 118*2f060190SKuan-Ying Lee gdb.write('page_owner tracks the page as allocated\n') 119*2f060190SKuan-Ying Lee else: 120*2f060190SKuan-Ying Lee gdb.write('page_owner tracks the page as freed\n') 121*2f060190SKuan-Ying Lee 122*2f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 123*2f060190SKuan-Ying Lee gdb.write("page_owner is not allocated\n") 124*2f060190SKuan-Ying Lee 125*2f060190SKuan-Ying Lee try: 126*2f060190SKuan-Ying Lee page_owner = self.get_page_owner(page_ext) 127*2f060190SKuan-Ying Lee gdb.write("Page last allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\ 128*2f060190SKuan-Ying Lee (page_owner["order"], page_owner["gfp_mask"],\ 129*2f060190SKuan-Ying Lee page_owner["pid"], page_owner["tgid"], page_owner["comm"],\ 130*2f060190SKuan-Ying Lee page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 131*2f060190SKuan-Ying Lee gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 132*2f060190SKuan-Ying Lee if page_owner["handle"] == 0: 133*2f060190SKuan-Ying Lee gdb.write('page_owner allocation stack trace missing\n') 134*2f060190SKuan-Ying Lee else: 135*2f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["handle"]) 136*2f060190SKuan-Ying Lee 137*2f060190SKuan-Ying Lee if page_owner["free_handle"] == 0: 138*2f060190SKuan-Ying Lee gdb.write('page_owner free stack trace missing\n') 139*2f060190SKuan-Ying Lee else: 140*2f060190SKuan-Ying Lee gdb.write('page last free stack trace:\n') 141*2f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["free_handle"]) 142*2f060190SKuan-Ying Lee if page_owner['last_migrate_reason'] != -1: 143*2f060190SKuan-Ying Lee gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']]) 144*2f060190SKuan-Ying Lee except: 145*2f060190SKuan-Ying Lee gdb.write("\n") 146*2f060190SKuan-Ying Lee 147*2f060190SKuan-Ying Lee def read_page_owner(self): 148*2f060190SKuan-Ying Lee pfn = self.min_pfn 149*2f060190SKuan-Ying Lee 150*2f060190SKuan-Ying Lee # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area 151*2f060190SKuan-Ying Lee while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0: 152*2f060190SKuan-Ying Lee pfn += 1 153*2f060190SKuan-Ying Lee 154*2f060190SKuan-Ying Lee while pfn < self.max_pfn: 155*2f060190SKuan-Ying Lee # 156*2f060190SKuan-Ying Lee # If the new page is in a new MAX_ORDER_NR_PAGES area, 157*2f060190SKuan-Ying Lee # validate the area as existing, skip it if not 158*2f060190SKuan-Ying Lee # 159*2f060190SKuan-Ying Lee if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)): 160*2f060190SKuan-Ying Lee pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1) 161*2f060190SKuan-Ying Lee continue; 162*2f060190SKuan-Ying Lee 163*2f060190SKuan-Ying Lee page = self.p_ops.pfn_to_page(pfn) 164*2f060190SKuan-Ying Lee page_ext = self.page_ext_get(page) 165*2f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 166*2f060190SKuan-Ying Lee pfn += 1 167*2f060190SKuan-Ying Lee continue 168*2f060190SKuan-Ying Lee 169*2f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 170*2f060190SKuan-Ying Lee pfn += 1 171*2f060190SKuan-Ying Lee continue 172*2f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 173*2f060190SKuan-Ying Lee pfn += 1 174*2f060190SKuan-Ying Lee continue 175*2f060190SKuan-Ying Lee 176*2f060190SKuan-Ying Lee try: 177*2f060190SKuan-Ying Lee page_owner = self.get_page_owner(page_ext) 178*2f060190SKuan-Ying Lee gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\ 179*2f060190SKuan-Ying Lee (page_owner["order"], page_owner["gfp_mask"],\ 180*2f060190SKuan-Ying Lee page_owner["pid"], page_owner["tgid"], page_owner["comm"],\ 181*2f060190SKuan-Ying Lee page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 182*2f060190SKuan-Ying Lee gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 183*2f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["handle"]) 184*2f060190SKuan-Ying Lee pfn += (1 << page_owner["order"]) 185*2f060190SKuan-Ying Lee continue 186*2f060190SKuan-Ying Lee except: 187*2f060190SKuan-Ying Lee gdb.write("\n") 188*2f060190SKuan-Ying Lee pfn += 1 189*2f060190SKuan-Ying Lee 190*2f060190SKuan-Ying LeeDumpPageOwner() 191