Linux kernel IOMMU代码分析笔记(12)——page-table entry的相关代码定义

DMA请求的地址转换如下图所示:

1

page-table entry格式如下:

2

因为每个page-table entry82^3)个byte,所以上面的转化图中只要9位就可以了(12 - 32^12 = 4KiB)。

GAW的定义:

Guest Address Width: Physical addressability limit within a partition (virtual machine)

可以理解为从虚拟机角度看到的物理地址宽度。举个例子,如果一个虚拟机只能访问2G内存,那么GAW就是31

AGAW的定义:Adjusted Guest Address Width。为了保证9bit长度的步长转化,GAWAGAW之间的转换伪代码如下:

R = (GAW - 12) MOD 9;
if (R == 0) {
    AGAW = GAW;
} else {
    AGAW = GAW + 9 - R;
}
if (AGAW > 64)
    AGAW = 64;

对应的函数是guestwidth_to_adjustwidth

static inline int guestwidth_to_adjustwidth(int gaw)
{
    int agaw;
    int r = (gaw - 12) % 9;

    if (r == 0)
        agaw = gaw;
    else
        agaw = gaw + 9 - r;
    if (agaw > 64)
        agaw = 64;
    return agaw;
}

AGAW的最小长度是30bit,参考以下规范定义(context-entry格式里的内容):

• 000b: 30-bit AGAW (2-level page table)
• 001b: 39-bit AGAW (3-level page table)
• 010b: 48-bit AGAW (4-level page table)
• 011b: 57-bit AGAW (5-level page table)
• 100b: 64-bit AGAW (6-level page table)

所以可以看到kernelagaw的一些转换代码会用到302这些数字:

static inline int agaw_to_level(int agaw)
{
    return agaw + 2;
}

static inline int agaw_to_width(int agaw)
{
    return min_t(int, 30 + agaw * LEVEL_STRIDE, MAX_AGAW_WIDTH);
}

static inline int width_to_agaw(int width)
{
    return DIV_ROUND_UP(width - 30, LEVEL_STRIDE);
}

 

Git数据模型笔记

Git object数据是一个有向无环图,即从任何一个commit出发都可以遍历其任何的parent,但绝不会有环。每个commit都指向一个tree,而一个tree则指向了一个或多个tree和(或)blob

Git数据模型如下图所示:

1

以下面目录结构为例:

2

工作目录下包含了两个目录和三个文件。初始化的git数据模型如下:

3

当修改lib/base/base_include.rb这个文件并提交以后,会产生一个新的blob以及相应的新tree。在当前commit打出tag以后,git数据模型如下:

4

修改根目录下init.rb文件提交后的git数据模型:

5

可以看到,每次commit都会产生一个新的tree
最后的git数据模型如下图所示,包含了16个不可改变的object

6

 

 

参考资料:
Git internals

Lua笔记(22)—— error和assert

Lua中的error函数定义:

error (message [, level])

打印出message后,会终止程序运行。关于level的含义,参考下面例子理解会更清楚(test.lua):

function f0()
        error("Error!")
end

function f1()
        f0()
end

function f2()
        f1()
end

f2()

执行如下:

lua: test.lua:2: Error!
stack traceback:
        [C]: in function 'error'
        test.lua:2: in function 'f0'
        test.lua:6: in function 'f1'
        test.lua:10: in function 'f2'
        test.lua:13: in main chunk
        [C]: in ?

默认情况下,level值为1。“lua: test.lua:2: Error!”把错误位置指向了脚本的第2行。把f0函数修改一下:

function f0()
        error("Error!")
end

再次执行,输出如下:

lua: test.lua:6: Error!
stack traceback:
        [C]: in function 'error'
        test.lua:2: in function 'f0'
        test.lua:6: in function 'f1'
        test.lua:10: in function 'f2'
        test.lua:13: in main chunk
        [C]: in ?

这次错误位置指向了脚本的第6行(“lua: test.lua:6: Error!”),也就是f1()函数,可以看到level指定了发生错误时,应该输出函数调用栈的哪一级函数。

assert函数定义如下:

assert (v [, message])

v是假(nilfalse)时,调用error函数,否则返回所有所有参数。其中message默认值是"assertion failed!"。举例如下:

function f0()
        assert(nil, "Assert!")
end
f0()

输出如下:

lua: test.lua:2: Assert!
stack traceback:
        [C]: in function 'assert'
        test.lua:2: in function 'f0'
        test.lua:13: in main chunk
        [C]: in ?

Lua笔记(21)—— “require module”的等价形式

Learn Lua in 15 Minutes中提到,require module的等价形式:

