yaffs2文件系统坏块发生记(读写代码及注释)

2023-11-18

yaffs2文件系统坏块产生记
        对于yaffs2文件系统来说,坏块管理无疑是最关键的问题;下面就Yaffs2文件系统读、写操作来分析坏块产生记。

        写操作:
        写chunk操作;
        参数1:yaffs_dev结构(全局)
        参数2:要写的2048字节数据
        参数3:这个chunk的oob数据
        参数4:是否使用保留区
static int yaffs_write_new_chunk(struct yaffs_dev *dev, 
                 const u8 *data,
                 struct yaffs_ext_tags *tags, int use_reserver)
{
    int attempts = 0;
    int write_ok = 0;
    int chunk;
   
    /*删除checkpt的数据,其实也就是无效掉checkpt的数据*/
    yaffs2_checkpt_invalidate(dev);
    do {
        struct yaffs_block_info *bi = 0;
        int erased_ok = 0;
/*申请一个没有使用的chunk*/
        chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);
        if (chunk < 0) {
/*进入到这里表示flash里面已经没有使用空间,没有使用空间,
整个文件系统进入自读状态*/
            /* no space */
            break;
        }

 /* First check this chunk is erased, if it needs
每个异常关机后再开机后,每个申请块的第一个chunk必须进行异常检查
这里仅仅是第一个申请到的chunk,除非是强制所有的都要检查,不然只
检查上面的内容。
         * checking. The checking policy (unless forced
         * always on) is as follows:
         *
/*检查第一个申请的chunk,如果检查通过,其它的chunk都不用再进行检查
了;如果检查失败,则再次检查;如果块被擦除,则不需要检查。
         * Check the first page we try to write in a block.
         * If the check passes then we don't need to check any
         * more. If the check fails, we check again...
         * If the block has been erased, we don't need to check.
         *
         * However, if the block has been prioritised for gc,
         * then we think there might be something odd about
         * this block and stop using it.
         *
         * Rationale: We should only ever see chunks that have
         * not been erased if there was a partially written
         * chunk due to power loss. This checking policy should
         * catch that case with very few checks and thus save a
         * lot of checks that are most likely not needed.
         *
         * Mods to the above
         * If an erase check fails or the write fails we skip the
         * rest of the block.
        /* let's give it a try */
        attempts++;
/*如果dev->param.always_check_erased置位,表示所有chunk都检查*/
        if (dev->param.always_check_erased)
            bi->skip_erased_check = 0;
/*第一次的要进行检查*/
        if (!bi->skip_erased_check) {
/*对当前申请的chunk进行检查,检查的原则是,先检查tag数据中的ecc是否正确,
检查无异常则erased为1,否则erased为0*/
            erased_ok = yaffs_check_chunk_erased(dev, chunk);
            if (erased_ok != YAFFS_OK) {
/*进入到这里表示数据检查不通过,回收这个异常chunk*/
                yaffs_trace(YAFFS_TRACE_ERROR,
                  "**>> yaffs chunk %d was not erased",
                  chunk);
                /* If not erased, delete this one,
                 * skip rest of block and
                 * try another chunk */
/*下面两个函数目前不想太过考虑*/
                yaffs_chunk_del(dev, chunk, 1, __LINE__);
                yaffs_skip_rest_of_block(dev);
                continue;
            }
        }
/*写数据到chunk,同时写oob区*/
        write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags);
/*表示不跳过检查*/
        if (!bi->skip_erased_check)
            write_ok =
/*这里对写入的数据进行检查,原则是先把main区的数据全部读出来,
然后再进行memcmp比较,比较完后再对oob的16个字节yaffs2数据
也进行对比,全部正常则表示写正常*/
                yaffs_verify_chunk_written(dev, chunk, data, tags);
        if (write_ok != YAFFS_OK) { 
/*如果写不正常,则进行下面的回收操作,下面对这个问题再另行分析*/
yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);
            continue;
        }
