您是第 位访客

disksim-3.0 with flashsim 源码分析(五):写操作

flashsim中读写操作的函数调用流程是: callFsim() –> send_flash_request() –> opm_write() –> nand_page_write()

callFsim(): 主要解决的是缓冲区中映射表的查找和替换。
send_flash_request(): 通过判断当前使用的是什么类型的FTL,然后将读写请求发送到该 FTL 中去处理。
opm_write(): FTL 中的读写操作,
nand_page_write(): 最底层的读写操作的实现。

下面以 DFTL 为例,介绍下dftl中的写操作。在 dflt.c 中声明了两个全局变量 free_blk_no[]free_page_no[],两个数组的大小都是2,含义如下:

free_blk_no[0]:当前正在写的用户数据,写入到哪个block中
free_blk_no[1]:当前正在写的映射表,写入到哪个block中
free_page_no[0]:下一个将要写的用户数据,写入到哪个page中
free_page_no[1]:下一个将要写的映射表,写入到哪个page中

所以,当我们要写入一个页的用户数据时,由 free_blk_no[0] 和 free_page_no[0] 可以确定该数据写入到哪个物理页上。当我们要写入一个映射页到闪存上时,由 free_blk_no[1] 和 free_page_no[1] 可以确定该映射页写入到哪个物理映射页上。

opm_write()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// lsn表示起始的logicial sector number
// size表示写请求的大小,单位是sector,通常一次只能写一个page(8个sector)
// mapdir_flag,表示写映射表还写数据
size_t opm_write(sect_t lsn, sect_t size, int mapdir_flag){
int i;
int lpn = lsn/SECT_NUM_PER_PAGE; // logical page number
int size_page = size/SECT_NUM_PER_PAGE; // size in page
int ppn;
int small;
sect_t lsns[SECT_NUM_PER_PAGE]; // lsns[] 保存的是将要对哪些 logical sector number 进行写入
int sect_num = SECT_NUM_PER_PAGE;
sect_t s_lsn; // starting logical sector number
sect_t s_psn; // starting physical sector number
sect_t s_psn1;
ASSERT(lpn < opagemap_num);
ASSERT(lpn + size_page <= opagemap_num);
s_lsn = lpn * SECT_NUM_PER_PAGE; // start logical sector number
if(mapdir_flag == 2) //要对 map page 进行写入
small = 0;
else if ( mapdir_flag == 1) //要对 data page 进行写入
small = 1;
else{
printf("something corrupted");
exit(0);
}
if (free_page_no[small] >= SECT_NUM_PER_BLK){ //当前block已经写完了
if ((free_blk_no[small] = nand_get_free_blk(0)) == -1){
while (free_blk_num < min_fb_num ){ //需要进行垃圾回收
opm_gc_run(small, mapdir_flag);
}
opm_gc_get_free_blk(small, mapdir_flag);
}
else {
free_page_no[small] = 0; //从新的一个blkno开始写
}
}
memset (lsns, 0xFF, sizeof (lsns));
s_psn = SECTOR(free_blk_no[small], free_page_no[small]); // start logical sector number
if(s_psn % SECT_NUM_PER_PAGE != 0){ //必须按页对齐(4KB)
printf("s_psn: %d\n", s_psn);
}
ppn = s_psn / SECT_NUM_PER_PAGE; //physical page number
if (opagemap[lpn].free == 0) { //要写入的地址已经存在数据
s_psn1 = opagemap[lpn].ppn * SECT_NUM_PER_PAGE;
for(i = 0; i<SECT_NUM_PER_PAGE; i++){
nand_invalidate(s_psn1 + i, s_lsn + i); //将原来的数据标记为无效
}
nand_stat(3); //将原来的数据标记为无效需要一次 write oob 操作
}
else {
opagemap[lpn].free = 0; // 要写入的地址不存在数据
}
for (i = 0; i < SECT_NUM_PER_PAGE; i++) {
lsns[i] = s_lsn + i;
}
if(mapdir_flag == 2) { // 更改映射表
mapdir[lpn].ppn = ppn;
opagemap[lpn].ppn = ppn;
}
else { // 更改映射表
opagemap[lpn].ppn = ppn;
}
free_page_no[small] += SECT_NUM_PER_PAGE;
nand_page_write(s_psn, lsns, 0, mapdir_flag); //发送到下一层写入
return sect_num;
}

nand_page_write()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
_u8 nand_page_write(_u32 psn, _u32 *lsns, _u8 isGC, int map_flag){
blk_t pbn = BLK_F_SECT(psn); // physical sector number(psn) to physical block number(pbn)
_u16 pin = IND_F_SECT(psn); // sector index, page index is the same as sector index
int i, valid_sect_num = 0;
if(pbn >= nand_blk_num){
printf("break !\n");
}
ASSERT(pbn < nand_blk_num);
ASSERT(OFF_F_SECT(psn) == 0);
if(map_flag == 2) {
nand_blk[pbn].page_status[pin/SECT_NUM_PER_PAGE] = 1; //map table
}
else{
nand_blk[pbn].page_status[pin/SECT_NUM_PER_PAGE] = 0; // data
}
for (i = 0; i <SECT_NUM_PER_PAGE; i++) {
if (lsns[i] != -1) {
if(nand_blk[pbn].state.free == 1) { //写入的block必须是空闲的
printf("blk num = %d",pbn);
}
ASSERT(nand_blk[pbn].sect[pin + i].free == 1); //写入的sector必须是空闲
nand_blk[pbn].sect[pin + i].free = 0; //标记为非空闲
nand_blk[pbn].sect[pin + i].valid = 1; //标记为有效数据
nand_blk[pbn].sect[pin + i].lsn = lsns[i]; //记录映射
nand_blk[pbn].fpc--; //free page(sector) counter
nand_blk[pbn].lwn = pin + i; //last written sector number
valid_sect_num++;
}
else{
printf("lsns[%d] do not have any lsn\n", i);
}
}
ASSERT(nand_blk[pbn].fpc >= 0);
if (isGC) {
nand_stat(GC_PAGE_WRITE);
} else {
nand_stat(PAGE_WRITE);
}
return valid_sect_num;
}

nand_get_free_blk()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// isGC 表示是否在GC过程中调用这个函数
_u32 nand_get_free_blk (int isGC) {
_u32 blk_no = -1, i;
int flag = 0,flag1=0;
flag = 0; //是否找到一个擦写次数最少的空闲块的标志
flag1 = 0; //是否找到一个空闲块的标志
MIN_ERASE = 9999999;
if ((isGC == 0) && (free_blk_num <= min_fb_num)) { //空闲块数量低于阈值
return -1; // 需要进行 GC
}
for(i = 0; i < nand_blk_num; i++){ //寻找一个擦写次数最少的空闲block
if (nand_blk[i].state.free == 1) {
flag1 = 1;
if ( nand_blk[i].state.ec < MIN_ERASE ) {
blk_no = i;
MIN_ERASE = nand_blk[i].state.ec;
flag = 1;
}
}
}
if(flag1 != 1){ // have no free block
printf("no free block left=%d",free_blk_num);
ASSERT(0);
}
if ( flag == 1) {
flag = 0;
ASSERT(nand_blk[blk_no].fpc == SECT_NUM_PER_BLK);
ASSERT(nand_blk[blk_no].ipc == 0);
ASSERT(nand_blk[blk_no].lwn == -1);
nand_blk[blk_no].state.free = 0;
free_blk_idx = blk_no; //好像并没什么用处,后面并没有使用此变量
free_blk_num--;
return blk_no;
}
else{
printf("shouldn't reach...\n");
}
return -1;
}