Re: [PATCH 00/15] KVM: x86: fully implement vMTRR

From: Xiao Guangrong
Date: Sat May 30 2015 - 07:02:45 EST




On 05/30/2015 06:59 PM, Xiao Guangrong wrote:
Currently guest MTRR is completely prohibited if cache snoop is supported on
IOMMU (!noncoherent_dma) and host does the emulation based on the knowledge
from host side, however, host side is not the good point to know
what the purpose of guest is. A good example is that pass-throughed VGA
frame buffer is not always UC as host expected

Besides that, there is a bugs in current code if noncoherent_dma is detected:
KVM still uses hugepage even if the 4K pages in that ranges span between
on different memory types and uses the cache type of first page to do memory
mapping

This patchset enables full MTRR virtualization and currently only works on
Intel EPT architecture

There is the code will be attached in this thread is used to check the effect
of this patchset:
[12834.843439] __INIT__.
[12835.909109] Running in preload MTRR_TYPE_UNCACHABLE 4753900 ns.
[12835.915232] Running in MTRR_TYPE_UNCACHABLE 5257522 ns.
[12838.083948] Running in preload MTRR_TYPE_WRBACK 13266 ns.
[12838.084998] Running in MTRR_TYPE_WRBACK 13099 ns.


Has been attached in this mail.
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/ioctl.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <linux/uaccess.h>

#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif

//#define DEBUG

#ifdef DEBUG
#define dprintk(fmt, args...) \
printk(fmt, ##args)
#define debug_show_mtrr() show_mtrr()
#else
//#define pr_debug(format, ...) do {} while (0)
#define dprintk(fmt, ...) do {} while (0)
#define debug_show_mtrr() do {} while (0)
#endif

MODULE_LICENSE("GPL");

#define NR_PAGES_ORDER 1

static u64 __do_write(struct page *page, int page_nr)
{
void *addr = page_address(page);
u64 total = 0;
int i;

for (i = 0; i < PAGE_SIZE * page_nr; i++) {
((char *)addr)[i] = (char)0x99;
}

return total;
}

static u64 __do_read(struct page *page, int page_nr)
{
void *addr = page_address(page);
u64 total = 0;
int i;

for (i = 0; i < PAGE_SIZE * page_nr; i++) {
total += ((char *)addr)[i];
if (((char *)addr)[i] != (char)0x99) {
printk("read failed %x expected 0x99.\n", ((char *)addr)[i]);
break;
}
}

return total;
}

static void do_rw(struct page *page, int page_nr, const char *cachetype)
{
ktime_t start, end;
u64 total;

start = ktime_get();
__do_write(page, page_nr);
total = __do_read(page, page_nr);
end = ktime_get();
printk("Running in %s %lld ns.\n", cachetype,
ktime_to_ns(ktime_sub(end, start)));
}

static int add_region_to_mtrr(struct page *page, int page_nr, int type,
const char *cachetype)
{
unsigned long pfn;
int ret;

pfn = page_to_pfn(page);

ret = mtrr_add(pfn << PAGE_SHIFT, PAGE_SIZE * page_nr, type, 1);
if (ret < 0)
printk("mtrr_add %s failed, return %d.\n", cachetype, ret);

dprintk("%s add region %lx nrpage %d.\n", cachetype, pfn << PAGE_SHIFT, page_nr);
return ret;
}

static int del_region_from_mtrr(int entry, struct page *page, int page_nr, const char *cachetype)
{
unsigned long pfn;
int ret;

pfn = page_to_pfn(page);

ret = mtrr_del(entry, pfn << PAGE_SHIFT, PAGE_SIZE * page_nr);
if (ret < 0)
printk("mtrr_del %s failed, return %d.\n", cachetype, ret);
dprintk("%s del region %lx nrpage %d.\n", cachetype, pfn << PAGE_SHIFT, page_nr);
return ret;
}

static int memory_rw(void)
{
struct page *page;
int page_order, page_nr, ret;

page_order = NR_PAGES_ORDER;
page_nr = 1 << page_order;

page = alloc_pages(GFP_KERNEL, page_order);
if (!page)
return -ENOMEM;

ret = add_region_to_mtrr(page, page_nr, MTRR_TYPE_UNCACHABLE,
"MTRR_TYPE_UNCACHABLE");
if (ret < 0)
goto exit;

do_rw(page, page_nr, "preload MTRR_TYPE_UNCACHABLE");
do_rw(page, page_nr, "MTRR_TYPE_UNCACHABLE");

ret = del_region_from_mtrr(ret, page, page_nr, "MTRR_TYPE_UNCACHABLE");
if (ret < 0)
goto exit;

ret = add_region_to_mtrr(page, page_nr, MTRR_TYPE_WRBACK,
"MTRR_TYPE_WRBACK");
if (ret < 0)
goto exit;

do_rw(page, page_nr, "preload MTRR_TYPE_WRBACK");
do_rw(page, page_nr, "MTRR_TYPE_WRBACK");

ret = del_region_from_mtrr(ret, page, page_nr, "MTRR_TYPE_WRBACK");
if (ret < 0)
goto exit;

ret = 0;
exit:
__free_pages(page, page_order);
return ret;

}

static int __init my_init(void)
{
printk("__INIT__.\n");

dprintk("######### INIT MTRR...\n");

return memory_rw();
}

static void __exit my_exit(void)
{
dprintk("###### EXIT MTRR...\n");
return;
}

module_init(my_init)
module_exit(my_exit)