这里我们继续前天没测完的一些猜想:
Last Blog>>>>
c. Write/Read with meta. 这里的meta是啥东西?
一个猜想:OCSSD里面一个sector存储单元(storage unit)配套存在一个meta存储单元. 带外数据(out-of-bound data)可能指的就是这样的每个寻址单位其对应的一小块字节区.
d. Access mode: Sngl/Dual/Quad
一个猜想:由于之前推断出的结构,加上这个测试示范Dual和Quad 2个模式都是在nplane大于相应的个数时才做的(Dual:2, Quad:4),很可能这里的access mode是单次I/O同时发向不同的plane,可以使I/O并发.
1.write/read未经过align的内存区域会如何?
貌似不会出什么问题,但还是尽量遵守.(test_write_no_align
)
2.R/W with meta
经过测试,由于这的环境是一个page只有一个sector(geo->nsectors == 1),这里至少可以确定metadata是与page一一对应的(是否与sector一一对应还有待测试). (test_write_read_meta
)
3.access mode
经过测试可以得知, access mode实际上就是达到一个命令(nvme_cmd)同时写多个plane的效果.
以DUAL为例,就是『有机会』一次读/写同时操作同一个lun上的2个plane,
为啥说有机会,先看下面的写API:
ssize_t nvm_addr_write(struct nvm_dev *dev,
struct nvm_addr addrs[], //要写的sector的地址
int naddrs, //写多少个sector,
const void *data, const void *meta, //要写的内容
uint16_t flags, //可能的值 : NVM_FLAG_PMODE_SNGL,NVM_FLAG_PMODE_DUAL, NVM_FLAG_PMODE_QUAD
struct nvm_ret *ret)
这里地址数组中提供的地址可能与flags(access mode)并没有啥关系,例如,DUAL模式,但要写入的地址却都在某一个plane上,这里的处理可能需要到qemu里查看是怎么处理的(SDK固件代码应该是类似的)。
稍微推理一下应该就是对所有的地址序列作一个预处理,看哪些地址可以以大于1的模式向下发.当然DUAL和QUAD应该是『有机会』性能上较好.
test_dual_plane_w_r_multi_2_2,
test_dual_plane_w_r_multi_3_1,
test_dual_plane_w_r_multi_4_0,
test_dual_plane_w_r_multi_4_0_f
#include
#include
#include
#include
static char nvm_dev_path[NVM_DEV_PATH_LEN] = "/dev/nvme0n1";
static struct nvm_dev *dev;
static const struct nvm_geo *geo;
static struct nvm_addr a0;
static int channel = 0;
static int lun = 0;
static int plane = 0;
static int block = 10;
#define STRLEN 20
#define METALEN 16
static char Str[STRLEN] = "OCSSD Test";
static char Meta[METALEN] = "I am Meta";
static char Meta2[METALEN] = "I am Meta2";
static void *Buf_for_1_read = NULL; //Buf for reading 1 sector
static void *Buf_for_1_meta_read = NULL; // Buf for reading 1 metadata
#define FAIL_ERASE do{ printf("Erase Failed\n"); }while(0)
#define FAIL_ALLOC do{ printf("Alloc Failed\n"); }while(0)
#define FAIL_WRITE do{ printf("Write Failed\n"); }while(0)
#define FAIL_READ do{ printf("Read Failed\n"); }while(0)
#define ADDR_INVALID do{ printf("Addr Invalid\n"); }while(0)
#define THE_SAME_IO do{ printf("IO Same\n"); }while(0)
#define NOT_THE_SAME_IO do{ printf("IO Not Same\n"); }while(0)
#define THE_SAME_META do{ printf("META Same\n"); }while(0)
#define NOT_THE_SAME_META do{ printf("META Not Same\n"); }while(0)
int setup(void)
{
dev = nvm_dev_open(nvm_dev_path);
if (!dev) {
perror("nvm_dev_open");
return -1;
}
geo = nvm_dev_get_geo(dev);
a0.ppa = 0;
a0.g.ch = channel;
a0.g.lun = lun;
a0.g.pl = plane;
a0.g.blk = block; //only blk addr
Buf_for_1_read = nvm_buf_alloc(geo, 2 * geo->sector_nbytes);
if (!Buf_for_1_read) {
FAIL_ALLOC;
return -1;
}
Buf_for_1_meta_read = nvm_buf_alloc(geo, 2 * geo->meta_nbytes);
if (!Buf_for_1_meta_read) {
FAIL_ALLOC;
return -1;
}
return 0;
}
int teardown(void)
{
nvm_dev_close(dev);
if ( Buf_for_1_read ) {
free(Buf_for_1_read);
}
if ( Buf_for_1_meta_read) {
free(Buf_for_1_meta_read);
}
return 0;
}
uint64_t alignblk(struct nvm_addr addr)
{
struct nvm_addr alg;
alg.ppa = addr.ppa;
alg.g.pg = 0;
alg.g.sec = 0;
return alg.ppa;
}
uint64_t Get_addr_for_test()
{
struct nvm_addr addr;
addr.ppa = a0.ppa;
addr.g.pg = 1;
addr.g.sec = 0;
return addr.ppa;
}
void My_pr_addr_cap(const char* str)
{
#define digestlen 8
char digest[digestlen];
strncpy(digest, str, digestlen);
digest[digestlen - 1] = '\0';
printf("%8s | %s | %s | %s | %-4s | %3s | %s \n",
digest,"ch", "lun", "pl", "blk", "pg", "sec");
}
void My_pr_nvm_addr(struct nvm_addr addr)
{
printf(" | %2d | %3d | %2d | %4d | %3d | %d\n",
addr.g.ch, addr.g.lun, addr.g.pl,
addr.g.blk, addr.g.pg, addr.g.sec);
}
void My_pr_addr_with_str(const char *str, struct nvm_addr x)
{
My_pr_addr_cap(str);
My_pr_nvm_addr(x);
}
void EraseNpl_1Blk(struct nvm_addr wh)//
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
const int npl = geo->nplanes;
struct nvm_addr whichblk[npl];
for(int i = 0; i < npl; ++i){
whichblk[i].ppa = alignblk(wh);
whichblk[i].g.pl = i;
}
res = nvm_addr_erase(dev, whichblk, npl, pmode, &ret);//Erase 1 block of all planes inside a lun.
if(res < 0){
FAIL_ERASE;
nvm_ret_pr(&ret);
}
}
//pmode = Single-plane, with or without meta
void Write_1Sector(struct nvm_addr wh, const char *IO, int use_meta, const char *Meta)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
void *bufptr = NULL, *bufptr_meta = NULL;
int addr_valid = nvm_addr_check(wh, geo);
if(addr_valid){
ADDR_INVALID;
nvm_bounds_pr(addr_valid);
goto OUT;
}
//buf setup
bufptr = nvm_buf_alloc(geo, geo->sector_nbytes);//sector size
if(!bufptr){
FAIL_ALLOC;
goto OUT;
}
memcpy(bufptr, IO, strlen(IO));
if(use_meta){
bufptr_meta = nvm_buf_alloc(geo, geo->meta_nbytes);
if(!bufptr_meta){
FAIL_ALLOC;
goto FREE_OUT1;
}
memcpy(bufptr_meta, Meta, geo->meta_nbytes);
}
//2. write
res = nvm_addr_write(dev, &wh, 1, bufptr,
use_meta ? bufptr_meta : NULL, pmode, &ret);//Write 1 sector
if(res < 0){
FAIL_WRITE;
}
free(bufptr_meta);
FREE_OUT1:
free(bufptr);
OUT:
if(res < 0){
nvm_ret_pr(&ret);
}
return;
}
void Write_2Sectors(struct nvm_addr wh, const char *IO1, const char *IO2, int use_meta, const char *Meta1, const char *Meta2)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
void *bufptr = NULL, *bufptr_meta = NULL;
int addr_valid = nvm_addr_check(wh, geo);
if(addr_valid){
ADDR_INVALID;
nvm_bounds_pr(addr_valid);
goto OUT;
}
struct nvm_addr whs[2];
whs[0].ppa = wh.ppa;
whs[1].ppa = wh.ppa;
whs[0].g.pl = 0;
whs[1].g.pl = 1;
printf("Write 2 Sectors SGNL:\n");
My_pr_addr_with_str("First", whs[0]);
My_pr_addr_with_str("Second", whs[1]);
//buf setup
bufptr = nvm_buf_alloc(geo, 2 * geo->sector_nbytes);//sector size
if(!bufptr){
FAIL_ALLOC;
goto OUT;
}
memcpy(bufptr, IO1, strlen(IO1));
memcpy(bufptr + 1 * geo->sector_nbytes, IO2, strlen(IO2));
if(use_meta){
bufptr_meta = nvm_buf_alloc(geo, 2 * geo->meta_nbytes);
if(!bufptr_meta){
FAIL_ALLOC;
goto FREE_OUT1;
}
memcpy(bufptr_meta, Meta1,
strlen(Meta1) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta1) );
memcpy(bufptr_meta + 1 * geo->meta_nbytes, Meta2,
strlen(Meta2) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta2) );
}
//2. write
res = nvm_addr_write(dev, whs, 2, bufptr,
use_meta ? bufptr_meta : NULL, pmode, &ret);//Write 1 sector
if(res < 0){
FAIL_WRITE;
}
free(bufptr_meta);
FREE_OUT1:
free(bufptr);
OUT:
if(res < 0){
nvm_ret_pr(&ret);
}
return;
}
//pmode = Dual-plane, with or without meta
void Write_Dual_Sector(struct nvm_addr wh, const char *IO1, const char *IO2, int use_meta, const char *Meta1, const char *Meta2)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_DUAL;
void *bufptr = NULL, *bufptr_meta = NULL;
int addr_valid = nvm_addr_check(wh, geo);
if(addr_valid){
ADDR_INVALID;
nvm_bounds_pr(addr_valid);
goto OUT;
}
struct nvm_addr whs[2];
whs[0].ppa = wh.ppa;
whs[1].ppa = wh.ppa;
whs[0].g.pl = 0;
whs[1].g.pl = 1;
printf("Write Dual Sectors:\n");
My_pr_addr_with_str("First", whs[0]);
My_pr_addr_with_str("Second", whs[1]);
//buf setup
bufptr = nvm_buf_alloc(geo, 2 * geo->sector_nbytes);//sector size
if(!bufptr){
FAIL_ALLOC;
goto OUT;
}
memcpy(bufptr, IO1, strlen(IO1));
memcpy(bufptr + 1 * geo->sector_nbytes, IO2, strlen(IO2));
if(use_meta){
bufptr_meta = nvm_buf_alloc(geo, 2 * geo->meta_nbytes);
if(!bufptr_meta){
FAIL_ALLOC;
goto FREE_OUT1;
}
memcpy(bufptr_meta, Meta1,
strlen(Meta1) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta1) );
memcpy(bufptr_meta + 1 * geo->meta_nbytes, Meta2,
strlen(Meta2) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta2) );
}
//2. write
res = nvm_addr_write(dev, whs, 2, bufptr,
use_meta ? bufptr_meta : NULL, pmode, &ret);//Write 1 sector
if(res < 0){
FAIL_WRITE;
}
free(bufptr_meta);
FREE_OUT1:
free(bufptr);
OUT:
if(res < 0){
nvm_ret_pr(&ret);
}
return;
}
void Read_1Sector(struct nvm_addr wh, int use_meta)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
res = nvm_addr_read(dev, &wh, 1, Buf_for_1_read,
use_meta ? Buf_for_1_meta_read : NULL, pmode, &ret);//only use the first sector_nbytes
if(res < 0){
FAIL_READ;
nvm_ret_pr(&ret);
}
}
void Read_Dual_Sector(struct nvm_addr wh, int use_meta)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
struct nvm_addr whs[2];
whs[0].ppa = wh.ppa;
whs[1].ppa = wh.ppa;
whs[0].g.pl = 0;
whs[1].g.pl = 1;
res = nvm_addr_read(dev, whs, 2, Buf_for_1_read,
use_meta ? Buf_for_1_meta_read : NULL, pmode, &ret);//use 2 * sector_nbytes
if(res < 0){
FAIL_READ;
nvm_ret_pr(&ret);
}
}
int MemCmp(unsigned char *a, unsigned char *b, int len)
{
for(int i = 0; i < len; ++i){
if(a[i] != b[i]){
return -1;
}
}
return 0;
}
void Test_Write_Dual_4_Sectors(struct nvm_addr whs[], const char *IO1, const char *IO2,
const char *IO3, const char *IO4,
int use_meta, const char *Meta1, const char *Meta2,
const char *Meta3, const char *Meta4)
{
char str[16];
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_DUAL;
void *bufptr = NULL, *bufptr_meta = NULL;
int addr_valid;
for(int i = 0; i < 4; i++){
addr_valid = nvm_addr_check(whs[i], geo);
if(addr_valid){
ADDR_INVALID;
nvm_bounds_pr(addr_valid);
goto OUT;
}
}
printf("Write Dual Sectors:\n");
for(int i = 0; i < 4; i++){
sprintf(str, "a:%d", i);
My_pr_addr_with_str(str, whs[i]);
}
//buf setup
bufptr = nvm_buf_alloc(geo, 4 * geo->sector_nbytes);//sector size
if(!bufptr){
FAIL_ALLOC;
goto OUT;
}
memcpy(bufptr, IO1, strlen(IO1));
memcpy(bufptr + 1 * geo->sector_nbytes, IO2, strlen(IO2));
memcpy(bufptr + 2 * geo->sector_nbytes, IO3, strlen(IO3));
memcpy(bufptr + 3 * geo->sector_nbytes, IO4, strlen(IO4));
if(use_meta){
bufptr_meta = nvm_buf_alloc(geo, 4 * geo->meta_nbytes);
if(!bufptr_meta){
FAIL_ALLOC;
goto FREE_OUT1;
}
memcpy(bufptr_meta, Meta1,
strlen(Meta1) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta1) );
memcpy(bufptr_meta + 1 * geo->meta_nbytes, Meta2,
strlen(Meta2) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta2) );
memcpy(bufptr_meta + 2 * geo->meta_nbytes, Meta3,
strlen(Meta3) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta3) );
memcpy(bufptr_meta + 3 * geo->meta_nbytes, Meta4,
strlen(Meta4) >= geo->meta_nbytes ? geo->meta_nbytes : strlen(Meta4) );
}
//2. write
res = nvm_addr_write(dev, whs, 4, bufptr,
use_meta ? bufptr_meta : NULL, pmode, &ret);//Write 1 sector
if(res < 0){
FAIL_WRITE;
}
free(bufptr_meta);
FREE_OUT1:
free(bufptr);
OUT:
if(res < 0){
nvm_ret_pr(&ret);
}
return;
}
void test_dual_plane_w_r_multi_4_0_f(void)
{
struct nvm_addr addr[4];
int plnum[4] = {0, 0, 0, 0};
int pgnum[4] = {0, 1, 2, 3};
for(int i = 0; i < 4; i++){
addr[i].ppa =Get_addr_for_test();
addr[i].g.pl = plnum[i];
addr[i].g.pg = pgnum[i];
}
struct nvm_addr addr2[4];
int plnum2[4] = {1, 1, 1, 1};
int pgnum2[4] = {0, 1, 2, 3};
for(int i = 0; i < 4; i++){
addr2[i].ppa =Get_addr_for_test();
addr2[i].g.pl = plnum2[i];
addr2[i].g.pg = pgnum2[i];
}
EraseNpl_1Blk(addr[0]);
Test_Write_Dual_4_Sectors(addr, "IO1", "IO2", "IO3", "IO4",
1, "Meta1", "Meta2", "Meta3", "Meta4");
for(int i = 0; i < 4; i++){
Read_1Sector(addr2[i], 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
}
}
void test_dual_plane_w_r_multi_4_0(void)
{
char str[16];
struct nvm_addr addr[4];
int plnum[4] = {0, 0, 0, 0};
int pgnum[4] = {0, 1, 2, 3};
for(int i = 0; i < 4; i++){
addr[i].ppa =Get_addr_for_test();
addr[i].g.pl = plnum[i];
addr[i].g.pg = pgnum[i];
}
EraseNpl_1Blk(addr[0]);
Test_Write_Dual_4_Sectors(addr, "IO1", "IO2", "IO3", "IO4",
1, "Meta1", "Meta2", "Meta3", "Meta4");
for(int i = 0; i < 4; i++){
Read_1Sector(addr[i], 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
}
}
void test_dual_plane_w_r_multi_3_1(void)
{
char str[16];
struct nvm_addr addr[4];
int plnum[4] = {0, 0, 0, 1};
int pgnum[4] = {0, 1, 2, 3};
for(int i = 0; i < 4; i++){
addr[i].ppa =Get_addr_for_test();
addr[i].g.pl = plnum[i];
addr[i].g.pg = pgnum[i];
}
EraseNpl_1Blk(addr[0]);
Test_Write_Dual_4_Sectors(addr, "IO1", "IO2", "IO3", "IO4",
1, "Meta1", "Meta2", "Meta3", "Meta4");
for(int i = 0; i < 4; i++){
Read_1Sector(addr[i], 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
}
}
void test_dual_plane_w_r_multi_2_2(void)
{
char str[16];
struct nvm_addr addr[4];
int plnum[4] = {0, 0, 1, 1};
int pgnum[4] = {0, 1, 0, 1};
for(int i = 0; i < 4; i++){
addr[i].ppa =Get_addr_for_test();
addr[i].g.pl = plnum[i];
addr[i].g.pg = pgnum[i];
}
EraseNpl_1Blk(addr[0]);
Test_Write_Dual_4_Sectors(addr, "IO1", "IO2", "IO3", "IO4",
1, "Meta1", "Meta2", "Meta3", "Meta4");
for(int i = 0; i < 4; i++){
Read_1Sector(addr[i], 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
}
}
void test_sngl_plane_2_w_1_r(void) //write 2 sectors by sngl-plane, 1 by 1 and read it by dual-plane
{
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
const char *IO1 = "foo_78654";
const char *IO2 = "bar_90876";
const char *Meta1 = "foo_meta";
const char *Meta2 = "bar_meta";
EraseNpl_1Blk(addr);
Write_2Sectors(addr, IO1, IO2, 1, Meta1, Meta2);
Read_Dual_Sector(addr, 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
printf("IO2:%s ||| Meta:%s\n",
(char*)(Buf_for_1_read + 1 * geo->sector_nbytes),
(char*)(Buf_for_1_meta_read + 1 * geo->meta_nbytes));
}
void test_dual_plane_w_r(void) //write 2 sectors by dual-plane, read 2 sectors by sngl-plane
{
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
const char *IO1 = "foo_78654";
const char *IO2 = "bar_90876";
const char *Meta1 = "foo_meta";
const char *Meta2 = "bar_meta";
EraseNpl_1Blk(addr);
Write_Dual_Sector(addr, IO1, IO2, 1, Meta1, Meta2);
Read_Dual_Sector(addr, 1);
printf("IO1:%s ||| Meta:%s\n",
(char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
printf("IO2:%s ||| Meta:%s\n",
(char*)(Buf_for_1_read + 1 * geo->sector_nbytes),
(char*)(Buf_for_1_meta_read + 1 * geo->meta_nbytes));
}
void test_read_a_none_write_addr(void) // read a sector which not writen before.
{
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
My_pr_addr_with_str("addr", addr);
EraseNpl_1Blk(addr);
Read_1Sector(addr, 1);
}
void test_write_read_meta(void) // meta: a sector a meta or a block a meta.
{
struct nvm_addr addr, addr2;
addr.ppa = Get_addr_for_test();
addr2.ppa = Get_addr_for_test();
addr2.g.pg += 1;
const char a0[] = "8765uiytr";
const char a1[] = "234aygt65";
const char a2[] = "003 teststring";
const char a3[] = "004 teststring";
EraseNpl_1Blk(addr); //same block, only need 1 erase operation.
Write_1Sector(addr, a0, 1, a1);
Write_1Sector(addr2, a2, 1, a3);
My_pr_addr_with_str("addr1", addr);
Read_1Sector(addr, 1);
printf("IO:%s ||| Meta:%s\n", (char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
My_pr_addr_with_str("addr2", addr2);
Read_1Sector(addr2, 1);
printf("IO:%s ||| Meta:%s\n", (char*)Buf_for_1_read, (char*)Buf_for_1_meta_read);
}
void test_write_no_align(void)
{
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
void *memptr = nvm_buf_alloc(geo, 2 * geo->sector_nbytes);
if(!memptr){
FAIL_ALLOC;
return ;
}
void *memptr_unalign = memptr + 2048;
printf("%p %p\n", memptr, memptr_unalign);
printf("memptr_align: %lu memptr_unalign: %lu\n", ((uintptr_t)memptr % geo->sector_nbytes), ((uintptr_t)memptr_unalign % geo->sector_nbytes));
memcpy(memptr_unalign, Str, STRLEN);
EraseNpl_1Blk(addr);
res = nvm_addr_write(dev, &addr, 1, memptr_unalign, NULL, pmode, &ret);//Write 1 sector
if(res < 0){
FAIL_WRITE;
goto OUT;
}
Read_1Sector(addr, 0);
if(0 == MemCmp(Str, Buf_for_1_read, strlen(Str))){
THE_SAME_IO;
}else{
NOT_THE_SAME_IO;
}
OUT:
free(memptr);
if(res < 0){
nvm_ret_pr(&ret);
}
}
void test_basic(void);
void test_write_no_erase(void)
{
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
printf("Before test: ");
test_basic();
printf("Run test: ");
Write_1Sector(addr, Str, 0, NULL);
}
void test_erase_1pl_1blk(void)
{
struct nvm_addr addr;
struct nvm_ret ret;
ssize_t res;
int pmode = NVM_FLAG_PMODE_SNGL;
const int npl = geo->nplanes;
struct nvm_addr whichblk[npl];
addr.ppa = a0.ppa;
for(int i = 0; i < npl; ++i){
whichblk[i].ppa = alignblk(addr);
whichblk[i].g.pl = i;
}
res = nvm_addr_erase(dev, whichblk, 1, pmode, &ret);//Erase 1 block of 1 planes inside a lun.
if(res < 0){
FAIL_ERASE;
nvm_ret_pr(&ret);
}
}
void test_basic(void)
{
struct nvm_addr addr;
addr.ppa = Get_addr_for_test();
EraseNpl_1Blk(addr);
Write_1Sector(addr, Str, 0, NULL);
Read_1Sector(addr, 0);
if(0 == MemCmp(Str, Buf_for_1_read, strlen(Str))){
THE_SAME_IO;
}else{
NOT_THE_SAME_IO;
}
}
typedef void (* FuncType) (void);
void RunTests()
{
FuncType tests[] = {
// test_basic,
// test_erase_1pl_1blk,
// test_write_no_erase,
// test_write_no_align,
// test_write_read_meta,
// test_read_a_none_write_addr,
// test_sngl_plane_2_w_1_r,
// test_dual_plane_w_r,
// test_dual_plane_w_r_multi_2_2,
test_dual_plane_w_r_multi_3_1,
test_dual_plane_w_r_multi_4_0,
test_dual_plane_w_r_multi_4_0_f
};
const char *teststr[] = {
// "test_basic",
// "test_erase_1pl_1blk",
// "test_write_no_erase",
// "test_write_no_align",
// "test_write_read_meta",
// "test_read_a_none_write_addr",
// "test_sngl_plane_2_w_1_r",
// "test_dual_plane_w_r",
// "test_dual_plane_w_r_multi_2_2",
"test_dual_plane_w_r_multi_3_1",
"test_dual_plane_w_r_multi_4_0",
"test_dual_plane_w_r_multi_4_0_f"
};
for(int i = 0; i < (sizeof(tests) / sizeof(FuncType)); i++){
printf("====Test %d : %s====\n", i, teststr[i]);
tests[i]();
}
}
int main()
{
if( setup() < 0){
goto OUT;
}
RunTests();
OUT:
teardown();
return 0;
}
总结
最后,对I/O部分我总结如下:
1.读写的单位是sector page(这里因为每个page只有一个sector,所以sector也ok, 见liblightnvm_issue3). 擦除的单位是block.
2.做任何读/写/擦除前需要检查该地址是否合法(越界?liblightnvm提供nvm_addr_check
,nvm_bounds_pr
)
3.不能写一个没有擦除过的地址.
4.不能读一个擦除过但没有写过的地址.(test_read_a_none_write_addr
)
5,6
是关于并行的:
5.erase操作目前只能对一个lun上的所有plane同时发擦除命令. 其性能如何目前并不能确定(qemu),从字面上而言,应该是同时擦除所有的plane.
6.与上面类似,access_mode从字面意义上应该也是同时针对一个lun上的1 / 2 / 4 个plane同时进行读写,而性能由于是qemu,并不能确定一定存在真.并行。(因为qemu最底层也是变成一个fwrite/fread)