summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Crowell <dcrowell@us.ibm.com>2011-08-08 09:36:04 -0500
committerDaniel M. Crowell <dcrowell@us.ibm.com>2011-09-06 12:18:36 -0500
commitf093b902e49a0ee46d232cd196ec48f88f801735 (patch)
treec6d540acf79d21e21c936a381968e4158fee29c7 /src
parentdcf7c7f2c3be17df41e3cc483dbec6f085b05353 (diff)
downloadtalos-hostboot-f093b902e49a0ee46d232cd196ec48f88f801735.tar.gz
talos-hostboot-f093b902e49a0ee46d232cd196ec48f88f801735.zip
Adding a basic interface and implementation to the Segment/Block
path to update the LRU statistics when the PageTableManager code clears the reference bit. This is not meant to be a complete implementation (different Task is open for that). This is Task 3400. Change-Id: If67efd16ead6f68a74f5f5a698013c1b852864d9 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/231 Tested-by: Jenkins Server Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r--src/include/kernel/basesegment.H12
-rw-r--r--src/include/kernel/block.H16
-rw-r--r--src/include/kernel/ptmgr.H64
-rw-r--r--src/include/kernel/segment.H14
-rw-r--r--src/include/kernel/segmentmgr.H13
-rw-r--r--src/include/kernel/spte.H29
-rw-r--r--src/include/util/align.H6
-rw-r--r--src/kernel/basesegment.C6
-rw-r--r--src/kernel/block.C43
-rw-r--r--src/kernel/ptmgr.C175
-rw-r--r--src/kernel/segmentmgr.C21
-rw-r--r--src/usr/testcore/kernel/ptmgrtest.H32
12 files changed, 377 insertions, 54 deletions
diff --git a/src/include/kernel/basesegment.H b/src/include/kernel/basesegment.H
index a661ae5ce..72869d535 100644
--- a/src/include/kernel/basesegment.H
+++ b/src/include/kernel/basesegment.H
@@ -63,7 +63,17 @@ class BaseSegment : public Segment
*
* Calls block chain to deal with page fault.
*/
- bool handlePageFault(task_t* i_task, uint64_t i_addr);
+ virtual bool handlePageFault(task_t* i_task, uint64_t i_addr);
+
+ /**
+ * @brief Implementation of the pure-virtual function from Segment.
+ * Update LRU statistics on the block that owns the address *
+ *
+ * @param[in] i_vaddr - Virtual Address of page
+ * @param[in] i_stats - Usage statistics
+ */
+ virtual void updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats );
/**
* @brief Allocates a block of virtual memory of the given size
diff --git a/src/include/kernel/block.H b/src/include/kernel/block.H
index 94daf0b3a..2421f97f9 100644
--- a/src/include/kernel/block.H
+++ b/src/include/kernel/block.H
@@ -30,6 +30,8 @@
#include <kernel/task.H>
#include <kernel/vmmmgr.H>
#include <kernel/blockmsghdlr.H>
+#include <kernel/msghandler.H>
+#include <kernel/ptmgr.H>
class ShadowPTE;
class Segment;
@@ -140,6 +142,20 @@ class Block
*/
uint64_t findPhysicalAddress(uint64_t i_vaddr) const;
+ /**
+ * @brief Responsible for updating usage counts within the block [chain].
+ *
+ * @param[in] i_vaddr - Abbreviated Virtual Address of page
+ * (bottom 23 bits are zero)
+ * @param[in] i_stats - Usage statistics
+ *
+ * If the address is not within this block or a matching page cannot be
+ * found, the block will attempt to make calls down the block-chain
+ * if it exists.
+ */
+ void updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats );
+
friend class Segment;
friend class BaseSegment;
friend class StackSegment;
diff --git a/src/include/kernel/ptmgr.H b/src/include/kernel/ptmgr.H
index bab4ba3b4..c7505bc1f 100644
--- a/src/include/kernel/ptmgr.H
+++ b/src/include/kernel/ptmgr.H
@@ -41,14 +41,14 @@ class PageTableManager
*/
enum
{
- PTE_UNKNOWN = 0x0000000000000000, /**< Entry wasn't found */
- PTE_PRESENT = 0x0000000000000001, /**< Entry is present in table */
- PTE_VALID = 0x0000000000000002, /**< Entry is valid */
- PTE_READ_ONLY = 0x0000000000000004, /**< Read-Only */
- PTE_EXECUTE = 0x0000000000000008, /**< Execute permission */
- PTE_CACHE_INHIBITED = 0x0000000000000010, /**< Cache-Inhibited Access */
- PTE_MODIFIED = 0x0000000000000020, /**< Page has been modified */
- PTE_ACCESSED = 0x0000000000000040, /**< Page has been accessed */
+ PTE_UNKNOWN = 0x0000000000000000, /**< Entry wasn't found */
+ PTE_PRESENT = 0x0000000000000001, /**< Entry is present in table */
+ PTE_VALID = 0x0000000000000002, /**< Entry is valid */
+ PTE_READ_ONLY = 0x0000000000000004, /**< Read-Only */
+ PTE_EXECUTE = 0x0000000000000008, /**< Execute permission */
+ PTE_CACHE_INHIBITED = 0x0000000000000010, /**< Cache-Inhibited Access */
+ PTE_MODIFIED = 0x0000000000000020, /**< Page has been modified */
+ PTE_ACCESSED = 0x0000000000000040, /**< Page has been accessed */
};
/**
@@ -56,11 +56,30 @@ class PageTableManager
*/
enum
{
- PT_SIZE = (1 << 18), /**< Size of Page Table in bytes */
- PTE_SIZE = 2*sizeof(uint64_t), /**< Size of a single Page Table Entry in bytes */
- PTEG_SIZE = 8, /**< Number of PTEs in a single PTE Group */
- PTEG_COUNT = (PT_SIZE / PTE_SIZE) / PTEG_SIZE, /**< Number of PTEGs in the Page Table */
- INVALID_PN = 0xFFFFFFFFFFFFFFFF, /**< Invalid value for a Page Number return */
+ PT_SIZE = (1 << 18), /**< Size of Page Table in bytes */
+ PTE_SIZE = /**< Size of 1 Page Table Entry in bytes */
+ 2*sizeof(uint64_t),
+ PTEG_SIZE = 8, /**< Number of PTEs in a single PTE Group */
+ PTEG_COUNT = /**< Number of PTEGs in the Page Table */
+ (PT_SIZE / PTE_SIZE) / PTEG_SIZE,
+ INVALID_PN = /**< Error value for a Page Number return */
+ 0xFFFFFFFFFFFFFFFF,
+ };
+
+ /**
+ * LRU Statistics
+ */
+ struct UsageStats_t {
+ union {
+ struct {
+ uint64_t C:1; /**< Page has been modified */
+ uint64_t R:1; /**< Page has been referenced */
+ uint64_t LRU:2; /**< Current value of LRU */
+ uint64_t rsv:60; /**< Reserved space */
+ };
+ uint64_t fullword; /**< Full Dword0 */
+ };
+ UsageStats_t():fullword(0){};
};
/**
@@ -175,7 +194,8 @@ class PageTableManager
struct { /**< Dword0 Attributes */
uint64_t B:2; /**< 0:1 Segment Size */
uint64_t AVA:55; /**< 2:56 Abbreviated Virtual Address = VA w/o bottom 23 bits */
- uint64_t SW:2; /**< 57:58 =SW[0:1] - Reserved */
+ uint64_t SW:1; /**< 57 =SW[] - Reserved */
+ uint64_t R2:1; /**< 58 =SW[1] - Shadow R bit */
uint64_t LRU:2; /**< 59:60 =SW[2:3] - Used for LRU algorithm */
uint64_t L:1; /**< 61 Virtual page size */
uint64_t H:1; /**< 62 Hash function identifier */
@@ -413,13 +433,27 @@ class PageTableManager
VmmManager::ACCESS_TYPES getAccessType( const PageTableEntry* i_pte );
/**
+ * @brief Calculate the original Virtual Address from a PTE
+ *
+ * @param[in] i_pte PTE to examine, must be pointer to real entry in table
+ *
+ * @return uint64_t page-aligned virtual address
+ */
+ uint64_t getVirtAddrFromPTE( const PageTableEntry* i_pte );
+
+ /**
* @brief Fill in default values for the PTE
*
* @param[out] o_pte PTE to modify
*/
static void setupDefaultPTE( PageTableEntry* o_pte );
-
+ /**
+ * @brief Push C/R/LRU bits to the VMM
+ *
+ * @param[in] i_pte PTE to examine, must be pointer to real entry in table
+ */
+ void pushUsageStats( PageTableEntry* i_pte );
// Allow testcase to see inside
friend class ptmgrtest;
diff --git a/src/include/kernel/segment.H b/src/include/kernel/segment.H
index 05c5c0fdc..d7a4a7547 100644
--- a/src/include/kernel/segment.H
+++ b/src/include/kernel/segment.H
@@ -28,6 +28,7 @@
#include <kernel/task.H>
#include <errno.h>
+#include <kernel/ptmgr.H>
/** @class Segment
* @brief Virtual segment class to handle virtual memory management within
@@ -75,6 +76,19 @@ class Segment
*/
virtual uint64_t findPhysicalAddress(uint64_t i_vaddr) const { return -EFAULT; };
+ /**
+ * @brief Update LRU statistics on the block that owns the address
+ *
+ * @param[in] i_vaddr - Virtual Address of page
+ * @param[in] i_stats - Usage statistics
+ */
+ virtual void updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats )
+ {
+ //default to a NOOP
+ return;
+ };
+
protected:
/** The base address of the segment. */
const uint64_t iv_baseAddress;
diff --git a/src/include/kernel/segmentmgr.H b/src/include/kernel/segmentmgr.H
index 0d8caec33..563497047 100644
--- a/src/include/kernel/segmentmgr.H
+++ b/src/include/kernel/segmentmgr.H
@@ -29,6 +29,7 @@
#include <kernel/task.H>
#include <builtins.h>
+#include <kernel/ptmgr.H>
// Forward declaration.
class Segment;
@@ -107,6 +108,15 @@ class SegmentManager
*/
static uint64_t findPhysicalAddress(uint64_t i_vaddr);
+ /**
+ * @brief Update LRU statistics on the block that owns the page
+ *
+ * @param[in] i_vaddr - Virtual Address of page
+ * @param[in] i_stats - Usage statistics
+ */
+ static void updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats );
+
private:
/** See handlePageFault. */
@@ -115,6 +125,9 @@ class SegmentManager
void _addSegment(Segment* i_segment, size_t i_segId);
/** See initSLB. */
void _initSLB();
+ /** See updateRefCount. */
+ void _updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats );
/** See findPhysicalAddress */
uint64_t _findPhysicalAddress(uint64_t i_vaddr) const;
diff --git a/src/include/kernel/spte.H b/src/include/kernel/spte.H
index 1a284d6e7..abd81dff3 100644
--- a/src/include/kernel/spte.H
+++ b/src/include/kernel/spte.H
@@ -66,8 +66,11 @@ class ShadowPTE
/** Allocate from a zero'd page. */
uint32_t allocate_from_zero:1;
+ /** LRU value - lower means it was accessed more recently. */
+ uint32_t last_access:3;
+
/** Reserved for future use. */
- uint32_t reserved:6;
+ uint32_t reserved:3;
} PACKED;
};
@@ -106,10 +109,34 @@ class ShadowPTE
bool isDirty() const { return dirty; };
/** Set dirty bit. */
void setDirty(bool i_dirty) { dirty = i_dirty; };
+
/** Get allocate-from-zero bit. */
bool isAllocateFromZero() const { return allocate_from_zero; };
/** Set allocate-from-zero bit. */
void setAllocateFromZero(bool i_zero) { allocate_from_zero = i_zero; };
+
+ /** Get last_acces bits. */
+ uint8_t getLRU() const { return last_access; };
+ /** Increment the LRU bits. */
+ void incLRU() {
+ if( last_access < 0b111 )
+ {
+ last_access++;
+ }
+ };
+ /** Decrement the LRU bits. */
+ void decLRU() {
+ if( last_access > 0 )
+ {
+ last_access--;
+ }
+ };
+ /** Zero out the LRU bits. */
+ void zeroLRU() {
+ last_access = 0;
+ };
+
+
};
#endif
diff --git a/src/include/util/align.H b/src/include/util/align.H
index 3ac7eee97..7ff67ba38 100644
--- a/src/include/util/align.H
+++ b/src/include/util/align.H
@@ -25,7 +25,13 @@
#include <limits.h>
+// Return a number >= input that is aligned on a 4-byte boundary
#define ALIGN_4(u) ((u + 0x3ull) & ~0x3ull)
+
+// Return a number >= input that is aligned on a page boundary
#define ALIGN_PAGE(u) ((u + (PAGESIZE-1)) & ~(PAGESIZE-1))
+// Return a number <= input that is aligned on a page boundary
+#define ALIGN_PAGE_DOWN(u) ((u) - (u)%PAGESIZE)
+
#endif
diff --git a/src/kernel/basesegment.C b/src/kernel/basesegment.C
index 2dc99d458..72ace03c8 100644
--- a/src/kernel/basesegment.C
+++ b/src/kernel/basesegment.C
@@ -136,3 +136,9 @@ uint64_t BaseSegment::findPhysicalAddress(uint64_t i_vaddr) const
return (iv_block ? iv_block->findPhysicalAddress(i_vaddr) : -EFAULT);
}
+void BaseSegment::updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats )
+{
+ // Just call over to block chain
+ iv_block->updateRefCount(i_vaddr, i_stats);
+}
diff --git a/src/kernel/block.C b/src/kernel/block.C
index 1402bd60a..59a7892f2 100644
--- a/src/kernel/block.C
+++ b/src/kernel/block.C
@@ -32,7 +32,7 @@
#include <kernel/vmmmgr.H>
#include <kernel/ptmgr.H>
#include <kernel/pagemgr.H>
-//#include <kernel/console.H>
+#include <kernel/console.H>
Block::~Block()
{
@@ -235,6 +235,7 @@ void Block::setPageAllocateFromZero(uint64_t i_vAddr)
else
{
// No block owns this address. Code bug.
+ printk("setPageAllocateFromZero> i_vaddr=0x%.lX\n", i_vAddr );
kassert(iv_nextBlock);
}
return;
@@ -247,10 +248,10 @@ void Block::setPageAllocateFromZero(uint64_t i_vAddr)
void Block::releaseAllPages()
{
- // Release all pages from page table.
+ // Release all pages from page table.
PageTableManager::delRangeVA(iv_baseAddr, iv_baseAddr + iv_size);
- // Free all pages back to page manager.
+ // Free all pages back to page manager.
for(uint64_t page = iv_baseAddr;
page < (iv_baseAddr + iv_size);
page += PAGESIZE)
@@ -264,3 +265,39 @@ void Block::releaseAllPages()
}
}
}
+
+void Block::updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats )
+{
+ // Check containment, call down chain if address isn't in this block.
+ if (!isContained(i_vaddr))
+ {
+ if (iv_nextBlock)
+ {
+ iv_nextBlock->updateRefCount(i_vaddr, i_stats);
+ }
+ else
+ {
+ // No block owns this address. Code bug.
+ printk("updateRefCount> i_vaddr=0x%.lX\n", i_vaddr );
+ kassert(iv_nextBlock);
+ }
+ return;
+ }
+
+ ShadowPTE* spte = getPTE(i_vaddr);
+
+ // Adjust the LRU statistics
+ if( i_stats.R )
+ {
+ spte->zeroLRU();
+ }
+ else
+ {
+ spte->incLRU();
+ }
+
+ // track the changed/dirty bit
+ spte->setDirty( i_stats.C );
+
+}
diff --git a/src/kernel/ptmgr.C b/src/kernel/ptmgr.C
index f1e72bbca..0feffb7e6 100644
--- a/src/kernel/ptmgr.C
+++ b/src/kernel/ptmgr.C
@@ -24,8 +24,10 @@
#include <kernel/vmmmgr.H>
#include <util/singleton.H>
#include <kernel/console.H>
+#include <kernel/segmentmgr.H>
#include <arch/ppc.H>
#include <assert.h>
+#include <util/align.H>
//#define Dprintk(...) printkd(args...)
#define Dprintk(args...)
@@ -308,14 +310,17 @@ void PageTableManager::_addEntry( uint64_t i_vAddr,
//Note: no need to lock here because that is handled by higher function
+ // page-align this address
+ uint64_t l_vaddr = ALIGN_PAGE_DOWN(i_vAddr);
+
PageTableEntry pte_data;
setupDefaultPTE( &pte_data );
// find the matching PTEG first so we only do it once
- uint64_t pteg_addr = findPTEG( i_vAddr );
+ uint64_t pteg_addr = findPTEG( l_vaddr );
//look for a matching entry in the table already
- PageTableEntry* pte_slot = findPTE( i_vAddr, pteg_addr );
+ PageTableEntry* pte_slot = findPTE( l_vaddr, pteg_addr );
if( pte_slot == NULL )
{
// look for an empty/invalid entry that we can use
@@ -324,14 +329,26 @@ void PageTableManager::_addEntry( uint64_t i_vAddr,
{
// look for a valid entry we can steal
pte_slot = findOldPTE( pteg_addr );
+
+ // delete the entry that we're going to steal first
+ delEntry( pte_slot );
}
+
+ // since the entry isn't in the table right now we should
+ // start fresh with the usage bits
+ pte_data.C = 0b0; //Clear Changed bit
+ pte_data.R = 0b0; //Clear Referenced bit
+ pte_data.LRU = 0b00; //Clear LRU bits
}
else
{
- if( (pte_slot->V == 1) && (i_page != pte_slot->PN) )
+ if( pte_slot->V == 1 )
{
- Eprintk( "**ERROR** PageTableManager::_addEntry> Duplicate PTE with different page number\n" );
- kassert(false);
+ if( i_page != pte_slot->PN )
+ {
+ Eprintk( "**ERROR** PageTableManager::_addEntry> Duplicate PTE with different page number\n" );
+ kassert(false);
+ }
}
}
@@ -378,7 +395,11 @@ void PageTableManager::_delEntry( uint64_t i_vAddr )
*/
void PageTableManager::delEntry( PageTableEntry* i_pte )
{
+ // clear the entry from the table
writePTE( i_pte, i_pte, false );
+
+ // need to notify VMM when we remove a PTE
+ pushUsageStats( i_pte );
}
/**
@@ -412,7 +433,7 @@ void PageTableManager::_delRangePN( uint64_t i_pnStart,
{
if( (pte->V == 1) && (pte->PN >= i_pnStart) && (pte->PN <= i_pnFinish) )
{
- writePTE( pte, pte, false );
+ delEntry( pte );
}
pte++;
@@ -604,12 +625,18 @@ void PageTableManager::writePTE( PageTableEntry* i_pte,
PageTableEntry* i_dest,
bool i_valid )
{
- // are we stealing a PTE
- bool pte_stolen = false;
+ // Are we stealing a valid PTE?
if( (i_dest->V == 1) && i_valid )
{
- pte_stolen = true;
- printPTE( "Stealing", i_dest );
+ // If the AVAs match then we're just modifying permissions or something
+ if( i_pte->AVA != i_dest->AVA )
+ {
+ // this should never happen because we should always go
+ // through the delEntry() path instead
+ printPTE( "Stealing", i_dest );
+ Eprintk( "**ERROR** PageTableManager::writePTE> Trying to steal a PTE\n" );
+ kassert(false);
+ }
}
if(ivTABLE)
@@ -703,7 +730,7 @@ void PageTableManager::printPTE( const char* i_label,
}
else
{
- printkd( "[%4ld:%4ld]> @%p : %.16lX %.16lX : AVA=%16lX, PN=%ld\n", pte_num/PTEG_SIZE, pte_num%PTEG_SIZE, i_pte, i_pte->dword0, i_pte->dword1, i_pte->AVA, i_pte->PN );
+ printkd( "[%4ld:%4ld]> @%p : %.16lX %.16lX : VA=%16lX, PN=%ld\n", pte_num/PTEG_SIZE, pte_num%PTEG_SIZE, i_pte, i_pte->dword0, i_pte->dword1, getVirtAddrFromPTE(i_pte), i_pte->PN );
}
}
@@ -822,9 +849,9 @@ VmmManager::ACCESS_TYPES PageTableManager::getAccessType( const PageTableEntry*
*/
void PageTableManager::setupDefaultPTE( PageTableEntry* o_pte )
{
- o_pte->B = 0b01; //Segment Size (01=1TB)
- o_pte->L = 0b0; //Virtual page size (1=>4KB)
- o_pte->H = 0b0; //Hash function identifier (0=primary hash)
+ o_pte->B = 0b01; //Segment Size (01=1TB)
+ o_pte->L = 0b0; //Virtual page size (1=>4KB)
+ o_pte->H = 0b0; //Hash function identifier (0=primary hash)
}
/**
@@ -876,7 +903,7 @@ PageTableManager::PageTableEntry* PageTableManager::findOldPTE( uint64_t i_ptegA
pte++;
}
- PageTableManager::printPTE( "Dropping PTE", old_pte );
+ //PageTableManager::printPTE( "Dropping PTE", old_pte );
return old_pte;
}
@@ -908,21 +935,34 @@ void PageTableManager::updateLRU( const PageTableEntry* i_newPTE )
new_pte = *pte_cur;
old_pte = *pte_cur;
+ // if the entry is valid and has been used since last update
+ // then reset the LRU to 0 and clear the R bit
if( (new_pte.V == 1) && (new_pte.R == 1) )
{
- new_pte.LRU = 1;
+ new_pte.LRU = 0;
new_pte.R = 0;
+ new_pte.R2 = 1; // remember that the R bit was set
}
else
{
+ // page hasn't been used, increment the LRU
if( new_pte.LRU < 0b11 )
{
new_pte.LRU++;
}
}
- } while( !__sync_bool_compare_and_swap( &(pte_cur->dword0),
- old_pte.dword0,
- new_pte.dword0 ) );
+
+
+ // if the value currently in the table (pte_cur) is still
+ // equal to the value we saved off earlier (old_pte) then
+ // we will write our new data (new_pte) into the table
+ // else we will try again
+ } while( !__sync_bool_compare_and_swap( &(pte_cur->dword1),
+ old_pte.dword1,
+ new_pte.dword1 ) );
+
+ // LRU and R2 are on dword0
+ pte_cur->dword0 = new_pte.dword0;
// tlbie, eieio, tlbsync, ptesync
invalidateTLB(pte_cur);
@@ -965,3 +1005,100 @@ void PageTableManager::invalidateTLB( PageTableEntry* i_pte )
Tprintk( "<< PageTableManager::invalidateTLB( )\n" );
}
+/**
+ * @brief Calculate the original Virtual Address from a PTE
+ */
+uint64_t PageTableManager::getVirtAddrFromPTE( const PageTableEntry* i_pte )
+{
+ uint64_t pte_addr = (uint64_t)i_pte;
+
+ /*
+ 0....5....1....5....2....5....3....5....4....5....50...5....6....5....7....5.. full VA (78 bits)
+ 000000000000000vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv000000000000 top 15 and bottom 12 bits are zero
+ 000000000000000---------aaaaaaaaaaaaaa---------------------------------------- X=VA[24:37]
+ 000000000000000bbbbbbbbbbbbBBBBBBBBBBB---------------------------------------- Y=VA[0:37]
+ 000000000000000-----------------------cccccccccccccccccCCCCCCCCCCC------------ Z=VA[38:65]
+ 000000000000000dddddddddddddddddddddddddddddddddddddddd----------------------- AVA = VA[0:54]
+
+ 0000000000000000000000000000BBBBBBBBBBB = Y' = VA[27:37] = AVA[27:37]
+ 0000000000000000000000000000CCCCCCCCCCC = Z' = VA[55:65]
+
+ X^Y^Z % 2048 = ptegnum
+ 0^Y^Z % 2048 = ptegnum (all of X is above the % line)
+ Y' ^ Z' = ptegnum
+ Z' = Y' ^ ptegnum
+ */
+
+ // first get the PTEG number (=hash result) based on the PTE pointer
+ uint64_t pteg_num = (pte_addr - getAddress())/PTEG_SIZE_BYTES;
+
+ // next pull the Y' value out of the AVA
+ uint64_t Yp = EXTRACT_RJ_LEN( i_pte->AVA, 55, 27, 37 );
+
+ // next invert the XOR operation from the hash function
+ uint64_t Zp = Yp ^ pteg_num;
+
+ // finally put everything together to make a complete virtual address
+ uint64_t va = (Zp << 12) | (i_pte->AVA << 23);
+
+ return va;
+}
+
+/**
+ * @brief Push C/R/LRU bits to the VMM
+ *
+ * @param[in] i_pte PTE to examine, must be pointer to real entry in table
+ */
+void PageTableManager::pushUsageStats( PageTableEntry* i_pte )
+{
+ // skip this in unit-test mode because addresses aren't really backed
+ if( ivTABLE )
+ {
+ return;
+ }
+
+ UsageStats_t stats;
+
+ PageTableEntry new_pte = *i_pte;
+ PageTableEntry old_pte = *i_pte;
+
+ // use this funny loop to avoid races where another thread
+ // is causing the C bit to be updated at the same time
+ do {
+ new_pte = *i_pte;
+ old_pte = *i_pte;
+
+ // always update the R,R2,C bits, even for invalid entries
+ // it is up to the caller to be sensible about when this
+ // function is called
+
+ // read and clear the referenced bit
+ stats.R = new_pte.R || new_pte.R2;
+ new_pte.R = 0;
+ new_pte.R2 = 0; //R2 is on dword0
+
+ // read and clear the changed bit
+ stats.C = new_pte.C;
+ new_pte.C = 0;
+
+ // just read the LRU and send it up
+ stats.LRU = new_pte.LRU;
+
+ // if the value currently in the table (i_pte) is still
+ // equal to the value we saved off earlier (old_pte) then
+ // we will write our new data (new_pte) into the table
+ // else we will try again
+ } while( !__sync_bool_compare_and_swap( &(i_pte->dword1),
+ old_pte.dword1,
+ new_pte.dword1 ) );
+
+ // R2 is on dword0
+ i_pte->dword0 = new_pte.dword0;
+
+ // tlbie, eieio, tlbsync, ptesync
+ invalidateTLB(i_pte);
+
+ // now we need to send what we learned to the rest of the VMM
+ uint64_t va = getVirtAddrFromPTE(i_pte);
+ SegmentManager::updateRefCount( va, stats );
+}
diff --git a/src/kernel/segmentmgr.C b/src/kernel/segmentmgr.C
index 3f43db965..c52b4de04 100644
--- a/src/kernel/segmentmgr.C
+++ b/src/kernel/segmentmgr.C
@@ -49,6 +49,12 @@ uint64_t SegmentManager::findPhysicalAddress(uint64_t i_vaddr)
return Singleton<SegmentManager>::instance()._findPhysicalAddress(i_vaddr);
}
+void SegmentManager::updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats )
+{
+ Singleton<SegmentManager>::instance()._updateRefCount(i_vaddr,i_stats);
+}
+
bool SegmentManager::_handlePageFault(task_t* i_task, uint64_t i_addr)
{
size_t segId = getSegmentIdFromAddress(i_addr);
@@ -112,3 +118,18 @@ uint64_t SegmentManager::_findPhysicalAddress(uint64_t i_vaddr) const
return paddr;
}
+void SegmentManager::_updateRefCount( uint64_t i_vaddr,
+ PageTableManager::UsageStats_t i_stats )
+{
+ // This constant should come from page manager. Segment size.
+ const size_t SLBE_s = 40;
+
+ // Get segment ID from effective address.
+ size_t segId = i_vaddr >> SLBE_s;
+
+ // Call contained segment object to update the reference count
+ if ((segId < MAX_SEGMENTS) && (NULL != iv_segments[segId]))
+ {
+ iv_segments[segId]->updateRefCount( i_vaddr, i_stats );
+ }
+}
diff --git a/src/usr/testcore/kernel/ptmgrtest.H b/src/usr/testcore/kernel/ptmgrtest.H
index 9d1793710..c32573a43 100644
--- a/src/usr/testcore/kernel/ptmgrtest.H
+++ b/src/usr/testcore/kernel/ptmgrtest.H
@@ -95,8 +95,7 @@ class ptmgrtest : public CxxTest::TestSuite
*/
void test_hash40( void )
{
- TS_TRACE( ">> test_hash40 <<" );
- printkd( ">> test_hash40 <<\n" );
+ TS_TRACE( ">> ptmgrtest::test_hash40 <<" );
uint64_t fails, total = 0;
// Initialize the Page Table
@@ -123,7 +122,6 @@ class ptmgrtest : public CxxTest::TestSuite
delete ptmgr;
TS_TRACE( "ptmgrtest::test_hash40> fails=%d/%d", fails, total );
- printkd( "ptmgrtest::test_hash40> fails=%ld/%ld\n", fails, total );
}
/**
@@ -134,8 +132,7 @@ class ptmgrtest : public CxxTest::TestSuite
*/
void test_addEntry( void )
{
- TS_TRACE( ">> test_addEntry <<" );
- printkd( ">> test_addEntry <<\n" );
+ TS_TRACE( ">> ptmgrtest::test_addEntry <<" );
uint64_t fails = 0;
uint64_t total = 0;
uint64_t status = 0;
@@ -160,6 +157,16 @@ class ptmgrtest : public CxxTest::TestSuite
else if( (status & PageTableManager::PTE_VALID) && (pn == TEST_DATA[x].page) )
{
PASS_TRACE( "ptmgrtest::test_addEntry> PASS1 : 0x%.16lX", TEST_DATA[x].va );
+
+ PageTableManager::PageTableEntry* pte = ptmgr->findPTE(TEST_DATA[x].va);
+ uint64_t va = ptmgr->getVirtAddrFromPTE(pte);
+ if( va != (TEST_DATA[x].va - TEST_DATA[x].va%4096) )
+ {
+ printk( "ptmgrtest::test_addEntry> ERROR6 : VA doesn't match expected" );
+ printk( "Exp=%.16lX, Act=%.16lX", TEST_DATA[x].va, va );
+ fails++;
+ }
+ total++;
}
else
{
@@ -217,7 +224,6 @@ class ptmgrtest : public CxxTest::TestSuite
delete ptmgr;
TS_TRACE( "ptmgrtest::test_addEntry> fails=%d/%d", fails, total );
- printkd( "ptmgrtest::test_addEntry> fails=%ld/%ld\n", fails, total );
}
/**
@@ -227,8 +233,7 @@ class ptmgrtest : public CxxTest::TestSuite
*/
void test_delEntry( void )
{
- TS_TRACE( ">> test_delEntry <<" );
- printkd( ">> test_delEntry <<\n" );
+ TS_TRACE( ">> ptmgrtest::test_delEntry <<" );
uint64_t fails = 0;
uint64_t total = 0;
uint64_t ignored = 0;
@@ -266,7 +271,6 @@ class ptmgrtest : public CxxTest::TestSuite
delete ptmgr;
TS_TRACE( "ptmgrtest::test_delEntry> fails=%d/%d", fails, total );
- printkd( "ptmgrtest::test_delEntry> fails=%ld/%ld\n", fails, total );
}
/**
@@ -277,8 +281,7 @@ class ptmgrtest : public CxxTest::TestSuite
*/
void test_delRangeVA( void )
{
- TS_TRACE( ">> test_delRangeVA <<" );
- printkd( ">> test_delRangeVA <<\n" );
+ TS_TRACE( ">> ptmgrtest::test_delRangeVA <<" );
uint64_t fails = 0;
uint64_t total = 0;
uint64_t ignored = 0;
@@ -326,7 +329,6 @@ class ptmgrtest : public CxxTest::TestSuite
delete ptmgr;
TS_TRACE( "ptmgrtest::test_delRangeVA> fails=%d/%d", fails, total );
- printkd( "ptmgrtest::test_delRangeVA> fails=%ld/%ld\n", fails, total );
}
/**
@@ -337,8 +339,7 @@ class ptmgrtest : public CxxTest::TestSuite
*/
void test_delRangePN( void )
{
- TS_TRACE( ">> test_delRangePN <<" );
- printkd( ">> test_delRangePN <<\n" );
+ TS_TRACE( ">> ptmgrtest::test_delRangePN <<" );
uint64_t fails = 0;
uint64_t total = 0;
uint64_t pagenum = 0;
@@ -390,11 +391,12 @@ class ptmgrtest : public CxxTest::TestSuite
delete ptmgr;
TS_TRACE( "ptmgrtest::test_delRangePN> fails=%d/%d", fails, total );
- printkd( "ptmgrtest::test_delRangePN> fails=%ld/%ld\n", fails, total );
}
//@todo - Test LRU
+ //@todo - Test propagation of C/R bits to blocks
+
private:
/**
* @brief Populate the table with entries
OpenPOWER on IntegriCloud