裸机移植yaffs2文件系统


裸机移植yaffs2演示
 
Linux系统上
 
[fulinux@CentOS6 ~]$ cd yaffs2/
 
[fulinux@centos6 yaffs2]$ ls
 
bootstrap.c  bootstrap.lds  bsp  common  makefile  start.S  yaffs2
 
[fulinux@centos6 yaffs2]$ make
 
........................
 
........................
 
........................
 
[fulinux@centos6 yaffs2]$ ls
 
bootstrap.bin  bootstrap.c  bootstrap.lds  bootstrap.map  bsp  common  makefile  start.S  yaffs2
 
[fulinux@centos6 yaffs2]$
 

将bootstrap.bin文件拷贝到/tfpt目录下
 
s3c2440开发板上
 
[ s3c2440@guowenxue ]# set bstarp 'tftp 31000000 bootstrap.bin;go 31000000'
 
[ s3c2440@guowenxue ]# save
 
Saving Environment to NAND...
 
Erasing Nand...
 
Erasing at 0x60000 -- 100% complete.
 
Writing to Nand... done
 
[ s3c2440@guowenxue ]# run bstarp
 
dm9000 i/o: 0x20000300, id: 0x90000a46
 
DM9000: running in 16 bit mode
 
MAC: 08:00:3e:26:0a:6b
 
could not establish link
 
operating at 100M full duplex mode
 
Using dm9000 device
 
TFTP from server 192.168.1.2; our IP address is 192.168.1.111
 
Filename 'bootstrap.bin'.
 
Load address: 0x31000000
 
Loading: T ##############
 
done
 
Bytes transferred = 193924 (2f584 hex)
 
## Starting application at 0x31000000 ...
 

Bootstrap nandflash yaffs2 test Version 0.0.1
 
malloc memory space: 0x30f00000~0x31000000
 
Malloc address: 30f00008, string: Hello World!
 

Configures yaffs mount /nand: start block 16, end block 96
 
'/nand' mounted
 
Create directory [/nand/foo]
 
Create File [/nand/foo/f1] content: [foo/f1]
 
Create File [/nand/foo/f2] content: [foo/f2]
 
Create File [/nand/foo/f3] content: [foo/f3]
 
Create File [/nand/foo/f4] content: [foo/f4]
 
Create directory [/nand/bar]
 
Create File [/nand/bar/f1] content: [bar/f1]
 
List folder '/nand' with recursive:
 
drw- 1 /nand/bar 2048 bytes
 
-rw- 1 /nand/bar/f1 6 bytes
 
drw- 1 /nand/foo 2048 bytes
 
-rw- 1 /nand/foo/f4 6 bytes
 
-rw- 1 /nand/foo/f3 6 bytes
 
-rw- 1 /nand/foo/f2 6 bytes
 
-rw- 1 /nand/foo/f1 6 bytes
 
drw- 1 /nand/lost+found 2048 bytes
 
Remove /nand/foo/f4
 
Remove /nand/bar
 
List folder '/nand' with recursive:
 
drw- 1 /nand/foo 2048 bytes
 
-rw- 1 /nand/foo/f3 6 bytes
 
-rw- 1 /nand/foo/f2 6 bytes
 
-rw- 1 /nand/foo/f1 6 bytes
 
drw- 1 /nand/lost+found 2048 bytes
 
unmount and remount
 

List folder '/nand' with recursive:
 
drw- 1 /nand/lost+found 2048 bytes
 

代码分析
 
[fulinux@centos6 yaffs2]$ ls
 
bootstrap.bin  bootstrap.c  bootstrap.lds  bootstrap.map  bsp  common  makefile  start.S  yaffs2
 
[fulinux@centos6 yaffs2]$
 
先看看make后生成的bootstrap.map文件中主要的段:
 
Linker script and memory map startsize size
 
                0x0000000000000000                . = ALIGN (0x4)
 
(.stack xxxxxxxxxxxxxxxxxx          xxxx  运行时初始化这个段)
 
.text              0x0000000031000000    0x2c3dc
 
.rodata            0x000000003102c3dc      0x2c58
 
.data            0x000000003102f054        0x10
 
.bss            0x000000003102f588      0x36b4
 

其中代码段的第一个代码是start.S
 
start.S文件
 