-- Another file can use mod.lua's functionality:
local mod = require('mod')  -- Run the file mod.lua.

-- require is the standard way to include modules.
-- require acts like:     (if not cached; see below)
local mod = (function ()
  <contents of mod.lua>
end)()
-- It's like mod.lua is a function body, so that
-- locals inside mod.lua are invisible outside it.

这个解释很好地说明了require module的原理,让人豁然开朗。

DMA Remapping —— Domain

Domain是平台上一个抽象的隔离环境,并且被分配了一块主机物理内存。I/O设备作为domain的指定设备(assigned device),可以访问分配给domain的内存。在虚拟化环境下,每个虚拟机都会被当做一个独立的domain

I/O设备分配到指定的domain,并只能访问指定domain所拥有的物理资源。依赖于具体的软件模型,DMA请求的地址可以是虚拟机,也就是domainGuest-Physical AddressGPA),或是由PASID指定进程定义的application Virtual AddressVA),或是由软件定义的抽象的I/O virtual addressIOVA)。不管哪种情况,DMA Remapping硬件都是把相应的地址翻译成Host-Physical AddressHPA)。

什么是KVM?

KVMKernel-based Virtual Machine)是LinuxX86平台提供的完整虚拟化(virtualization) 解决方案,这个方案包括了虚拟化扩展(Intel VTAMD-V)。KVM提供了一个包含虚拟化核心功能可加载的内核模块:kvm.ko,另外还有和处理器相关的模块:kvm-intel.kokvm-amd.ko

利用KVM,你可以创建多个运行LinuxWindows的虚拟机,每个虚拟机都有自己私有的虚拟硬件:网卡,磁盘,等等。

KVM用户空间的组件被包含进入了QEMU的主线。

参考资料:
Kernel Virtual Machine

Lua笔记(20)—— 编译

loadfiledofile都会从文件中加载chunkloadfile只编译这段chunk,并且把编译好的chunk以函数的形式返回,但不运行,而dofile会运行这段chunk。此外,出现错误时,dofileraise error,而loadfile会返回错误代码dofile的代码类似这样:

function dofile (filename)
    local f = assert(loadfile(filename))
    return f()
end

load函数(Lua 5.1使用loadstring)与loadfile类似,只不过load是从字符串中加载chunk,而不是从文件中。需要注意的是,load加载的是chunk,也就是语句,不是表达式。如果要求表达式值的话,需要在前面加上return,这样就得到一个返回表达式值的语句。

Lua会把任何独立的chunk作为一个有变参参数的匿名函数的函数体。举个例子:load('a=1')返回等价于下面的表达式:

function (...) a = 1 end
 

Lua笔记(19)—— 表达式(expression)和语句(statement)

Lua中的表达式(expression)定义:

Expressions denote values. Expressions in Lua include the numeric constants and string literals, variables, unary and binary operations, and function calls. Expressions include also the unconventional function definitions and table constructors.

表达式产生值,包括:数字常量,字符串,变量,单目和双目运算,另外还有函数调用。此外,表达式还包括函数定义和创建table

Lua中的语句(statement)定义:

Lua supports an almost conventional set of statements, similar to those in C or Pascal. The conventional statements include assignment, control structures, and procedure calls. Lua also supports some not so conventional statements, such as multiple assignments and local variable declarations.

语句包括:赋值,控制结构,过程调用(block),多重赋值和local变量定义。

Go语言实践技巧(8)——channel类型

声明channel时,<-表明方向:

chan T          // 能收发`T`类型变量
chan<- float64  // 只能发送 float64 类型变量 (write-only)
<-chan int      // 只能接收 int 类型变量 (read-only)

<-同最左边的channel结合:

chan<- chan int    // 同 chan<- (chan int)
chan<- <-chan int  // 同 chan<- (<-chan int)
<-chan <-chan int  // 同 <-chan (<-chan int)

参考资料:
How to understand “<-chan” in declaration?

Lua笔记(18)—— 解释器的交互模式

Lua自带解释器(Lua.c编译生成的可执行文件)的交互模式会把每一单独的输入行看做是一个chunk,除非这一行不是完整的命令,例如:

> function test()
>> print("test")
>> end

>>标示这一行和之前的行属于同一行。

因此,如果在一行输入local i = 1Lua就会运行这个chunk。再另起一行输入新命令以后,相当于一个新的chunk,前一行的local变量是不可见的。举例如下:

> local i = 10
> print(i)
nil

解决这个问题可以使用do-end将这个代码包成一个chunk

> do
>> local i = 10
>> print(i)
>> end
10