/*置位,这个块只检查一次*/
        bi->skip_erased_check = 1;
        /* Copy the data into the robustification buffer */
        yaffs_handle_chunk_wr_ok(dev, chunk, data, tags);
    } while (write_ok != YAFFS_OK &&
         (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
    if (!write_ok)
        chunk = -1;
    if (attempts > 1) {
        yaffs_trace(YAFFS_TRACE_ERROR,
            "**>> yaffs write required %d attempts",
            attempts);
/*计算写异常次数*/
        dev->n_retired_writes += (attempts - 1);
    }
    return chunk;
}

下面接着分析yaffs_handle_chunk_wr_error()函数:
static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk,
                    int erased_ok)
{
    int flash_block = nand_chunk / dev->param.chunks_per_block;
    struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
/*操作这个块优先进行垃圾回收,下面紧接着分析这个函数*/
    yaffs_handle_chunk_error(dev, bi);
    if (erased_ok) {
        /* Was an actual write failure,
         * so mark the block for retirement.*/
/*这里置位,直接表示这个块会被标记为坏块*/
        bi->needs_retiring = 1;
        yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
          "**>> Block %d needs retiring", flash_block);
    }
    /* Delete the chunk */
    yaffs_chunk_del(dev, nand_chunk, 1, __LINE__);
    yaffs_skip_rest_of_block(dev);
}

下面分析yaffs_handle_chunk_err()函数:

void yaffs_handle_chunk_error(struct yaffs_dev *dev,
                  struct yaffs_block_info *bi)
{
    if (!bi->gc_prioritise) {
        /*表示这个块上有出现过ecc校验出错,
         * 回收时优先回收这一块*/
        bi->gc_prioritise = 1;
        /*表示这个设备上有优先可以回收的块*/
        dev->has_pending_prioritised_gc = 1;
        /*记录ecc错误的次数*/
        bi->chunk_error_strikes++;
/*如果ecc异常超过3次以上,那么表示这个块也就是坏块了*/
        if (bi->chunk_error_strikes > 3) {
            bi->needs_retiring = 1; /* Too many stikes, so retire */
            /*这里是我要检验的一个点,就是有ecc出错的情况下
             * 这里会有打印信息*/
            yaffs_trace(YAFFS_TRACE_ALWAYS,
                "yaffs: Block struck out");
        }
    }
}

至上,写操作中已经出现了坏块,打上bi->needs_retiring=1的块将会直接标记为坏块


下面进行具体标记坏块分析:
void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)
{
    struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no);
    int erased_ok = 0;
    int i;
    /* If the block is still healthy erase it and mark as clean.
     * If the block has had a data failure, then retire it.
     */
    yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
        "yaffs_block_became_dirty block %d state %d %s",
        block_no, bi->block_state,
        (bi->needs_retiring) ? "needs retiring" : "");
   /*清除当前最旧的块序列号*/
    yaffs2_clear_oldest_dirty_seq(dev, bi);
    /*当前块可回收*/
    bi->block_state = YAFFS_BLOCK_STATE_DIRTY;
    /* If this is the block being garbage collected then stop gc'ing */
    /*是不是表示当前的这个块正在进行回收,个人认为这里就是表示当前块正在被回收*/
    if (block_no == dev->gc_block)
        dev->gc_block = 0;
    /* If this block is currently the best candidate for gc
     * then drop as a candidate */
    
    /*如果这是存储的最脏的擦除块,那么直接丢弃它*/
    if (block_no == dev->gc_dirtiest) {
        dev->gc_dirtiest = 0;
        dev->gc_pages_in_use = 0;
    }
    /*数据有错误在这个块上???*/
    /*这个块上出现过三次以上的ecc错误*/
    if (!bi->needs_retiring) {
        /*使checkpt无效?*/
        yaffs2_checkpt_invalidate(dev);
        /*调用mtd的nand flash接口擦除*/
        erased_ok = yaffs_erase_block(dev, block_no);
        if (!erased_ok) {
            dev->n_erase_failures++;
            yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
              "**>> Erasure failed %d", block_no);
        }
    }
/*上面如果bi->needs_retiring置位了,表示这个块直接被标记为坏块*/
    /* Verify erasure if needed */
    /*检查是否擦除成功*/
    if (erased_ok && 
        ((yaffs_trace_mask & YAFFS_TRACE_ERASE) ||
         !yaffs_skip_verification(dev))) {
        for (i = 0; i < dev->param.chunks_per_block; i++) {
            if (!yaffs_check_chunk_erased(dev,
                block_no * dev->param.chunks_per_block + i)) {
                yaffs_trace(YAFFS_TRACE_ERROR,
                    ">>Block %d erasure supposedly OK, but chunk %d not erased",
                    block_no, i);
            }
        }
    }
    /*如果是这样,表示整个块均不能用了,*/
    if (!erased_ok) {
        /* We lost a block of free space */
        /*能用空间减去一个单位的block*/
        dev->n_free_chunks -= dev->param.chunks_per_block;
/*下面函数将会直接调用标记坏块接口,下面具体分析这个函数*/
        yaffs_retire_block(dev, block_no);
        yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
            "**>> Block %d retired", block_no);
        return;
    }
    /* Clean it up... */
    /*块的状态为空*/
    bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
    /*序列号为空*/
    bi->seq_number = 0;
    /*可用块++*/
    dev->n_erased_blocks++;
    /*这个块中被使用的chunk为0*/
    bi->pages_in_use = 0;
    bi->soft_del_pages = 0;
    bi->has_shrink_hdr = 0;
    bi->skip_erased_check = 1; /* Clean, so no need to check */
    bi->gc_prioritise = 0;
    bi->has_summary=0;
    /*整个块均处理完成了,把这个块的所有的chunk位清除*/
    yaffs_clear_chunk_bits(dev, block_no);
    yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no);
}