/********************************************************************************************
 
 *        File:  start.S - Startup Code for ARM920 CPU-core
 
 *    Version:  1.0.0
 
 *  Copyright:  2011 (c) Guo Wenxue <guowenxue@gmail.com>
 
 * Description:  When system power up, the CPU will comes here to excute the first code here.
 
 *  ChangeLog:  1, Release initial version on "Tue Jul 12 16:43:18 CST 2011"
 
 *
 
 *******************************************************************************************/
 


/*
 
 *************************************************************************
 
 *
 
 * Jump vector table as in table 3.1 in [1]
 
 *
 
 *************************************************************************
 
 */
 

.globl _start
 
_start: b  start_code
 

_TEXT_BASE:
 
    .word  TEXT_BASE
 
   
 
.globl _armboot_start
 
_armboot_start:
 
    .word _start
 

/*
 
 * These are defined in the board-specific linker script.
 
 */
 
.globl _bss_start
 
_bss_start:
 
    .word __bss_start
 

.globl _bss_end
 
_bss_end:
 
    .word _end
 

start_code:
 
    /* Set up the stack */
 
stack_setup:
 
    ldr r0, =TEXT_BASE      /* upper 128 KiB: relocated uboot  */
 
    sub r0, r0, #CONFIG_SYS_MALLOC_LEN  /* malloc area              */
 
    sub sp, r0, #12    /* leave 3 words for abort-stack    */
 
    bic sp, sp, #7      /* 8-byte alignment for ABI compliance */
 

clear_bss:
 
    ldr r0, _bss_start      /* find start of bss segment        */
 
    ldr r1, _bss_end        /* stop here                        */
 
    mov r2, #0x00000000    /* clear                            */
 

clbss_l:str r2, [r0]        /* clear loop...                    */
 
    add r0, r0, #4
 
    cmp r0, r1
 
    ble clbss_l
 

bl  bootstrap_main
 
堆栈段初始化
 
其中TEXT_BASE和#CONFIG_SYS_MALLOC_LEN变量是在makefile文件中定义的,如下:
 
# Set the stack top base address here
 
TEXT_BASE=0x31000000
 
STACK_BASE=0x31010000
 
MALLOC_SIZE=0x100000
 
CFLAGS+=-DTEXT_BASE=$(TEXT_BASE) -DSTACK_BASE=${STACK_BASE} -DCONFIG_SYS_MALLOC_LEN=${MALLOC_SIZE}
 
上面的CFLAGS一行中的-D的意思类似于#define,,例如-DTEXT_BASE=$(TEXT_BASE) 等于#define TEXT_BASE 0X31000000。.
 
BSS段初始化
 
Bss段初始化代码其中的_bss_start  ---> __bss_start是在makefile文件中
 
APP_NAME=bootstrap
 
LDFLAGS=-Bstatic -T$(APP_NAME).lds -Ttext $(TEXT_BASE)
 
参数LDFLAGS依赖于文件bootstrap.lds文件,如下
 
/********************************************************************************************
 
 *        File:  bootstrap.lds
 
 *    Version:  1.0.0
 
 *  Copyright:  2011 (c) Guo Wenxue <guowenxue@gmail.com>
 
 * Description:  This is the LD linker configure script for bootstrap
 
 *  ChangeLog:  1, Release initial version on "Tue Jul 12 16:43:18 CST 2011"
 
 *
 
 *******************************************************************************************/
 

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
 
OUTPUT_ARCH(arm)
 
ENTRY(_start)
 

SECTIONS{
 
    . = ALIGN(4);
 
    .text  :
 
    {
 
      start.o  (.text)
 
      *(.text)
 
    }
 
    . = ALIGN(4);
 
    .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
 
    . = ALIGN(4);
 
    .rodata : { *(.rodata) }
 
    . = ALIGN(4);
 
    .data : { *(.data) }
 
    . = ALIGN(4);
 
    __bss_start = .;
 
    .bss : { *(.bss) }
 
    _end = .;
 
}
 
然后是跳转到c函数中的bootstarp_main()函数中去,bootstarp_main()函数如下:
 
位置:yaffs2/bootstrap.c
 
int bootstrap_main(void)
 
