tdp_page_fault 函数解析之level,gfn变量的含义

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leoufung/article/details/52638357

首先感谢Intel OTC的 wufeng、OenHan、chenhe、ruanshuai给予的帮助和支持。其实我就是各位大牛的笔和嘴偷笑


问题综述

tdp_page_fault 函数是虚拟机发生 EPT voilation的处理函数,完成EPT表项的建立,最不好理解的就是gaddr变量、level变量、gfn变量的含义。本文就将根据主要部分,对各个变量的含义,作用,数据结构加以分析。在详细的分析代码之前,我们介绍一些背景知识,再对代码内容加以分析


一、地址空间

在虚拟化中,GUEST认为自己拥有从0开始的完整物理地址空间,叫做GUEST物理地址空间, GPA,GPA中含所有RAM、ROM或MMIO等,这些就是qemu中的address space;

将GUEST地址空间按照页面大小分割,每个单元叫做一个GUEST物理页框,每个页框有个编号,就像图书的页号一样,记为GFN。

与此同时HOST真正拥有物理地址空间(HPA),同样将其按照页框分割,每个页框的编号几位HFN或PFN。

当分配出一个真正的物理页面时,page的时候,page的起始地址可以用PFN表示;因为虚拟机真实的使用这个物理页面,GFN和PFN就一个转换关系,这就是影子页表(或EPT)完成的映射


二、影子页表逻辑结构

1.  在未开启大页的情况下,64位机器上,影子页表是4级结构,非叶子几点表的表项指向下一级页表基地址,如绿色所示;叶子节点表的表项指向一个真正的物理页面,如下图所示:



最开始的根表的级别是level = 4,其次是level = 3, 一直到level = 1的表,level1表的表项指向4K的真实物理页面(当然这里只是分配了HPA,还需要配合HOST上的PF分配真实物理页面);

level4表就是影子页表(EPT)的根表,其物理地址就被记录在VMCS的EPT Pointer中,每个VCPU都有一个EPT Pointer,也就是每个VCPU都有自己的MMU和一套页表。

gaddr就是发生EPT voilation的guest物理地址,GFN就是gaddr对应的页框号,转换公式如下 gfn = gaddr / PAGE_SIZE。gfn是1的倍数。位置关系如上图所示  



2.  在开启大页的情况下,64位机器上,这里以2M大页,影子页表3级结构为例说明,如下图所示:


在这里,叶子表变成了level 2表;其他的同普通影子页表相同;

在2M大页的情况下,一个大页内可以包含512个小页。

在KVM的代码中通过KVM_PAGES_PER_HPAGE(level)宏可以获得,level代表了第几级页表是叶子节点页表。

/*
 * level-1级叶子页表所管理的页面 是 level 级叶子页表所管理的页面大小的 2^9倍。
 * KVM_HPAGE_GFN_SHIFT 表示以level级页表为叶子页表的情况下,所管理的页面是标准页面的多少倍
 */
#define KVM_HPAGE_GFN_SHIFT(x)	(((x) - 1) * 9) 
#define KVM_HPAGE_SHIFT(x)	(PAGE_SHIFT + KVM_HPAGE_GFN_SHIFT(x)) /*基本页面大小是PAGE_SHIFT,KVM_HPAGE_GFN_SHIFT(x)是倍数关系*/
#define KVM_HPAGE_SIZE(x)	(1UL << KVM_HPAGE_SHIFT(x)) /*当前所管理的页面大小 */
#define KVM_HPAGE_MASK(x)	(~(KVM_HPAGE_SIZE(x) - 1))
#define KVM_PAGES_PER_HPAGE(x)	(KVM_HPAGE_SIZE(x) / PAGE_SIZE) /*当前所管理的页面,是标准页面的多少倍*/

gaddr就是发生EPT voilation的guest物理地址,  fn = gaddr / PAGE_SIZE,这里的FN是标准页面情况下,gaddr的页框号;如果一个大页面中含有512个标准页面的话,大页面的起始页框号就应该是512的整倍数,如0,512,1024等。对FN向下取元整就可以得到大页面的起始页帧号。如下图


举个例子,如果 fn = 513,则gfn = 512; 如果 fn = 511,gfn = 0;

转换为数学公式如下:

a = a & ~(b-1) ;就是A对B去元整,得到的是B的整倍数。 如  5 & ~(4-1) = 4; 5 & ~(8-1) = 0

这在我们的代码里面也是有体现的

	gfn_t gfn = gpa >> PAGE_SHIFT; 
......
	level = mapping_level(vcpu, gfn);
	gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
......
KVM_PAGES_PER_HPAGE(level) 按照上面的分析,就是大页是普通页面多少倍,也就是对其关系;那么这里计算出来的gfn就是按照大页倍数对齐后的起始gfn编号。


三,代码分析

tdp_page_fault的关键代码如下

static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
			  bool prefault)
{
	gfn_t gfn = gpa >> PAGE_SHIFT; 
......
	force_pt_level = mapping_level_dirty_bitmap(vcpu, gfn);
	if (likely(!force_pt_level)) {
		level = mapping_level(vcpu, gfn);
		gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
	} else
		level = PT_PAGE_TABLE_LEVEL;

	if (fast_page_fault(vcpu, gpa, level, error_code))
		return 0;
......
	if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable))
		return 0;
......

	/*调用__direct_map函数进行EPT映射*/
	r = __direct_map(vcpu, gpa, write, map_writable,
			 level, gfn, pfn, prefault);

......
}

1. 首先根据gpa计算出标准页面情况下的gfn

2. 得到level,level就是影子页表中,level级页表作为叶子页表,其目录项指向页面(pfn)。

2. 根据gfn和gpa,在memslot中查找,得到qemu中分配页面的HVA,在通过__get_user_pages_fast得到这个HVA页面的pfn(当然这只是一个PFN,还需要PF完成真正页面的分配)

3. __direct_map 完成EPT页表的构造,并在最后一级页表项中将gfn同pfn映射起来


__direct_map 函数中关于gfn、level的内容将会在后面的文章进行介绍


展开阅读全文

看看这个函数含义

05-23

[code=PHP]rn$g = array(rn "varrun_path" => "/var/run",rn "varetc_path" => "/var/etc",rn "vardb_path" => "/var/db",rn "varlog_path" => "/var/log",rn "etc_path" => "/etc",rn "tmp_path" => "/tmp",rn "conf_path" => "/conf",rn "ftmp_path" => "/ftmp",rn "conf_default_path" => "/conf.default",rn "cf_path" => "/cf",rn "cf_conf_path" => "/cf/conf",rn "www_path" => "/usr/local/www",rn "xml_rootobj" => "freenas",rn "debug" => false,rn "latest_config" => "5.7",rn "nopccard_platforms" => array("wrap", "net48xx"),rn "wireless_regex" => "/^(ndis|wi|ath|an|ral|ural|wai|iwi|awi|wlan|ipw)/",rn "default_passwd" => "freenas",rn "default_ip" => "192.168.1.250"rn);rnrnfunction mwexec($command, $logerr = false) rn global $g;rnrn // Set PATHrn putenv("PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin");rnrn if ($g['debug']) rn if (!$_SERVER['REMOTE_ADDR'])rn echo "mwexec(): $command\n";rn passthru($command, $retval);rn else rn $redirect = ">/dev/null 2>&1";rnrn if (true === $logerr)rn $redirect = "2>&1 >/dev/null";rnrn exec("$command $redirect", $output, $retval);rn if ((true === $logerr) && (is_array($output))) rn write_log(implode($output));rn rn rnrn return $retval;rnrnrnfunction mwexec2($command, &$output = NULL, &$return_var = NULL) rn // Set PATHrn putenv("PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin");rn return exec($command, $output, $return_var);rnrnrn/* wrapper for exec() in background */rnfunction mwexec_bg($command) rn global $g;rnrn // Set PATHrn putenv("PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin");rnrn if ($g['debug']) rn if (!$_SERVER['REMOTE_ADDR'])rn echo "mwexec(): $command\n";rn rnrn exec("nohup $command > /dev/null 2>&1 &");rnrnrn/* unlink a file, if it exists */rnfunction unlink_if_exists($fn) rn if (file_exists($fn))rn unlink($fn);rnrn[/code]rn哪位高手帮忙看下这几个函数到底有什么作用 有什么含义呀 论坛

没有更多推荐了,返回首页