分析  yaffs_retire_block(dev, block_no)函数,下面内容没有什么好分析的了,直接看代码就能知道什么情况:
static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)
{
    struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block);
    yaffs2_checkpt_invalidate(dev);
    yaffs2_clear_oldest_dirty_seq(dev, bi);
    /*标志为坏块*/
    if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) {
        if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) {
            yaffs_trace(YAFFS_TRACE_ALWAYS,
                "yaffs: Failed to mark bad and erase block %d",
                flash_block);
        } else {
            struct yaffs_ext_tags tags;
            int chunk_id =
                flash_block * dev->param.chunks_per_block;
            u8 *buffer = yaffs_get_temp_buffer(dev);
            memset(buffer, 0xff, dev->data_bytes_per_chunk);
            memset(&tags, 0, sizeof(tags));
            tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK;
    if (dev->param.write_chunk_tags_fn(dev, chunk_id -
                               dev->chunk_offset,
                               buffer,
                               &tags) != YAFFS_OK)
                yaffs_trace(YAFFS_TRACE_ALWAYS,
                    "yaffs: Failed to write bad block marker to block %d",
                    flash_block);
            yaffs_release_temp_buffer(dev, buffer);
        }
    }
    bi->block_state = YAFFS_BLOCK_STATE_DEAD;
    bi->gc_prioritise = 0;
    bi->needs_retiring = 0;
    dev->n_retired_blocks++;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

yaffs2文件系统坏块发生记(读写代码及注释) 的相关文章

  • 阿里弃用Hibernate,却用MyBatis,竟然是因为这个!

    最近一直在研究MyBatis源码 作为国内经常使用的持久层框架 其内部代码的设计非常优秀 比如在开发过程中 有能力对框架进行深度的定制化开发 解决BUG也更加得心应手 另外学习开发者是如何设计高扩展性 低耦合性的代码 便于在自己的开发场景中
  • 66W真的比60W充电更快吗?基于Charge pump Charger的快充方案分析

    智能手机发展至今 充电功率和电池续航一直是人们最为关注的问题之一 从早期的5V 1A和5V 2A的低瓦数快充 到后来的高压大电流和低压小电流两极分化 不同手机厂商都制定了自己的充电协议 如OPPO的VOOC vivo的Flash Charg
  • 【HBZ分享】Redis的热点key问题

    Redis是如何将数据落在某个Redis节点上的 通过crc16取模 不是hash算法 是校验一种算法 计算该key应该落到哪个hash槽 solt 上 一共16384个hash槽上 这些槽位会均匀分布在每个节点 上 注意 只有主节点才有槽