{
 
    char *ptr = NULL;
 
    int rv = -1;
 
   
 
    console_serial_init();
 
    printf("\b\n");   
 
    printf("\bBootstrap nandflash yaffs2 test Version 0.0.1\n");
 

    /*  armboot_start is defined in the board-specific linker script */
 
    mem_malloc_init (TEXT_BASE - CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN);
 
   
 
    ptr = (char *)malloc(MALLOC_SIZE);                         
 
    strncpy(ptr, "Hello World!\n", MALLOC_SIZE);
 
    printf("Malloc address: %p, string: %s\n", ptr, ptr);     
 
    free(ptr);
 
   
 
    yaffs_test(YAFFSFS_MNT_POINT);
 

hang:
 
    while(1)
 
        ;
 

    return 0;
 
}
 
首先是初始化串口,console_serial_init()函数如下:
 
位置:yaffs2/bsp/s3c_board.h
 
#define CONSOLE_BAUDRATE        115200
 
#define CONSOLE_SERIAL          S3C2440_UART0
 
其中S3C2440_UART0在文件yaffs2/bsp/s3c2440.h中定义:
 
enum s3c2440_uarts_nr {
 
    S3C2440_UART0 = 0,
 
    S3C2440_UART1 = 1,
 
    S3C2440_UART2 = 2
 
};
 

#define console_serial_init()      s3c2440_serial_init(CONSOLE_BAUDRATE, CONSOLE_SERIAL)
 
即--> console_serial_init()-->s3c2440_serial_init()如下:
 
int s3c2440_serial_init(unsigned int baudrate, int index)
 
{
 
    struct s3c2440_uart *uart = s3c2440_get_base_uart(index);
 

    /* FIFO enable, Tx/Rx FIFO clear */
 
    uart->UFCON = 0x07;
 
    uart->UMCON = 0x0;
 

    /* Normal,No parity,1 stop,8 bit */
 
    uart->ULCON = 0x3;
 
    /*
 
    * tx=level,rx=edge,disable timeout int.,enable rx error int.,
 
    * normal,interrupt or polling
 
    */
 
    uart->UCON = (1<<8) | (1<<2) | (1<<0);
 

//  uart->UMCON = 0x1; /* RTS up */
 

    s3c2440_set_baudrate(baudrate, index);
 

    return (0);
 
}
 
设置波特率函数s3c2440_set_baudrate()
 
void s3c2440_set_baudrate(unsigned int baudrate, int index)
 
{
 
    struct s3c2440_uart *uart = s3c2440_get_base_uart(index);
 
    unsigned int reg = 0;
 
    int i;
 
    reg = s3c2440_get_pclk() / (16 * baudrate) - 1;
 

    uart->UBRDIV = reg;
 
    for (i = 0; i < 100; i++);
 
}
 
返回到bootstrap_main()函数中去,看下printf()函数如何将打印信息通过串口显示出来。
 
#define CFG_PBSIZE                  1024 /*  Print Buffer Size */
 
void printf(const char *fmt, ...)
 
{
 
    va_list args;
 
    uint i;
 
    char printbuffer[CFG_PBSIZE];
 

    va_start(args, fmt);
 

    /* For this to work, printbuffer must be larger than
 
    * anything we ever want to print.
 
    */
 
    i = vsprintf(printbuffer, fmt, args);
 
    va_end(args);
 

    console_serial_puts(printbuffer);
 
}
 
调用函数console_serial_puts()函数,如下:
 
#define console_serial_puts(s)    s3c2440_serial_puts(s, CONSOLE_SERIAL)
 
void s3c2440_serial_puts(const char *s, int index)
 
{
 
    while (*s)
 
    {
 
        if (*s == '\n')              /*  If \n, also do \r */
 
            s3c2440_serial_putc('\r', index);
 
        s3c2440_serial_putc (*s++, index);
 
    }
 
}
 
调用函数s3c2440_serial_putc()函数,如下:
 
/*
 
 * Output a single byte to the serial port.
 
 */
 
void s3c2440_serial_putc (char c, int index)
 
{
 
    struct s3c2440_uart *uart = s3c2440_get_base_uart(index);
 

    /* wait for room in the tx FIFO */
 
    //while ((!(uart->UTRSTAT & 0x2)));
 
    while (uart->UFSTAT & (1<<14));
 
    uart->UTXH = c;
 
}
 
