"""
Constants and routines for manipulating kernel page struct.
Copyright 2014-2017 Cray Inc.  All Rights Reserved
"""

from pykdump.API import *

import crashlib.cid
import crashlib.cid.machdep_table
import crashlib.cid.page_flags
import crashlib.cid.phys_mem_map

# --------------------------------------------------------------------------

page_struct_size = getSizeOf('struct page')

# --------------------------------------------------------------------------

# Create a function for determining whether a page is controlled by the
# buddy allocator.  Note that earlier kernels (< 3.0) have a page flag, while
# later kernels use the _mapcount field.

if hasattr(crashlib.cid.pgflags, 'PG_buddy'):
    def is_buddy_page(page):
        return page.flags & crashlib.cid.pgflags.PG_buddy.mask;
else:
    def is_buddy_page(page):
        # Early implementations used -2, later use -128
        return page._mapcount.counter == -128 or page._mapcount.counter == -2

if hasattr(crashlib.cid.pgflags, 'PG_compound'):
    def is_compound_page_head(page):
        return (page.flags & (crashlib.cid.pgflags.PG_reclaim.mask |
                              crashlib.cid.pgflags.PG_compound.mask)
               ) == crashlib.cid.pgflags.PG_compound

    def is_compound_page_tail(page):
        return (page.flags & (crashlib.cid.pgflags.PG_reclaim.mask |
                              crashlib.cid.pgflags.PG_compound.mask)
               ) == (crashlib.cid.pgflags.PG_reclaim.mask |
                     crashlib.cid.pgflags.PG_compound.mask)

    def is_compound_page(page):
        return page.flags & crashlib.cid.pgflags.PG_compound.mask

elif hasattr(crashlib.cid.pgflags, 'PG_tail'):
    # PG_head and PG_tail defined
    def is_compound_page_head(page):
        return page.flags & (crashlib.cid.pgflags.PG_head.mask)

    def is_compound_page_tail(page):
        return page.flags & (crashlib.cid.pgflags.PG_tail.mask)

    def is_compound_page(page):
        return is_compound_page_head(page) or is_compound_page_tail(page)

else:
    # Only PG_head is defined
    def is_compound_page_head(page):
        return page.flags & (crashlib.cid.pgflags.PG_head.mask)

    def is_compound_page_tail(page):
        return page.compound_head & 1

    def is_compound_page(page):
        return is_compound_page_head(page) or is_compound_page_tail(page)

# --------------------------------------------------------------------------

# Find the page order of a buddy page

def buddy_order(page):
    """Retrieve the order of a page in the buddy allocator"""
    return page.private

# --------------------------------------------------------------------------

# Create a function to determine the page order of a compound page

if member_offset('struct page', 'compound_order') > -1:
    def compound_order(page):
        """Retrieve the page order for a compound page."""
        # A compound page is a series of contiguous pages, thus there are
        # at least two page structs.  The second page struct (first tail page)
        # contains the page order; the head page uses the space for a
        # different purpose.
        return page[1].compound_order

else:
    def compound_order(page):
        """Retrieve the page order for a compound page."""
        # A compound page is a series of contiguous pages, thus there are
        # at least two page structs.  The second page struct (first tail page)
        # contains the page order stored in the lru.prev field; the head page
        # uses the space for a different purpose.
        return page[1].lru.prev

# --------------------------------------------------------------------------

def pfn(page):
    """Returns the pfn for the supplied page struct or page struct address."""
    vmemmap_vaddr = crashlib.cid.mdtbl.vmemmap_vaddr
    return (page - vmemmap_vaddr) / page_struct_size

# --------------------------------------------------------------------------