随机推荐

  • Qt生成随机数

    参考网址 https www it610 com article 5005396 htm https www cnblogs com bingcaihuang archive 2011 02 11 1951401 html 生成随机数主要用
  • vue监听缓存数据(localStorage)

    方法 可以重写localStorage的setItem方法 当调用setItem方法设置新值的时候 会new Event setItemEvent 用window dispatchEvent 这个方法来派发一个事件 让window去监听 以
  • 用Python实现AI声音克隆的原理和代码示例

    声音克隆是一种利用机器学习技术学习特定人说话的声音特征 并以此生成合成音频的技术 通常在语音合成和人机交互等领域有广泛的应用 下面是一个简单的Python实现示例 1 数据收集 首先 需要从多个不同说话人的语音数据集中收集原始音频数据 并将
  • __init__()方法和__new__()方法

    class A object def init self args kwargs print init A def new cls args kwargs print new A s cls return object new cls ar
  • 操作系统哲学原理(14)内存原理-段式内存管理

    说明 该系类文章更多的是从从哲学视角看 操作系统 这门学科 同时也是 操作系统的学习笔记总结 因为博主 这些年主要是以研究安卓系统和 嵌入式Linux为主 因此这个系类文章也是这两个领域不可或缺的基石之一 尤其是对操作系统感兴趣的伙伴可特别
  • 504网关超时怎么解决_什么是504网关超时错误(以及如何解决)?

    504网关超时怎么解决 A 504 Gateway Timeout Error happens when a server that was attempting to load a web page did not get a respo
  • openwrt下使用SDK编译ipk包遇到Package hiOpenwrt is missing dependencies for the following libraries: libc.so.

    openwrt下使用SDK编译ipk包遇到Package hiOpenwrt is missing dependencies for the following libraries libc so 6 问题 缺少 libc so 6 库 但
  • C++:stat函数

    函数原型 int stat const char pathname struct stat statbuf 函数作用 用于获取文件状态信息 使用函数需要包含头文件 include
  • Docker安装Nginx(配置SSL证书)

    1 下载Nginx镜像 拉取镜像 docker pull nginx 查询镜像 docker images 2 创建配置文件 创建挂载目录 mkdir p home nginx conf d mkdir p home nginx confi
  • 用 Pytest+Allure 生成漂亮的 HTML 图形化测试报告

    本篇文章将介绍如何使用开源的测试报告生成框架 Allure 生成规范 格式统一 美观的测试报告 通过这篇文章的介绍 你将能够 将 Allure 与 Pytest 测试框架相结合 如何定制化测试报告内容 执行测试之后 生成 Allure 格式
  • Springboot+Vue前后端分离项目打包并部署到服务器

    一 打包前端项目 打开前端项目 使用npm run build命令进行打包 打包成功后结果如下 这时 该项目目录下有一个build目录是打包好的文件 将该目录下的所有文件复制到后端Springboot项目的resources static目
  • java微服务架构内存,微服务架构下的子服务器内存波动

    springcloud架构下的服务器jvm内存波动是正常的 服务器内存波动 本人验证了eureka 和 nacos 两种注册中心 nacos 最低内存占用为 60M 最高内存占用为 450M 波动值为400M左右 eureka 最低内存占用
  • Layer Norm

    参考ConvNeXt中的Layer Normalization LN 海斌的文章 知乎 https zhuanlan zhihu com p 481901798 Layer Norm本来是一个样本norm自己 如图所示 也就是说 在 C H
  • tomcat应用服务器大量接口超时,内存及CPU飙升100%以上解决流程

    问题现象 最近我们相关站点的docker环境出现一个奇怪的现象 大量接口超时 普遍都是几千毫秒 问题解决 经过排查 已经确定不是被调用服务端的问题 于是开始排查我们自己的环境 发现docker容器的内存 从启动开始内存不断上升 然后找到我们
  • 【学习打卡】Pandas第六章:缺失数据

    Pandas学习 第六章 缺失数据 一 缺失观测及其类型 1 了解缺失信息 a isna和notna方法 判断缺失值 b 查看缺失值的所在行 c 挑出所有非缺失值列 2 三种缺失符号 二 缺失数据的运算与分组 1 加号与乘号规则 2 gro
  • 用Anaconda安装TensorFlow

    一说起现在比较火的机器学习 深度学习之类的 不得不说的一定有谷歌的TensorFlow框架 关于TensorFlow我就不多说了 因为我对这个东西也不太了解 这才是第一次开始学习 那么今天要说的是就是在Windows平台上安装TensorF
  • 利用MVC做一个 常见的管理系统

    登陆的部分 gt 数据库 gt 表 admin id name pass regtime 表与类的映射关系 基于面向对象 转换成对象的操作 gt 登陆页面
  • Java课题笔记~ JSP开发模型

    MVC 1 JSP演化历史 1 早期只有servlet 只能使用response输出标签数据 非常麻烦 2 后来有了jsp 简化了Servlet的开发 如果过度使用jsp 在jsp中即写大量的java代码 有写html表 造成难于维护 难于
  • 积分路径上有奇点的积分_复变函数与积分变换 简明笔记(七):留数定理

    拖更了两年 最近终于开始补齐之前这篇稿子的内容 之后可能会在暑假期间慢慢写好 保证质量比保证完成速度更重要 留数理论是复积分和复级数理论结合的产物 在前面详细讨论过洛朗级数和柯西积分定理之后 导出留数理论是很正常的事情 系统建立留数理论 实
  • yaffs2文件系统坏块发生记(读写代码及注释)

    yaffs2文件系统坏块产生记 对于yaffs2文件系统来说 坏块管理无疑是最关键的问题 下面就Yaffs2文件系统读 写操作来分析坏块产生记 写操作 写chunk操作 参数1 yaffs dev结构 全局 参数2 要写的2048字节数据