返回bootstrap_main()函数,调用mem_mallloc_init()函数初始化动态分配的存储区,为我们调用malloc()函数做好准备工作,如下:
 
void mem_malloc_init(ulong start, ulong size)
 
{
 
    mem_malloc_start = start;
 
    mem_malloc_end = start + size;
 
    mem_malloc_brk = start;
 
   
 
    memset((void *)mem_malloc_start, 0, size);
 
    printf("malloc memory space: 0x%lx~0x%lx\n", start, start+size);
 

 
函数返回,调用函数
 
ptr = (char *)malloc(MALLOC_SIZE);
 
其中MALLOC_SIZE定义在makefile函数,MALLOC_SIZE=0x100000。我们深入到malloc函数中去,如下
 
声明位置:yaffs2/common/malloc.h
 
#define mALLOc      malloc
 
Void_t* mALLOc(size_t);
 
定义位置:yaffs2/common/dlmalloc.c
 
代码量很大,不拷贝了。
 
返回bootstrap_main()函数,调用如下函数目的是测试动态分配到存储区域是否成功:
 
    strncpy(ptr, "Hello World!\n", MALLOC_SIZE);
 
    printf("Malloc address: %p, string: %s\n", ptr, ptr);
 
    free(ptr);
 
接着就到了测试yaffs2文件系统的阶段了。
 
#define YAFFSFS_MNT_POINT              "/nand"
 
yaffs_test(YAFFSFS_MNT_POINT);
 
hang:
 
    while(1)
 
        ;
 
    return 0;
 
}
 
其中yaffs_test()函数主要是通过创建文件或目录,并删除一些文件的功能来达到测试yaffs2文件系统是否移植成功,代码如下:
 
位置:与bootstrap_main()函数同处于一个位置。
 
void yaffs_test(const char *mountpt)
 
{
 
    yaffs_start_up();
 

    yaffs_format(mountpt,0,0,0);
 

    yaffs_mount(mountpt);
 
    printf("'%s' mounted\n", mountpt);
 

    mkdir(mountpt, "foo");
 
    mkfile(mountpt, "foo/f1");
 

    mkfile(mountpt, "foo/f2");
 
    mkfile(mountpt, "foo/f3");
 
    mkfile(mountpt, "foo/f4");
 

    mkdir(mountpt, "bar");
 
    mkfile(mountpt, "bar/f1");
 
    ls(mountpt, NULL);
 

    rm(mountpt, "foo/f4");
 
    rm(mountpt, "bar");
 
    ls(mountpt, NULL);
 

    printf("unmount and remount\n\n");
 
   
 
    /*  Unmount/remount yaffs_trace_mask */
 
    yaffs_unmount(mountpt);
 
    yaffs_mount(mountpt);
 
    ls(mountpt, NULL);
 
}
 
其中yaffs_start_up()函数用来配置将要用到的设备,如下:
 
位置:yaffs2/yaffs2/yaffscfg2k.c
 
/* Configure the devices that will be used */
 
int yaffs_start_up(void)
 
{
 
    static int start_up_called = 0;
 

    if(start_up_called)
 
        return 0;
 
    start_up_called = 1;
 

    yaffs_devconfig(YAFFSFS_MNT_POINT, YAFFSFS_START_BLOCK, YAFFSFS_END_BLOCK);
 

    /* Call the OS initialisation (eg. set up lock semaphore */
 
    yaffsfs_OSInitialisation();
 

    return 0;
 
}
 
其中yaffs_devconfig(YAFFSFS_MNT_POINT, YAFFSFS_START_BLOCK, YAFFSFS_END_BLOCK),的参数是宏,如下:
 
#define YAFFSFS_MNT_POINT              "/nand"
 

#define YAFFSFS_OFFSET                (2*SIZE_1M)  /* YAFFS2 file system start offset address */
 
#define YAFFSFS_SIZE                  (10*SIZE_1M) /* YAFFS2 file system size */
 
#define YAFFSFS_START_BLOCK            ( YAFFSFS_OFFSET/NF_BLOCK_SIZE )
 

#define K9F2G08_BLOCK_SIZE            0x20000      /* Nandflash block size: 128K  */
 
#define NF_BLOCK_SIZE                  K9F2G08_BLOCK_SIZE
 

