
Linux 内核 Reed-Solomon 库编程接口详解从 init_rs 到编解码与纠错实战【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读Linux 内核的通用 Reed-SolomonRS库提供了一套与数据位宽无关的编解码与纠错函数被广泛用于通信与存储场景例如 NAND Flash 的 ECC 校验。本文以 Documentation/core-api/librs.rst 为核心系统讲解 RS 库的初始化、编码、解码、资源释放全流程并结合内核源码include/linux/rslib.h、lib/reed_solomon/reed_solomon.c、lib/reed_solomon/encode_rs.c、lib/reed_solomon/decode_rs.c与真实驱动如 DiskOnChip NAND 控制器深入讲解每个接口的参数语义、底层原理与调用约束。读完本文你将能够在自己的内核模块或驱动中正确完成 RS 编解码器的初始化、使用与销毁并理解纠错上限、擦除erasure处理、校正位掩码等进阶概念。Reed-Solomon 库是什么Reed-Solomon 码是一类纠错码通过在原始数据之后附加一组校验符号parity使得接收端能够在数据遭受一定数量的符号错误时仍能恢复出原始信息。内核中的通用 RS 库提供编码Encoding根据数据计算校验符号解码Decoding利用接收到的数据与校验符号计算伴随式syndrome定位并纠正错误。该库的代码源自 Phil KarnKA9Q的 Reed-Solomon 库由 Thomas Gleixner 移植并适配到内核参见 lib/reed_solomon/reed_solomon.c 的文件头注释模块描述为 Reed Solomon encoder/decoder作者为 Phil Karn, Thomas Gleixner。从源码结构看RS 库的设计具有两个层次codec 层struct rs_codec保存给定参数符号位宽、生成多项式、连续根、本原元、校验符号数下的 Galois 域查找表与生成多项式可被多个实例共享control 层struct rs_control每个调用者一个实例内含指向 codec 的指针以及解码所需的内部临时缓冲区。---------------- ------------------ | rs_control #1 |------| | ---------------- | rs_codec | (查找表 alpha_to、 | rs_control #2 |------| index_of、 | genpoly、nroots、fcr...) ---------------- | | ------------------初始化init_rs / init_rs_gfp参数语义初始化接口的完整原型定义在 include/linux/rslib.hstruct rs_control *init_rs_gfp(int symsize, int gfpoly, int fcr, int prim, int nroots, gfp_t gfp); static inline struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, int nroots);其中init_rs是init_rs_gfp以GFP_KERNEL为分配标志的便捷封装。各参数含义参数含义说明symsize符号位宽bits per symbol每个符号占用的比特数对应 codec 中的mm块内符号总数nn (1mm) - 1gfpolyGalois 域生成多项式系数0 次系数位于最低位必须为本原多项式传 0 时改用gffunc生成域元素非规范表示见init_rs_non_canonicalfcr生成多项式第一个连续根索引形式对应 codec 中的fcrprim用于生成多项式根的本原元索引形式对应 codec 中的primnroots生成多项式次数 校验符号数对应 codec 中的nrootsgfp内存分配标志init_rs固定使用GFP_KERNEL内部工作机制init_rs_gfp最终调用init_rs_internallib/reed_solomon/reed_solomon.c其行为如下参数合法性检查symsize 1、fcr/prim/nroots超出[0, 1symsize)范围时直接返回NULL查找可复用 codec在全局链表codec_list上遍历若已有symsize、gfpoly、gffunc、fcr、prim、nroots全部匹配的 codec则只将其users计数加一并新建rs_control指向它链表与计数由互斥锁rslistlock保护创建新 codeccodec_initlib/reed_solomon/reed_solomon.c负责分配alpha_to对数查找表、index_of反对数查找表、genpoly生成多项式三块数组通过移位加异或的方式生成 Galois 域查找表若循环结束后未回到首元素则判定多项式非本原并报错退出计算iprimprim 次单位根用于解码阶段由 nroots 个连续根逐步构造生成多项式并转换为索引形式以加速编码。性能注意事项新建 codec 时需要构建全部查找表文档明确指出 The function may take a while因此切勿在时间关键路径上调用 init_rs。最佳实践是在模块/驱动初始化阶段创建rs_control退出时释放lib/reed_solomon/reed_solomon.c 有同样说明。示例来自原文档/* the Reed Solomon control structure */ static struct rs_control *rs_decoder; /* Symbolsize is 10 (bits) * Primitive polynomial is x^10x^31 * first consecutive root is 0 * primitive element to generate roots 1 * generator polynomial degree (number of roots) 6 */ rs_decoder init_rs(10, 0x409, 0, 1, 6);该示例与 DiskOnChip NAND 驱动中的用法几乎一致drivers/mtd/nand/raw/diskonchip.c使用init_rs(10, 0x409, FCR, 1, NROOTS)其中NROOTS定义为 4FCR定义为 510参见 drivers/mtd/nand/raw/diskonchip.c 与 drivers/mtd/nand/raw/diskonchip.c并在驱动的 remove/exit 路径中调用free_rs(doc-rs_decoder)。编码encode_rs8 / encode_rs16编码函数原型include/linux/rslib.hint encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, uint16_t invmsk); int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, uint16_t invmsk);encode_rs88 位数据宽度的通用 codec符号位宽 1–15 位由CONFIG_REED_SOLOMON_ENC8控制编译encode_rs1616 位数据宽度符号位宽 1–15 位由CONFIG_REED_SOLOMON_ENC16控制编译两者共用同一份数据宽度无关的实现 lib/reed_solomon/encode_rs.c通过 C 预处理器#include进各自的包装函数中。编码过程与约束校验缓冲区必须预先清零par作为输出缓冲区其元素类型为uint16_t为了支持大于 8 位的符号调用前应由调用者初始化为 0文档示例使用memset(par, 0, sizeof(par))长度合法性检查pad nn - nroots - len若pad 0 || pad nn则返回-ERANGEencode_rs.c第 24-26 行核心算法对每个数据符号先与invmsk异或后与par[0]组合得到反馈项fb再按生成多项式更新校验寄存器并做移位本质是线性反馈移位寄存器LFSR式的除法求余invmsk 的用途非零的invmsk会在编码时对展开后的数据做按位异或翻转。文档以FLASH ECC为例说明其价值擦除后的 Flash 全为 0xFF若不处理读回擦除块会触发 ECC 错误通过将数据翻转为全 0x00 再编码全 0x00 的 RS 校验码也是全 0x00存储前再次翻转使校验码也变为 0xFF从而保证读回擦除块时不会误报 ECC 错误。示例来自原文档/* Parity buffer. Size number of roots */ uint16_t par[6]; /* Initialize the parity buffer */ memset(par, 0, sizeof(par)); /* Encode 512 byte in data8. Store parity in buffer par */ encode_rs8(rs_decoder, data8, 512, par, 0);符号位宽与连续比特流的限制原文档特别指出数据字节会在编码时按需展开到目标符号位宽例如符号位宽为 10 时每 8 位数据被映射为 10 位符号参与运算但目前不支持符号位宽 ! 8 的连续比特流编码。如果需要该能力从代码结构看只需在包装层增加比特组装逻辑并非大改动。解码decode_rs8 / decode_rs16解码函数原型include/linux/rslib.hint decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, uint16_t *corr); int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, uint16_t *corr);参数详解参数含义data接收到的数据域可传NULL见仅返回校正信息模式par接收到的校验符号可传NULLlen数据长度s伴随式syndrome必须为索引形式传NULL表示由库内部计算no_eras擦除erasure个数eras_pos擦除位置数组可为NULLinvmsk数据翻转掩码只作用于数据不作用于校验符号corr存储校正位掩码的缓冲区配合eras_pos使用可为NULL返回值纠正的符号数遇到不可纠正错误时返回-EBADMSG。注意返回值计数包含对校验符号的纠错参见 lib/reed_solomon/reed_solomon.c 的注释。底层算法流程解码核心实现位于 lib/reed_solomon/decode_rs.c整体为经典的两阶段流程伴随式计算若调用者未提供s则对接收到的数据与校验符号逐符号计算伴随式并转换为索引形式syn_error为零说明接收字本身就是一个码字直接返回 0Berlekamp-Massey 算法由伴随式迭代求解错误擦除定位多项式lambdaChien 搜索遍历域元素寻找lambda的根得到错误位置若根落在 padding 区k pad或根的数量与deg_lambda不符判定为不可纠正错误返回-EBADMSG错误值求解由 Forney 公式计算错误值并对校正结果做伴随式回验第 289-301 行确保纠正后确实是码字结果输出根据调用者提供的参数组合选择把校正结果写回数据/校验缓冲区或写入correras_pos缓冲区。解码所需的lambda、syn、b、t、omega、root、reg、loc等中间缓冲区全部内嵌在rs_control结构体中大小为RS_DECODE_NUM_BUFFERS * (nroots 1)参见 lib/reed_solomon/reed_solomon.c 与 lib/reed_solomon/reed_solomon.c因此同一rs_control上的解码调用必须串行化不允许并发重入。用法一库内计算伴随式并直接纠正数据来自原文档/* Parity buffer. Size number of roots */ uint16_t par[6]; uint8_t data[512]; int numerr; /* Receive data */ ..... /* Receive parity */ ..... /* Decode 512 byte in data8.*/ numerr decode_rs8(rs_decoder, data8, par, 512, NULL, 0, NULL, 0, NULL);这是最常用的一站式解码库内部完成伴随式计算并在原处纠正数据与校验中的错误。用法二硬件解码器提供伴随式直接纠正数据来自原文档/* Parity buffer. Size number of roots */ uint16_t par[6], syn[6]; uint8_t data[512]; int numerr; /* Receive data */ ..... /* Receive parity */ ..... /* Get syndrome from hardware decoder */ ..... /* Decode 512 byte in data8.*/ numerr decode_rs8(rs_decoder, data8, par, 512, syn, 0, NULL, 0, NULL);许多硬件 ECC 引擎自带伴随式计算能力。此时将s传入即可跳过软件伴随式计算只执行纠错阶段减少 CPU 开销。注意s必须转换为索引形式再传入。用法三硬件解码器提供伴随式仅取回校正信息来自原文档/* Parity buffer. Size number of roots */ uint16_t par[6], syn[6], corr[8]; uint8_t data[512]; int numerr, errpos[8]; /* Receive data */ ..... /* Receive parity */ ..... /* Get syndrome from hardware decoder */ ..... /* Decode 512 byte in data8.*/ numerr decode_rs8(rs_decoder, NULL, NULL, 512, syn, 0, errpos, 0, corr); for (i 0; i numerr; i) { do_error_correction_in_your_buffer(errpos[i], corr[i]); }注意此模式下不需要向解码器传入data与par可传NULL。解码器把错误位置写入errpos、把校正位掩码写入corr由调用者通常是位序比较怪异的硬件解码器驱动自行完成纠错。这正是 lib/reed_solomon/reed_solomon.c 中corr eras_pos分支的行为。擦除erasure与纠错能力边界no_eras/eras_pos参数支持擦除模式当硬件能标记某些符号不可信如 Flash 坏块标记时把这些位置告知解码器可以显著提升纠错效率。从测试代码 lib/reed_solomon/test_rslib.c 可见库的纠错能力遵循经典 RS 码容量公式2 * 错误数 擦除数 nroots即在 nroots 个校验符号下最多可纠正nroots/2个未知位置的符号错误若错误位置全部已知纯擦除则可纠正多达nroots个错误。测试程序正是按errs与eras的二维组合遍历验证的。资源释放free_rs/* Release resources */ free_rs(rs_decoder);free_rslib/reed_solomon/reed_solomon.c的行为是若传入NULL直接返回获取rslistlock将该 control 关联 codec 的users计数减一只有当users降为 0即调用者是 codec 的最后使用者时才真正释放 codec 的查找表与结构体并把它从codec_list摘除最后释放rs_control本身。这种引用计数设计允许多个实例共享同一参数集合的 codec避免重复构建昂贵的查找表。配置与编译选项RS 库的 Kconfig 入口位于 lib/Kconfigconfig REED_SOLOMON tristate config REED_SOLOMON_ENC8 bool config REED_SOLOMON_DEC8 bool config REED_SOLOMON_ENC16 bool config REED_SOLOMON_DEC16 boolREED_SOLOMON是库本体三态可编译为模块由需要它的驱动通过select自动开启通常不需要手动配置REED_SOLOMON_ENC8/DEC8、REED_SOLOMON_ENC16/DEC16分别决定是否编译 8 位/16 位数据宽度的编解码包装函数。例如 DiskOnChip NAND 驱动就在 drivers/mtd/nand/raw/Kconfig 中select REED_SOLOMON与select REED_SOLOMON_DEC16。此外lib/Kconfig.debug 提供REED_SOLOMON_TESTtristate Reed-Solomon library testdepends on DEBUG_KERNEL || m自动selectREED_SOLOMON/ENC16/DEC16可在启动或模块加载时运行库的自测。自测程序test_rsliblib/reed_solomon/test_rslib.c 是库的完整自测与压力测试模块由 Ferdinand Blomqvist 编写通过 lib/reed_solomon/Makefile 中的obj-$(CONFIG_REED_SOLOMON_TEST) test_rslib.o构建。其价值在于为文档中三个解码用法提供了可对照的验证实现测试代码表第 46-59 行覆盖了从(2,0x7)到(9,0x211)等多种 RS 码参数其中(8, 0x11d, 1, 1, 30)对应经典的 RS(255,223) 风格配置符号 8 位、255 符号块、30 个校验符号三种解码方法CORR_BUFFER、CALLER_SYNDROME、IN_PLACE第 21-25 行分别对应文档中的校正缓冲区模式、调用者提供伴随式、原地直接纠正与文档三个示例一一对应超出纠错能力的行为测试test_bc第 366-405 行验证当错误数超过纠错上限时解码器要么正确返回-EBADMSGrfail要么成功返回但结果必须是真正的码字对返回字重新编码比对校验统计noncw静默失败数确保不会出现纠正出错误码字的情况运行方式模块加载后执行测试并返回-EAGAIN使模块直接卸载测试失败会打印rslib: test failed全部通过则打印rslib: test ok。这个测试文件是理解 decode_rs 各参数组合的最佳参考样例例如compute_syndrome第 229-257 行展示了如何自行计算索引形式的伴随式fix_err第 221-227 行展示了如何利用corr与eras_pos完成外部纠错。真实内核用户参考除文档示例外内核中已有实际驱动采用这套接口DiskOnChip NAND 驱动drivers/mtd/nand/raw/diskonchip.c在探测时init_rs(10, 0x409, FCR, 1, NROOTS)NROOTS4FCR510在卸载路径free_rs(doc-rs_decoder)并使用 16 位宽解码接口完成 ECC 校验drivers/mtd/nand/raw/diskonchip.cNAND 子系统在 drivers/mtd/nand/raw/Kconfig 中同样select REED_SOLOMON与select REED_SOLOMON_DEC16为 NAND ECC 提供软件 RS 支持。需要说明RS 库 API 由CONFIG_REED_SOLOMON_ENC8/DEC8/ENC16/DEC16四个布尔选项门控若某个包装函数未编译对应原型不会出现在 include/linux/rslib.h 中调用方需确保依赖驱动正确select所需选项。已知问题与假设原文档在 Known Bugs And Assumptions 一节明确声明当前没有已知缺陷None。同时文档也提示了两个固有限制前文已详述数据字节是按需展开到符号位宽的不支持符号位宽 ! 8 的连续比特流编解码init_rs构建查找表耗时不能用于时间关键路径。编程注意事项总结结合文档与源码使用 RS 库时有以下几点需要格外留意串行化解码调用rs_control内嵌共享工作缓冲区同一实例上的decode_rs8/16调用不可并发重入校验缓冲区先清零编码前par必须清零否则结果错误伴随式必须为索引形式硬件提供的伴随式需先转换为索引形式可用index_of表再传入sinvmsk 只作用于数据编码时作用于数据与校验输出解码时只作用于数据、不影响校验符号返回值语义返回值为纠正的符号数含校验部分的纠错-EBADMSG表示超出纠错能力调用方应据此决定是否报告数据损坏生命周期管理在驱动初始化时init_rs、退出时free_rs并利用库的 codec 共享与引用计数机制避免重复构造。小结Linux 内核的通用 Reed-Solomon 库以struct rs_controlstruct rs_codec两层结构提供了高效、可复用的纠错能力init_rs或init_rs_gfp负责初始化与查找表构建encode_rs8/16负责校验计算decode_rs8/16通过伴随式计算、Berlekamp-Massey 算法、Chien 搜索与 Forney 公式完成错误定位与纠正free_rs负责引用计数式释放。文档中给出的三种解码模式分别覆盖了软件全流程、硬件伴随式复用与外部纠错三种典型场景配合 lib/reed_solomon/test_rslib.c 的测试代码与 DiskOnChip 等真实驱动开发者可以快速在自己的内核模块中落地一套可靠的数据完整性保障方案。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考