关于迁移
其他人的介绍:http://www.voidcn.com/article/p-txgoagur-xp.html
在每次IssueCommand的时候,都会在之前执行preHooks,之后执行postHooks。
【注意】
在GetChild( request )->IssueCommand( request );中,GetChild函数返回的是一个NVMObject_hook,而不是NVMObject;所以其实是先调用的NVMObjest.cpp的bool NVMObject_hook::IssueCommand( NVMainRequest *req )函数,该函数中先调用了preohooks,然后调用rv = trampoline->IssueCommand( req ); (trampoline即为该hook链接的对应NVMObject)这才调用了NVMObject的IssueCommand函数
CoinMigrator是一个回调类。而且既是prehooks也是posthooks.
首先会检查CoinMigrator的parent是不是nvmain。然后检查translator是Migrator。
1. 如果是prehook而且migratorTranslator->IsBuffered( request->address )也就是说【请求的这个块】已经存在buffer中。那么此时返回false.
在回到NVMObject_hook::IssueCommand中时,就【不会将命令发射下去】,也就是这个命令在这里停止了,不再Issue,直接返回buffer的结果。
bool NVMObject_hook::IssueCommand( NVMainRequest *req )
{
bool rv = true, dropRequest = false;
std::vector<NVMObject *>& preHooks = trampoline->GetHooks( NVMHOOK_PREISSUE );
std::vector<NVMObject *>& postHooks = trampoline->GetHooks( NVMHOOK_POSTISSUE );
std::vector<NVMObject *>::iterator it;
/* Call pre-issue hooks */
for( it = preHooks.begin(); it != preHooks.end(); it++ )
{
(*it)->SetParent( trampoline );
(*it)->SetCurrentHookType( NVMHOOK_PREISSUE );
dropRequest = !(*it)->IssueCommand( req );
(*it)->UnsetParent( );
}
/* Call IssueCommand. */
if( !dropRequest )
rv = trampoline->IssueCommand( req );
/* Call post-issue hooks. */
for( it = postHooks.begin(); it != postHooks.end(); it++ )
{
(*it)->SetParent( trampoline );
(*it)->SetCurrentHookType( NVMHOOK_POSTISSUE );
(*it)->IssueCommand( req );
(*it)->UnsetParent( );
}
return rv;
}
2. 如果是prehook但是请求的块没有被缓冲,则返回true。正常继续执行。
/* Migrations in progress must be served from the buffers during migration. */
if( GetCurrentHookType( ) == NVMHOOK_PREISSUE && migratorTranslator->IsBuffered( request->address ) )
{
/* Short circuit this request so it is not queued. */
rv = false;
/* Complete the request, adding some buffer read latency. */
GetEventQueue( )->InsertEvent( EventResponse, parent->GetTrampoline( ), request,
GetEventQueue()->GetCurrentCycle()+bufferReadLatency );
bufferedReads++;
return rv;
}
/* Don't inject results before the original is issued to prevent deadlock */
if( GetCurrentHookType( ) != NVMHOOK_POSTISSUE )
{
return rv;
}
3. 如果是posthook的情况,首先检查是否能够实现migrate, 这里首先要用到Migrator类,这是一个做地址变换的类,它里面存储了一些迁移相关的信息,先检查是否正在迁移,然后检查当前请求是否已经被迁移过了。然后保证请求的地址的通道不是promotionChannel【promotionChannel在配置文件中静态设置了】。
如果满足以上条件,则首先按照概率【决定是否迁移】,
首先promotee的地址是请求的地址,然后通过ChooseVictim函数【选择要替换的行】。
然后检查如果选择出的地址和要替换的地址都没有被迁移过,并且可以发射【CheckIssuable】,
具体的迁移由Migrator::StartMigration( NVMAddress& promotee, NVMAddress& demotee )完成,这个函数有两个输入,一个是promotee,另一个是demotee,迁移的目的是将内存行从慢速内存移动到快速内存中,promotee是在慢速内存中要迁移的地址,demotee是在快速内存中要被替换的地址。
添加hook,在CreateHook中,确认是哪个hook(配置文件最后AddHook配置为CoinMigrator)
NVMObject *HookFactory::CreateHook( std::string hookName )
{
NVMObject *hook = NULL;
if( hookName == "Visualizer" ) hook = new Visualizer( );
else if( hookName == "PostTrace" ) hook = new PostTrace( );
else if( hookName == "CoinMigrator" ) hook = new CoinMigrator( );
//else if( hookName == "MyHook" ) hook = new MyHook( );
if( hook != NULL )
hook->StatName( hookName );
return hook;
}
Migrator.cpp中函数进行一些迁移状态的判断(在CoinMigrator中被调用),还有地址翻译(迁移之后改变了,需要重新translate)
注意迁移不是立即迁移过去,而是需要IssueCommand进行读写,同时只可以有一个迁移操作在进行
CoinMigrator中两个重要函数:
TryMigration决定是否进行迁移以及被替换的page,若确认要迁移,对要交换的两个page发出读指令;
RequestComplete确认两个page都已经在交换buffer中后,发出写指令
以上两个函数均会调用Migrator.cpp中函数【确认/修改迁移状态】
先初始化查找牺牲页的一些信息(可替换的总数,如注释,可得到page count,再通过循环遍历所有fast memory的page依次进行替换) totalPromotionPages = p->RANKS * p->BANKS * p->ROWS;为重点;已获取后,每次需要选择victim page就直接选当前的,并指向下一个page,下次选下一个
void CoinMigrator::ChooseVictim( Migrator *at, NVMAddress& /*promotee*/, NVMAddress& victim )
{
/*
* Since this is no method called after every module in the system is
* initialized, we check here to see if we have queried the memory system
* about the information we need.
*/
if( !queriedMemory )
{
/*
* Our naive replacement policy will simply circle through all the pages
* in the fast memory. In order to count the pages we need to count the
* number of rows in the fast memory channel. We do this by creating a
* dummy request which would route to the fast memory channel. From this
* we can grab it's config pointer and calculate the page count.
*/
NVMainRequest queryRequest;
queryRequest.address.SetTranslatedAddress( 0, 0, 0, 0, promotionChannel, 0 );
queryRequest.address.SetPhysicalAddress( 0 );
queryRequest.type = READ;
queryRequest.owner = this;
NVMObject *curObject = NULL;
FindModuleChildType( &queryRequest, SubArray, curObject, parent->GetTrampoline( ) );
SubArray *promotionChannelSubarray = NULL;
promotionChannelSubarray = dynamic_cast<SubArray *>( curObject );
assert( promotionChannelSubarray != NULL );
Params *p = promotionChannelSubarray->GetParams( );
promotionChannelParams = p;
totalPromotionPages = p->RANKS * p->BANKS * p->ROWS;
currentPromotionPage = 0;
if( p->COLS != numCols )
{
std::cout << "Warning: Page size of fast and slow memory differs." << std::endl;
}
queriedMemory = true;
}
/*
* From the current promotion page, simply craft some translated address together
* as the victim address.
*/
uint64_t victimRank, victimBank, victimRow, victimSubarray, subarrayCount;
ncounter_t promoPage = currentPromotionPage;
victimRank = promoPage % promotionChannelParams->RANKS;
promoPage >>= NVM::mlog2( promotionChannelParams->RANKS );
victimBank = promoPage % promotionChannelParams->BANKS;
promoPage >>= NVM::mlog2( promotionChannelParams->BANKS );
subarrayCount = promotionChannelParams->ROWS / promotionChannelParams->MATHeight;
victimSubarray = promoPage % subarrayCount;
promoPage >>= NVM::mlog2( subarrayCount );
victimRow = promoPage;
victim.SetTranslatedAddress( victimRow, 0, victimBank, victimRank, promotionChannel, victimSubarray );
uint64_t victimAddress = at->ReverseTranslate( victimRow, 0, victimBank, victimRank, promotionChannel, victimSubarray );
victim.SetPhysicalAddress( victimAddress );
currentPromotionPage = (currentPromotionPage + 1) % totalPromotionPages;
}