#define YAFFSFS_END_BLOCK              ( YAFFSFS_START_BLOCK+(YAFFSFS_SIZE/NF_BLOCK_SIZE) )
 
yaffs_devconfig()函数的代码如下:
 
位置:yaffs2/yaffs2/yaffscfg2k.c
 
int yaffs_devconfig(char *_mp, int start_block, int end_block)
 
{
 
    struct yaffs_dev *dev = NULL;
 
    char *mp = NULL;
 

    dev = malloc(sizeof(*dev));
 
    mp = strdup(_mp);
 

    if (!dev || !mp)
 
    {
 
        /*  Alloc error */
 
        printf("Failed to allocate memory\n");
 
        return -1;
 
    }
 

    /*  Seems sane, so configure */
 
    memset(dev, 0, sizeof(*dev));
 
    dev->param.name = mp;
 
    dev->param.is_yaffs2 = 1;
 

    dev->param.total_bytes_per_chunk = NF_PAGE_SIZE;
 
    dev->param.spare_bytes_per_chunk = NF_SPARE_SIZE;
 
    dev->param.chunks_per_block = NF_BLOCK_SIZE / NF_PAGE_SIZE;
 
    dev->param.start_block = start_block;
 
    dev->param.end_block = end_block;
 
    dev->param.n_reserved_blocks = 8;
 

    dev->param.inband_tags = 0;
 
    dev->param.use_nand_ecc = 0;
 
    dev->param.no_tags_ecc = 0;
 
    dev->param.n_caches=0;
 
    dev->param.empty_lost_n_found = 1;
 
    dev->param.skip_checkpt_rd = 0;
 
    dev->param.skip_checkpt_wr = 0;
 
    dev->param.refresh_period = 1000;
 

    dev->param.initialise_flash_fn = ynf_init;
 
    dev->param.erase_fn = ynf_erase_block;
 
    dev->param.write_chunk_tags_fn = ynf_write_chunk_tags;
 
    dev->param.read_chunk_tags_fn = ynf_read_chunk_tags;
 
    dev->param.bad_block_fn = ynf_mark_block_bad;
 
    dev->param.query_block_fn = ynf_query_block;
 
    dev->driver_context = NULL;
 

    yaffs_add_device(dev);
 

    printf("Configures yaffs mount %s: start block %d, end block %d %s\n",
 
            mp, start_block, end_block, dev->param.inband_tags ? "using inband tags" : "");
 
    return 0;
 
}
 
其中主要是有一个重要的数据结构体struct yaffs_dev,如下
 
代码清单 struct yaffs_dev结构体
 
struct yaffs_dev {
 
    struct yaffs_param param;
 

    /* Context storage. Holds extra OS specific data for this device */
 
       
 
    void *os_context;
 
    void *driver_context;
 

    struct list_head dev_list;
 
   
 
    /* Runtime parameters. Set up by YAFFS. */
 
    int data_bytes_per_chunk;
 

    /* Non-wide tnode stuff */
 
    u16 chunk_grp_bits; /* Number of bits that need to be resolved if
 
                * the tnodes are not wide enough.
 
                */
 
    u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
 
   
 
    /* Stuff to support wide tnodes */
 
    u32 tnode_width;
 
    u32 tnode_mask;
 
    u32 tnode_size;
 
   
 
    /* Stuff for figuring out file offset to chunk conversions */
 
    u32 chunk_shift;    /* Shift value */
 
    u32 chunk_div;      /* Divisor after shifting: 1 for 2^n sizes */
 
    u32 chunk_mask;    /* Mask to use for power-of-2 case */
 

    int is_mounted;
 
    int read_only;
 
    int is_checkpointed;
 

    /* Stuff to support block offsetting to support start block zero */
 
    int internal_start_block;
 
    int internal_end_block;
 
    int block_offset;
 
int chunk_offset;
 

    /* Runtime checkpointing stuff */
 
    int checkpt_page_seq;  /* running sequence number of checkpt pages */
 
    int checkpt_byte_count;
 
    int checkpt_byte_offs;
 
    u8 *checkpt_buffer;
 
    int checkpt_open_write;
 
    int blocks_in_checkpt;
 
    int checkpt_cur_chunk;
 
    int checkpt_cur_block;
 
    int checkpt_next_block;
 
    int *checkpt_block_list;
 
    int checkpt_max_blocks;
 
