diff options
| author | Matthew Barth <msbarth@us.ibm.com> | 2011-09-29 15:42:30 -0500 |
|---|---|---|
| committer | MATTHEW S. BARTH <msbarth@us.ibm.com> | 2011-10-10 15:51:19 -0500 |
| commit | 8c062af8b6bd50a59823c7ec430ec5fc019052d2 (patch) | |
| tree | dbe0625a3024a3737ddfa5d32634ce8225fda991 /src/kernel | |
| parent | 4de170997eee6244b2091bb8bf065ae2da1396d7 (diff) | |
| download | talos-hostboot-8c062af8b6bd50a59823c7ec430ec5fc019052d2.tar.gz talos-hostboot-8c062af8b6bd50a59823c7ec430ec5fc019052d2.zip | |
Flush/Release/Evict pages functionality
Change-Id: Ic0bb4122164e11f6d13e6850abf8ae9bd32caea2
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/393
Tested-by: Jenkins Server
Reviewed-by: MATTHEW S. BARTH <msbarth@us.ibm.com>
Diffstat (limited to 'src/kernel')
| -rw-r--r-- | src/kernel/basesegment.C | 25 | ||||
| -rw-r--r-- | src/kernel/block.C | 89 | ||||
| -rw-r--r-- | src/kernel/blockmsghdlr.C | 73 | ||||
| -rw-r--r-- | src/kernel/syscall.C | 5 | ||||
| -rw-r--r-- | src/kernel/vmmmgr.C | 24 |
5 files changed, 202 insertions, 14 deletions
diff --git a/src/kernel/basesegment.C b/src/kernel/basesegment.C index 8eb7e5484..4cb2c39cb 100644 --- a/src/kernel/basesegment.C +++ b/src/kernel/basesegment.C @@ -118,7 +118,8 @@ int BaseSegment::_mmAllocBlock(MessageQueue* i_mq,void* i_va,uint64_t i_size) //Verify input address and size falls within this segment's address range if (l_vaddr < this->getBaseAddress() || l_vaddr >= (this->getBaseAddress() + (1ull << SLBE_s)) || - (l_blockSizeTotal + ALIGN_PAGE(i_size)) >= (1ull << SLBE_s)) + (l_blockSizeTotal + ALIGN_PAGE(i_size)) >= (1ull << SLBE_s) || + (l_vaddr != ALIGN_PAGE_DOWN(l_vaddr))) { return -EINVAL; } @@ -198,3 +199,25 @@ void BaseSegment::castOutPages(uint64_t i_type) // Could try again with a more agressive constraint if cast_out == 0 ???? if(cast_out) printkd("Cast out %ld pages,Type=%ld\n",cast_out,i_type); } +/** + * STATIC + * Remove pages by a specified operation of the given size + */ +int BaseSegment::mmRemovePages(VmmManager::PAGE_REMOVAL_OPS i_op, + void* i_vaddr, uint64_t i_size, task_t* i_task) +{ + return Singleton<BaseSegment>::instance()._mmRemovePages(i_op,i_vaddr, + i_size,i_task); +} + +/** + * Remove pages by a specified operation of the given size + */ +int BaseSegment::_mmRemovePages(VmmManager::PAGE_REMOVAL_OPS i_op, + void* i_vaddr, uint64_t i_size, task_t* i_task) +{ + //Don't allow removal of pages for base block + return (iv_block->iv_nextBlock ? + iv_block->iv_nextBlock->removePages(i_op,i_vaddr,i_size,i_task): + -EINVAL); +} diff --git a/src/kernel/block.C b/src/kernel/block.C index f72a8f6f7..5b1520b1b 100644 --- a/src/kernel/block.C +++ b/src/kernel/block.C @@ -27,6 +27,8 @@ #include <sys/msg.h> +#include <util/align.H> + #include <kernel/block.H> #include <kernel/spte.H> #include <kernel/vmmmgr.H> @@ -39,6 +41,9 @@ Block::~Block() { // Release shadow PTE array. delete[] iv_ptes; + //Release message handlers + delete iv_readMsgHdlr; + delete iv_writeMsgHdlr; // Delete next block in the chain. if (iv_nextBlock) @@ -51,11 +56,14 @@ void Block::init(MessageQueue* i_msgQ) { // Create a shadow PTE for each page. iv_ptes = new ShadowPTE[iv_size / PAGESIZE]; - this->iv_msgHdlr = NULL; if (i_msgQ != NULL) { - //Create message handler attribute for this block with this msgq - this->iv_msgHdlr = new BlockMsgHdlr(VmmManager::getLock(),i_msgQ,this); + //Create message handler to handle read operations for this block + this->iv_readMsgHdlr = + new BlockReadMsgHdlr(VmmManager::getLock(),i_msgQ,this); + //Create message handler to handle write operations for this block + this->iv_writeMsgHdlr = + new BlockWriteMsgHdlr(VmmManager::getLock(),i_msgQ,this); } } @@ -80,7 +88,7 @@ bool Block::handlePageFault(task_t* i_task, uint64_t i_addr) if (!pte->isPresent()) { - if (this->iv_msgHdlr != NULL) + if (this->iv_readMsgHdlr != NULL) { void* l_page = reinterpret_cast<void*>(pte->getPageAddr()); //If the page data is zero, create the page @@ -94,7 +102,7 @@ bool Block::handlePageFault(task_t* i_task, uint64_t i_addr) pte->setWritable(true); } //Send message to handler to read page - this->iv_msgHdlr->sendMessage(MSG_MM_RP_READ, + this->iv_readMsgHdlr->sendMessage(MSG_MM_RP_READ, reinterpret_cast<void*>(l_addr_palign),l_page,i_task); //Done(waiting for response) return true; @@ -406,7 +414,6 @@ size_t Block::castOutPages(uint64_t i_type) return cast_out; } - int Block::mmSetPermission(uint64_t i_va, uint64_t i_size,uint64_t i_access_type) { int l_rc = 0; @@ -508,5 +515,75 @@ int Block::mmSetPermission(uint64_t i_va, uint64_t i_size,uint64_t i_access_type } return l_rc; +} + +int Block::removePages(VmmManager::PAGE_REMOVAL_OPS i_op, void* i_vaddr, + uint64_t i_size, task_t* i_task) +{ + uint64_t l_vaddr = reinterpret_cast<uint64_t>(i_vaddr); + //Align virtual address & size to page boundary + /*The given virtual address will be 'rounded' down to the nearest page + boundary, along with the given size will be 'rounded' up to the + nearest divisible page size.*/ + uint64_t l_aligned_va = ALIGN_PAGE_DOWN(l_vaddr); + uint64_t l_aligned_size = ALIGN_PAGE(i_size); + //Find block containing virtual address + if(!this->isContained(l_aligned_va)) + { + return (iv_nextBlock ? + iv_nextBlock->removePages(i_op,i_vaddr,i_size,i_task):-EINVAL); + } + else if ((l_aligned_va+l_aligned_size) > (this->iv_baseAddr+this->iv_size)) + { + return -EINVAL; + } + //Perform requested page removal operation + for (l_vaddr = l_aligned_va;l_vaddr < (l_aligned_va+l_aligned_size); + l_vaddr+= PAGESIZE) + { + ShadowPTE* pte = getPTE(l_vaddr); + uint64_t pageAddr = pte->getPageAddr(); + if (pte->isPresent() && (0 != pageAddr)) + { + //Delete from HW page table immediately + PageTableManager::delEntry(l_vaddr); + if (pte->isDirty() && pte->isWriteTracked() && + this->iv_writeMsgHdlr != NULL) + { + releasePTE(pte); + //Send write msg with the page address + if (i_task != NULL) + { + this->iv_writeMsgHdlr->incMsgCount(i_task); + } + this->iv_writeMsgHdlr->addVirtAddr( + reinterpret_cast<void*>(l_vaddr),pageAddr); + this->iv_writeMsgHdlr->sendMessage(MSG_MM_RP_WRITE, + reinterpret_cast<void*>(l_vaddr), + reinterpret_cast<void*>(pageAddr),i_task); + } + else if (pte->isDirty() && !pte->isWriteTracked() && + i_op == VmmManager::EVICT) + { + //Leave as 'printk' to note page was skipped + printk("Block::removePages >> Unable to EVICT "); + printk("dirty page thats not write tracked: "); + printk("va: 0x%.16lX, pa: 0x%.16lX \n",l_vaddr, pageAddr); + } + else if (i_op != VmmManager::FLUSH) + { + //'Release' page entry + releasePTE(pte); + PageManager::freePage(reinterpret_cast<void*>(pageAddr)); + } + } + } + return 0; +} + +void Block::releasePTE(ShadowPTE* i_pte) +{ + i_pte->setPresent(false); + i_pte->setPageAddr(NULL); } diff --git a/src/kernel/blockmsghdlr.C b/src/kernel/blockmsghdlr.C index cf1683cab..fc49ae89d 100644 --- a/src/kernel/blockmsghdlr.C +++ b/src/kernel/blockmsghdlr.C @@ -22,8 +22,9 @@ // IBM_PROLOG_END #include <kernel/blockmsghdlr.H> #include <kernel/block.H> +#include <kernel/pagemgr.H> -MessageHandler::HandleResult BlockMsgHdlr::handleResponse( +MessageHandler::HandleResult BlockReadMsgHdlr::handleResponse( msg_sys_types_t i_type, void* i_key, task_t* i_task, int i_rc) { if (i_rc != 0) @@ -41,3 +42,73 @@ MessageHandler::HandleResult BlockMsgHdlr::handleResponse( return SUCCESS; } } + +MessageHandler::HandleResult BlockWriteMsgHdlr::handleResponse( + msg_sys_types_t i_type, void* i_key, task_t* i_task, int i_rc) +{ + //Default to indicate nothing specific has been done for this response. + MessageHandler::HandleResult l_result = UNHANDLED_RC; + if (i_rc != 0) + { + //Request default behavior of resume/kill task based on rc. + return l_result; + } + //Find the virtual address to page address mapping to know which page + //address to release since only the virtual address is available on the msg + PageAddrNode* l_paNode = iv_va2paList.find(i_key); + if (l_paNode) + { + l_result = SUCCESS; + //Complete page removal and remove list entry + PageManager::freePage(reinterpret_cast<void*>(l_paNode->pageAddr)); + iv_va2paList.erase(l_paNode); + delete l_paNode; + } + //Not handling a reponse from kernel + if (i_task != NULL) + { + //Find the task's msg count to know how many messages were sent + //to remove pages and whether to continue deferring the task or not + TaskMsgNode* l_tmNode = iv_msgGrpList.find(i_task); + if (l_tmNode) + { + //Last message for the given task + if (l_tmNode->msgCount == 1 && + (l_tmNode->next == NULL && l_tmNode->prev == NULL)) + { + l_result = SUCCESS; + iv_msgGrpList.erase(l_tmNode); + delete l_tmNode; + } + else + { + l_result = CONTINUE_DEFER; + l_tmNode->msgCount--; + } + } + } + + return l_result; +} + +void BlockWriteMsgHdlr::incMsgCount(task_t* i_task) +{ + TaskMsgNode* l_tmNode = iv_msgGrpList.find(i_task); + if (l_tmNode == NULL) + { + //Add task to list and set message count to 1 + l_tmNode = new TaskMsgNode(); + l_tmNode->key = i_task; + l_tmNode->msgCount = 0; + iv_msgGrpList.insert(l_tmNode); + } + l_tmNode->msgCount++; +} + +void BlockWriteMsgHdlr::addVirtAddr(void* i_vaddr,uint64_t i_pgAddr) +{ + PageAddrNode* l_paNode = new PageAddrNode(); + l_paNode->key = i_vaddr; + l_paNode->pageAddr = i_pgAddr; + iv_va2paList.insert(l_paNode); +} diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C index ecfdd9b1b..cb4650c7a 100644 --- a/src/kernel/syscall.C +++ b/src/kernel/syscall.C @@ -516,11 +516,12 @@ namespace Systemcalls */ void MmRemovePages(task_t* t) { - PAGE_REMOVAL_OPS oper = (PAGE_REMOVAL_OPS)TASK_GETARG0(t); + VmmManager::PAGE_REMOVAL_OPS oper = + (VmmManager::PAGE_REMOVAL_OPS)TASK_GETARG0(t); void* vaddr = (void*)TASK_GETARG1(t); uint64_t size = (uint64_t)TASK_GETARG2(t); - TASK_SETRTN(t, VmmManager::mmRemovePages(oper,vaddr,size)); + TASK_SETRTN(t, VmmManager::mmRemovePages(oper,vaddr,size,t)); } /** diff --git a/src/kernel/vmmmgr.C b/src/kernel/vmmmgr.C index c923d80e0..cfa29b569 100644 --- a/src/kernel/vmmmgr.C +++ b/src/kernel/vmmmgr.C @@ -147,9 +147,16 @@ bool VmmManager::_pteMiss(task_t* t, uint64_t effAddr) int VmmManager::mmAllocBlock(MessageQueue* i_mq,void* i_va,uint64_t i_size) { - return BaseSegment::mmAllocBlock(i_mq,i_va,i_size); + return Singleton<VmmManager>::instance()._mmAllocBlock(i_mq,i_va,i_size); } +int VmmManager::_mmAllocBlock(MessageQueue* i_mq,void* i_va,uint64_t i_size) +{ + lock.lock(); + int rc = BaseSegment::mmAllocBlock(i_mq,i_va,i_size); + lock.unlock(); + return rc; +} Spinlock* VmmManager::getLock() { @@ -169,12 +176,21 @@ uint64_t VmmManager::_findPhysicalAddress(uint64_t i_vaddr) return paddr; } -int VmmManager::mmRemovePages(PAGE_REMOVAL_OPS i_op, void* i_vaddr, - uint64_t i_size) +int VmmManager::mmRemovePages(VmmManager::PAGE_REMOVAL_OPS i_op, void* i_vaddr, + uint64_t i_size, task_t* i_task) { - return 0; + return Singleton<VmmManager>::instance()._mmRemovePages(i_op,i_vaddr, + i_size,i_task); } +int VmmManager::_mmRemovePages(VmmManager::PAGE_REMOVAL_OPS i_op,void* i_vaddr, + uint64_t i_size,task_t* i_task) +{ + lock.lock(); + int rc = BaseSegment::mmRemovePages(i_op,i_vaddr,i_size,i_task); + lock.unlock(); + return rc; +} int VmmManager::mmSetPermission(void* i_va, uint64_t i_size, uint64_t i_access_type) { |