    u32 checkpt_sum;
 
    u32 checkpt_xor;
 

    int checkpoint_blocks_required; /* Number of blocks needed to store
 
                    * current checkpoint set */
 

    /* Block Info */
 
    struct yaffs_block_info *block_info;
 
    u8 *chunk_bits;    /* bitmap of chunks in use */
 
    unsigned block_info_alt:1;  /* allocated using alternative alloc */
 
    unsigned chunk_bits_alt:1;  /* allocated using alternative alloc */
 
    int chunk_bit_stride;  /* Number of bytes of chunk_bits per block.
 
                * Must be consistent with chunks_per_block.
 
                */
 

    int n_erased_blocks;
 
    int alloc_block;    /* Current block being allocated off */
 
    u32 alloc_page;
 
    int alloc_block_finder; /* Used to search for next allocation block */
 

    /* Object and Tnode memory management */
 
    void *allocator;
 
    int n_obj;
 
    int n_tnodes;
 

    int n_hardlinks;
 

    struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS];
 
    u32 bucket_finder;
 

    int n_free_chunks;
 

    /* Garbage collection control */
 
    u32 *gc_cleanup_list;  /* objects to delete at the end of a GC. */
 
    u32 n_clean_ups;
 

    unsigned has_pending_prioritised_gc;    /* We think this device might
 
                        have pending prioritised gcs */
 
    unsigned gc_disable;
 
    unsigned gc_block_finder;
 
    unsigned gc_dirtiest;
 
    unsigned gc_pages_in_use;
 
    unsigned gc_not_done;
 
    unsigned gc_block;
 
    unsigned gc_chunk;
 
    unsigned gc_skip;
 
    struct yaffs_summary_tags *gc_sum_tags;
 

    /* Special directories */
 
    struct yaffs_obj *root_dir;
 
    struct yaffs_obj *lost_n_found;
 

    int buffered_block; /* Which block is buffered here? */
 
    int doing_buffered_block_rewrite;
 

    struct yaffs_cache *cache;
 
    int cache_last_use;
 

    /* Stuff for background deletion and unlinked files. */
 
    struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted
 
                    files live. */
 
    struct yaffs_obj *del_dir;  /* Directory where deleted objects are
 
                    sent to disappear. */
 
    struct yaffs_obj *unlinked_deletion;    /* Current file being
 
                            background deleted. */
 
    int n_deleted_files;    /* Count of files awaiting deletion; */
 
    int n_unlinked_files;  /* Count of unlinked files. */
 
    int n_bg_deletions; /* Count of background deletions. */
 

    /* Temporary buffer management */
 
    struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
 
    int max_temp;
 
    int temp_in_use;
 
    int unmanaged_buffer_allocs;
 
    int unmanaged_buffer_deallocs;
 

    /* yaffs2 runtime stuff */
 
    unsigned seq_number;    /* Sequence number of currently
 
                    allocating block */
 
    unsigned oldest_dirty_seq;
 
    unsigned oldest_dirty_block;
 

    /* Block refreshing */
 
    int refresh_skip;  /* A skip down counter.
 
                * Refresh happens when this gets to zero. */
 

    /* Dirty directory handling */
 
    struct list_head dirty_dirs;    /* List of dirty directories */
 

    /* Summary */
 
    int chunks_per_summary;
 
    struct yaffs_summary_tags *sum_tags;
 

    /* Statistics */
 
    u32 n_page_writes;
 
    u32 n_page_reads;
 
    u32 n_erasures;
 
    u32 n_erase_failures;
 
    u32 n_gc_copies;
 
    u32 all_gcs;
 
    u32 passive_gc_count;
 
    u32 oldest_dirty_gc_count;
 
    u32 n_gc_blocks;
 
    u32 bg_gcs;
 
    u32 n_retried_writes;
 
    u32 n_retired_blocks;
 
    u32 n_ecc_fixed;
 
    u32 n_ecc_unfixed;
 
    u32 n_tags_ecc_fixed;
 
    u32 n_tags_ecc_unfixed;
 
    u32 n_deletions;
 
    u32 n_unmarked_deletions;
 
    u32 refresh_count;
 
    u32 cache_hits;
 
    u32 tags_used;
 
    u32 summary_used;
 

};
 
这个结构体真是庞大啊。

相关内容