SML 存储管理库开发指南
更新时间: 2025/02/05
在Gitcode上查看源码

本文档介绍 openUBMC 中 SML(Storage Management Library,存储设备管理库)的 API 使用说明、应用场景、限制条件、接口样例、功能定制扩展、日志及常见问题定位方法。

SML 是华为自研的存储设备管理库,用于带外管理 RAID 控制器、物理盘、逻辑盘、阵列、电池等存储资源。上层业务通过 Lua 模块 sml 调用,底层由 l_sml(C++ Lua 绑定层)对接 SML 动态库,支持 LSI、PMC、华为等厂商 RAID 卡。

目录结构

bash
storage/
├── src/
   ├── lualib/
   └── sml/                    # Lua 层封装,对外 API 入口
       ├── init.lua            # sml 模块主入口
       ├── ctrl_info.lua       # 控制器信息封装
       ├── pd_info.lua         # 物理盘信息封装
       ├── ld_info.lua         # 逻辑盘信息封装
       ├── array_info.lua      # 阵列信息封装
       ├── battery_info.lua    # 电池信息封装
       ├── sml_callbacks.lua   # MCTP 回调注册
       └── ...
   ├── lualib-src/
   ├── sml/                    # SML C 源码
   ├── smlib/              # 公共头文件与接口定义
   ├── sml_base/           # 基础适配层
   ├── sml_lsi/            # LSI 厂商实现
   ├── sml_pmc/            # PMC 厂商实现
   ├── sml_histore/        # 华为厂商实现
   └── pd_log_parse/       # 硬盘日志解析
   └── l_sml/                  # Lua 调用 SML 的 C++ 入口
       ├── l_sml.cpp           # sml.core 模块注册
       ├── l_sml_adapter.h/cpp  # 动态库函数适配
       ├── l_ctrl.cpp          # 控制器接口
       ├── l_pd.cpp            # 物理盘接口
       ├── l_ld.cpp            # 逻辑盘接口
       └── ...
   └── service/
       └── smld.lua                # SML 守护服务
├── include/hwproxy/plugins/sml/    # hwproxy I2C 插件
   ├── init.lua
   ├── cmds.lua                    # 命令分发
   └── cmd_work.lua

API 使用说明

接口层次与 smlib 头文件

SML 接口分为三层:Lua 层sml 模块)→ l_sml 层(C++ Lua 绑定)→ smlib 层(C 接口,定义于 storage/src/lualib-src/sml/smlib/ 下的 .h 文件)。l_sml 通过 l_sml_adapterdlsym 动态解析 smlib 导出的 C 函数,并封装为 Lua 可调用接口。

smlib 头文件一览

头文件说明
sml.h总入口,聚合 sml_base、sml_common、sml_oob、sml_ctrl、sml_pd、sml_ld
sml_base.h基础类型、OOB 类型枚举、I2C/MCTP 回调函数指针类型
sml_oob.hOOB 操作相关结构体:SML_PD_OPERTATION_SSML_CTRL_OPERTATION_S
sml_ctrl.h控制器接口与结构体:注册/注销、信息获取、PHY 误码、BBU、Array 等
sml_pd.h物理盘接口与结构体:PD 信息、SMART、日志、诊断、固件升级等
sml_ld.h逻辑盘接口与结构体:LD 信息、创建/删除、属性设置、CacheCade 等
sml_public.h内部缓存与扩展数据结构(如 SML_PD_BASIC_INFO_SSML_LD_BASIC_INFO_S
sml_errcodes.h错误码定义
sml_physical_drive.h物理盘扩展类型(如 SML_PD_ESTIMATED_LIFESPAN_INFO_S
sml_logic_drive.h逻辑盘扩展类型(如 SML_CREATE_LD_COMMON_PROPERTIES_S

smlib C 接口与 Lua 接口对应关系

以下为 smlib 中声明的 C 函数(sml_*.h),l_sml 通过 l_sml_adapterg_sml_adapter 函数表调用,并最终映射到 Lua 的 sml.xxx 接口。

控制器(sml_ctrl.h)

C 接口说明Lua 对应
sml_register_controller(SML_CTRL_OOB_INFO_S*)注册/添加控制器sml.register_controller
sml_unregister_controller(SML_CTRL_OOB_INFO_S*)注销/删除控制器sml.unregister_controller
sml_get_ctrl_init_state(guint8)获取控制器初始化状态sml.get_ctrl_init_state
sml_get_ctrl_sas_addr(SML_CTRL_SAS_ADDR_S*)获取 SAS 地址sml.get_ctrl_sas_addr
sml_get_ctrl_ld_list(SML_CTRL_LD_LIST_S*)获取 LD 列表sml.get_ctrl_ld_list
sml_get_ctrl_pd_list(SML_CTRL_PD_LIST_S*)获取 PD 列表sml.get_ctrl_pd_list
sml_get_ctrl_array_list(SML_CTRL_ARRAY_LIST_S*)获取 Array 列表sml.get_ctrl_array_list
sml_get_ctrl_boot_devices(SML_CTRL_BOOTABLE_DEVICES_S*)获取启动设备sml.get_ctrl_boot_devices
sml_get_array_info(SML_CTRL_ARRAY_INFO_S*)获取 Array 信息sml.get_array_info
sml_get_ctrl_health_status(SML_CTRL_HEALTH_STATUS_S*)获取健康状态sml.get_ctrl_faultcode
sml_ctrl_operation(SML_CTRL_OPERTATION_S*)控制器操作sml.ctrl_operation
sml_clear_all_controller_info(void)清除控制器缓存sml.clear_all_controller_info
sml_dump_ctrl_single_log(...)导出控制器日志sml.dump_ctrl_single_log
sml_parse_controller_bin_log(...)解析控制器二进制日志sml.parse_controller_bin_log

物理盘(sml_pd.h)

C 接口说明Lua 对应
sml_pd_operation(SML_PD_OPERTATION_S*)PD 操作(定位、热备等)sml.pd_operation
sml_get_pd_log(SML_PD_LOG_S*)获取 PD 日志内部使用
sml_pd_fw_upgrade(...)硬盘固件升级sml.pd_upgrade_firmware
sml_pd_fw_active(SML_PD_FW_ACTIVE_S*)激活硬盘固件sml.pd_active_firmware
sml_get_smart_data_str(...)获取 SMART 字符串sml.pd_get_smart_info_str
sml_diagnose_encl_comm_error(SML_PD_FAULT_ANALYSIS*)诊断 Encl 通信故障sml.pd_diag_encl_comm_error
sml_diagnose_pd_sense_error(SML_PD_FAULT_ANALYSIS*)诊断 PD sense 错误sml.pd_diag_sense_error
sml_pd_trans_drive_data(...)传输硬盘数据sml.trans_drive_data

逻辑盘(sml_ld.h)

C 接口说明Lua 对应
sml_set_ld_boot_priority(SML_LD_TARGET_S*)设置启动优先级sml.set_ld_boot_priority
sml_set_ld_delete(SML_LD_TARGET_S*)删除逻辑盘sml.delete_volume
sml_create_ld_on_new_array(SML_RAID_ON_NEW_ARRAY_PARAM_S*)在新 Array 上创建 LDsml.create_ld_on_new_array
sml_create_ld_on_existed_array(SML_RAID_ON_EXISTED_ARRAY_PARAM_S*)在已有 Array 上创建 LDsml.add_ld_on_exist_array
sml_create_ld_as_cachecade(SML_RAID_CACHECADE_PARAM_S*)创建 CacheCade LDsml.create_ld_as_cachecade
sml_set_ld_cachecade_association(SML_LD_SET_CACHECADE_ASSOCIATION_S*)设置 CacheCade 关联sml.set_ld_cachecade
sml_get_ld_sscd_caching_enable(SML_LD_SSCD_CACHING_ENABLE_S*)获取 LD 是否关联 CacheCadesml.get_ld_sscd_caching_enable
sml_get_sscd_associated_ld_list(SML_SSCD_ASSOCIATED_LD_LIST_S*)获取 CacheCade 关联的 LD 列表sml.get_sscd_associated_ld_list
sml_get_ld_associated_sscd_list(SML_LD_ASSOCIATED_SSCD_LIST_S*)获取 LD 关联的 CacheCade 列表sml.get_ld_associated_sscd_list

OOB 回调类型(sml_base.h)

c
// I2C 通道
typedef gint32 (*I2C_WRITE_FUNC)(guint8 obj_index, guint8* pWritebuf, guint8 write_length);
typedef gint32 (*I2C_WRITEREAD_FUNC)(guint8 obj_index, guint8* pWritebuf, guint8 write_length,
    guint8* pReadbuf, guint8 read_length);

// MCTP 通道
typedef gint32 (*MCTP_WRITEREAD_FUNC)(guint8 obj_index, guint32 request_length, const guint8 *request,
    guint32 *response_length, guint8 *response, guint32 timeout);

注册信息结构体(sml_ctrl.h)

c
typedef struct tag_sml_register_ctrl_oob_info {
    guint8  i_controller_index;
    guint8  i_controller_typeid;
    union {
        struct {
            guint8  eid;
            guint16 phy_addr;
            MCTP_WRITEREAD_FUNC mctp_writeread_func;
        } over_mctp;
        struct {
            I2C_WRITE_FUNC i2c_write_func;
            I2C_WRITEREAD_FUNC i2c_writeread_func;
        } over_i2c;
    } register_info;
    guint8  i_oob_operate;
    guint8  i_oob_interface_type;
} SML_CTRL_OOB_INFO_S;

模块引入

lua
local sml = require 'sml'

所有 SML 接口均通过 sml 模块调用,内部会转发到 .smld 服务执行。I2C 通道的 RAID 卡会通过 hwproxy 插件处理,MCTP 通道的 RAID 卡通过 sml_callbacks 注册的 MCTP 读写回调通信。

控制器管理

接口说明
sml.register_controller(ctrl_index, type_id, operation, data)注册控制器
sml.unregister_controller(ctrl_index, type_id, operation, data)注销控制器
sml.get_ctrl_info(sch_prio, ctrl_index)获取控制器基本信息
sml.get_ctrl_pd_list(ctrl_index)获取控制器下物理盘列表
sml.get_ctrl_ld_list(ctrl_index)获取控制器下逻辑盘列表
sml.get_ctrl_array_list(ctrl_index)获取控制器下阵列列表
sml.ctrl_operation(ctrl_index, operation, data)控制器操作
sml.clear_all_controller_info(ctrl_index)清除控制器缓存
sml.update_ctrl_init_state(ctrl_index)更新控制器初始化状态

operation 取值:sml.SML_ADD_CTRL(添加)、sml.SML_REGISTER_CTRL(注册)、sml.SML_DEL_CTRL(删除)、sml.SML_UNREGISTER_CTRL(注销)。

物理盘管理

接口说明
sml.get_pd_info(sch_prio, ctrl_index, pd_device_id)获取物理盘信息
sml.pd_operation(ctrl_index, device_id, operation, data)物理盘操作(定位、热备等)
sml.get_pd_sas_smart_info(ctrl_index, pd_device_id)获取 SAS 盘 SMART 信息
sml.get_pd_slow_disk_info(ctrl_index, pd_device_id)获取慢盘信息
sml.pd_upgrade_firmware(ctrl_index, device_id, firmware_path)硬盘固件升级
sml.pd_active_firmware(ctrl_index, device_id)激活硬盘固件

逻辑盘管理

接口说明
sml.get_ld_info(sch_prio, ctrl_index, ld_target_id)获取逻辑盘信息
sml.set_ld_boot_priority(controller_id, volume_id, priority)设置启动优先级
sml.set_ld_readpolicy/writepolicy/name/...设置逻辑盘属性
sml.add_ld_on_exist_array(...)在已有阵列上创建逻辑盘
sml.create_ld_on_new_array(...)在新阵列上创建逻辑盘
sml.create_ld_as_cachecade(...)创建 CacheCade 逻辑盘
sml.delete_volume(controller_id, volume_id)删除逻辑盘

阵列与电池

接口说明
sml.get_array_info(ctrl_index, array_target_id)获取阵列信息
sml.get_battery_info(ctrl_index)获取 BBU 电池信息

诊断与日志

接口说明
sml.pd_diag_by_smart_log(ctrl_index, device_id)基于 SMART 日志诊断
sml.pd_diag_link_phy_error(...)链路 PHY 误码诊断
sml.get_ctrl_sas_phy_err(ctrl_index, force_update)获取 SAS PHY 误码
sml.dump_ctrl_single_log(ctrl_index, src_dir, log_type, log_name)导出控制器日志
sml.parse_controller_bin_log(ctrl_index, src_dir, dest_dir)解析控制器二进制日志

OOB 类型与常量

lua
sml.OOB_TYPE_OVER_I2C   -- I2C 通道(LSI)
sml.OOB_TYPE_OVER_PCIE -- PCIe/MCTP 通道(华为、PMC)
sml.SML_ADD_CTRL       -- 添加控制器
sml.SML_REGISTER_CTRL  -- 注册控制器
sml.SML_DEL_CTRL       -- 删除控制器
sml.SML_UNREGISTER_CTRL -- 注销控制器

注册流程与报文收发接口

SML 采用回调注入方式,将 BMC 侧的报文收发能力注入到各厂商 so 中。厂商 so 在初始化时接收这些回调,后续与 RAID 卡通信时统一通过回调完成 I2C 或 MCTP 读写。

整体注册流程

text
Lua 业务层                    smld 服务                    l_sml (C++)                    SML 厂商 so
     |                            |                              |                              |
     | register_controller()      |                              |                              |
     |--------------------------->| CMD.register_controller      |                              |
     |                            |                              |                              |
     |                            | 1. register_sml_adapter_function()                          |
     |                            |    - 根据 type_id 选择 so 路径                               |
     |                            |    - dlopen 加载 libsml_base.so / libsml_custom_base.so     |
     |                            |    - dlsym 获取 sml_register_controller 等函数地址          |
     |                            |------------------------------>|                              |
     |                            |                              |                              |
     |                            | 2. 设置 i_oob_interface_type (l_ctrl set_interface_type)     |
     |                            |    - OOB_I2C: register_info.over_i2c.i2c_write_func = i2c_write_cb    |
     |                            |              i2c_writeread_func = i2c_writeread_cb           |
     |                            |    - OOB_PCIE: register_info.over_mctp.mctp_writeread_func = mctp_writeread_cb |
     |                            |------------------------------>|                              |
     |                            |                              |                              |
     |                            | 3. register_controller() -> sml_register_controller(ctrl)     |
     |                            |------------------------------>| smlib_add_ctrl / smlib_init_ctrl_manage |
     |                            |                              |                              |
     |                            |                              | 4. ctrl_manage_object_constructor |
     |                            |                              |    - 根据 vendor 加载 libsml_lsi.so / libsml_pmc.so / libsml_histore.so |
     |                            |                              |    - dlsym 获取 pfn_init_ctrl_over_i2c / pfn_init_ctrl_over_pcie |
     |                            |                              |------------------------------>|
     |                            |                              |                              |
     |                            |                              | 5. pfn_init_ctrl_over_xxx(controller_id, 报文收发回调) |
     |                            |                              |<-----------------------------|
     |                            |                              |    厂商 so 将回调保存到内部,后续通信统一调用 |
     |                            |<-----------------------------|                              |
     |<---------------------------|                              |                              |

报文收发接口如何注入到厂商 so

1. l_sml 层:统一回调入口

l_ctrl.cppset_interface_type 中,根据 OOB 类型将 C++ 静态回调函数写入 SML_CTRL_OOB_INFO_S

OOB 类型回调字段实现函数
OOB_TYPE_OVER_I2Cregister_info.over_i2c.i2c_write_funcl_callbacks::i2c_write_cb
OOB_TYPE_OVER_I2Cregister_info.over_i2c.i2c_writeread_funcl_callbacks::i2c_writeread_cb
OOB_TYPE_OVER_PCIEregister_info.over_mctp.mctp_writeread_funcl_callbacks::mctp_writeread_cb

这些函数是 l_sml 中的静态实现,厂商 so 通过函数指针调用,实际执行时会落到 Lua 或 hwproxy 的收发逻辑。

2. I2C 通道(LSI):hwproxy 插件 + skynet 消息

调用链:厂商 so (libsml_lsi.so) → lsi_init_ctrl_manage(ctrl_id, i2c_write_func, i2c_writeread_func) → 保存回调 → 后续 I2C 访问时调用

报文收发实现

  1. hwproxy 插件include/hwproxy/plugins/sml/init.lua)提供 i2c_writei2c_readi2c_writeread,通过 running_chip(BlockIO)与 RAID 卡通信。
  2. l_callbacksi2c_write_cb / i2c_writeread_cb 调用 params::block_write_read,通过 skynet_send 将请求发往 hwproxy 的 sml 插件。
  3. 插件在 init_register 中注册 sml_writeread_${ctrl_id} 协议,收到消息后执行 local_write_readqueue_write_read,完成 I2C 读写。
  4. 插件通过 cbs:reply_write_read(ctrl_id, result, rsp_data) 将结果回写到 l_callbacks,block_write_read 中的 condition.wait 被唤醒并返回。

前置条件:业务层需先调用 sml.set_i2c_chip(ctrl_idx, chip),将对应控制器的 BlockIO 对象注册到 hwproxy 插件;插件通过 init_handle(ctrl_id, handle, msg_tag) 建立 ctrl_id 与 skynet 通信的映射。

3. MCTP 通道(华为、PMC):Lua 回调 + mctp_lib

调用链:厂商 so → histore_init_ctrl_manage / pmc_init_ctrl_manage(ctrl_id, mctp_writeread_func) → 将 mctp_writeread_func 保存到库内部配置(如 conf.func_list.mctp_send)→ 后续 MCTP 访问时调用

报文收发实现

  1. l_callbacks::mctp_writeread_cbs_params_map[obj_index] 取出 mctp_writeread_cb(Lua 函数),调用 (*s_params.mctp_writeread_cb)(obj_index, data, wait_response, timeout)
  2. Lua 层sml_callbacks.lua)实现 mctp_writeread,内部通过 mctp_infos.get_mctp_info(obj_index) 获取 phy_addr、vendor_id,再调用 mctp_lib.get_pcie_endpointep:Request / ep:Send 完成 MCTP 收发。
  3. 回调注册时机sml.inittimeout_call 在检测到 MCTP 控制器(无 i2c_chip_map)时,会向 sml.callback 发送 install_mctp_writeread_cb_${ctrl_idx}sml_callbacks 收到后执行 cbs:set_mctp_writeread_cb(ctrl_idx, mctp_writeread),将 Lua 的 mctp_writeread 绑定到该 ctrl_id。

前置条件:业务层需先调用 sml.mctp_infos.add_mctp_info(ctrl_id, eid, phy_addr, vendor_id) 注册 MCTP 端点信息。

4. 厂商 so 中的使用方式

厂商so 路径初始化函数报文接口保存位置
LSI/usr/lib64/libsml_lsi.solsi_init_ctrl_manage(ctrl_id, i2c_write, i2c_writeread)StoreLib 内部,通过 I2C 回调访问 RAID 卡
PMC/usr/lib64/libsml_pmc.sopmc_init_ctrl_manage(ctrl_id, mctp_writeread)load_storagecorepfn_register_mctp_func(mctp_writeread)
华为/usr/lib64/libsml_histore.sohistore_init_ctrl_manage(ctrl_id, mctp_writeread)conf.func_list.mctp_send = mctp_writeread_func

厂商 so 在初始化时只做“保存回调”,后续所有与 RAID 卡的通信都通过该回调完成,从而实现 BMC 侧 I2C/MCTP 能力与厂商协议的分离。

5. 注册顺序说明

  • I2C(LSI):须先 sml.set_i2c_chip(ctrl_idx, chip),再执行 register_controller。hwproxy 插件通过 init_handle 建立 ctrl_id 与 skynet 通信的映射,I2C 回调才能正确路由到对应 chip。
  • MCTP(华为、PMC):须先 sml.mctp_infos.add_mctp_info(ctrl_id, eid, phy_addr, vendor_id)。首次对该 ctrl_id 发起 SML 调用时,timeout_call 会向 sml_callbacks 发送 install_mctp_writeread_cb_${ctrl_id},完成 Lua 层 mctp_writeread 的注册,厂商 so 后续通过 mctp_writeread_cb 调用时才能正确落到 Lua 实现。

应用场景

  • RAID 卡识别与注册:通过 register_controller 将 RAID 卡加入 SML 管理,支持 I2C(LSI)和 MCTP(华为、PMC)两种带外通道。
  • 存储拓扑展示:通过 get_ctrl_pd_listget_ctrl_ld_listget_ctrl_array_list 获取控制器下的物理盘、逻辑盘、阵列列表,用于 Redfish/Web 等界面展示。
  • 硬盘健康监控:通过 get_pd_infoget_pd_sas_smart_infoget_pd_slow_disk_info 获取硬盘健康、SMART、慢盘信息,用于告警与亚健康检测。
  • 逻辑盘配置:通过 create_ld_on_new_arrayadd_ld_on_exist_arrayset_ld_* 等接口创建、修改、删除逻辑盘。
  • 硬盘定位与热备:通过 pd_operation 实现硬盘定位灯控制、全局/专用热备设置。
  • 固件升级:通过 pd_upgrade_firmwarepd_active_firmware 实现硬盘固件升级与激活。
  • 故障诊断:通过 pd_diag_*get_ctrl_sas_phy_errpd_diag_link_phy_error 进行 PHY 误码、链路故障诊断。

限制条件

  1. 厂商支持:当前支持 LSI(StoreLib)、PMC(storageCore)、华为(historelib)三类 RAID 卡,新增厂商需在 sml_base 下实现对应 vendor 适配。
  2. OOB 通道:LSI 通过 I2C + hwproxy 插件访问;华为、PMC 通过 MCTP over PCIe VDPCI 访问,需正确配置 MCTP 端点与 sml_callbacks 中的 mctp_writeread 回调。
  3. 控制器索引ctrl_index 从 0 开始,需与业务层(如 controller_object)的控制器 ID 对应。
  4. 超时:所有接口通过 timeout_call 调用 smld,存在默认超时,超时会抛出 sml_call timout 异常。
  5. 并发:同一控制器的读写由 SML 内部加锁保护,但多控制器并发调用需注意 smld 服务负载。

接口使用样例

注册 I2C 通道 RAID 卡

lua
local sml = require 'sml'

-- 设置 I2C chip,用于 I2C 通道的 RAID 卡
sml.set_i2c_chip(ctrl_idx, chip)  -- chip 为 hwproxy 的 BlockIO 对象

-- 添加控制器
sml.register_controller(ctrl_index, type_id, sml.SML_ADD_CTRL, {})

-- 注册控制器(指定 OOB 类型)
sml.register_controller(ctrl_index, type_id, sml.SML_REGISTER_CTRL, {})

注册 MCTP 通道 RAID 卡

lua
local sml = require 'sml'

-- 添加 MCTP 信息
sml.mctp_infos.add_mctp_info(ctrl_id, eid, phy_addr, vendor_id)

-- 添加控制器时传入 MCTP 参数
sml.register_controller(ctrl_index, type_id, sml.SML_ADD_CTRL, {
    Eid = eid,
    Phyaddr = phy_addr
})

获取控制器与物理盘信息

lua
local sml = require 'sml'

local ctrl_info = sml.get_ctrl_info({ Priority = 'Secondary' }, ctrl_index)
-- ctrl_info 为 c_ctrl_info 对象,包含控制器名称、温度、固件版本等

local pd_list = sml.get_ctrl_pd_list(ctrl_index)
-- pd_list 为 t_pd_item[],每项含 device_id、slot_num、enclosure_id

local pd_info = sml.get_pd_info({ Priority = 'High' }, ctrl_index, pd_device_id)
-- pd_info 为 c_pd_info 对象,含健康状态、容量、序列号等

创建逻辑盘

lua
local sml = require 'sml'

-- 在新阵列上创建逻辑盘
local ret, ld_id = sml.create_ld_on_new_array(
    controller_id,
    span_depth,           -- span 个数
    num_drive_per_span,   -- 每 span 硬盘数
    pd_sel,               -- 物理盘 ID 列表
    raid_level,           -- RAID 级别
    ld_name,              -- 逻辑盘名称
    strip_size,           -- 条带大小
    capacity,             -- 容量(MB)
    write_policy,         -- 写策略
    ...
)

物理盘操作(定位、热备)

lua
local sml = require 'sml'

-- 定位灯开启(持续 30 秒)
local data = string.pack('I1', 30)
sml.pd_operation(ctrl_index, device_id, 0, data)  -- 0: PD_OPERATION_LOCATE

-- 停止定位
sml.pd_operation(ctrl_index, device_id, 1, '')  -- 1: PD_OPERATION_STOP_LOCATE

-- 设置为全局热备
sml.pd_operation(ctrl_index, device_id, 2, '')  -- 2: PD_OPERATION_SET_GLOBAL_HOTSPARE

功能定制扩展案例

新增厂商 RAID 卡支持

  1. sml_base/src/vendor/ 下新增厂商目录(如 vendor_xxx)。
  2. 实现 vendor.h 中定义的接口:控制器注册/注销、PD/LD/Array 信息获取、操作执行等。
  3. sml_base 的适配逻辑中根据 type_id 选择对应 vendor。
  4. l_sml_adapter 中确保新厂商的 so 符号可被 dlsym 正确解析。

扩展控制器类型 ID

storage/include/hwproxy/plugins/sml/cmds.lua 中扩展 get_oob_interface_typetest_controller_vendor

lua
local NEW_RAID_TYPE<const> = 128

local function test_controller_vendor(type_id, vendor_id)
    -- ...
    elseif vendor_id == VENDER_NEW then
        if type_id == NEW_RAID_TYPE then
            return true
        end
    end
    return false
end

自定义 MCTP 回调

sml_callbacks.lua 中实现 mctp_writeread,供华为/PMC 等 MCTP 通道使用:

lua
local function mctp_writeread(obj_index, data, wait_response, timeout)
    local phy_addr, vendor_id = mctp_infos.get_mctp_info(obj_index)
    local ep = mctp_lib.get_pcie_endpoint(get_bus(), phy_addr, MCTP_MESSAGE_TYPE_VDPCI)
    local req_data = string.pack('I2', vendor_id) .. data
    if wait_response then
        return RET_OK, ep:Request({}, req_data, timeout / 1000, {}, {}):sub(3)
    else
        ep:Send({}, req_data, {})
        return RET_OK, ''
    end
end

日志介绍和常见问题定位方法

日志说明

  • sml 模块:使用 mc.logging,例如 log:notice("sml: set i2c chip, ctrl_idx=%d, chip=%s", ctrl_idx, chip.path)
  • sml_callbacks:MCTP 读写失败时使用 skynet.error(data) 输出错误信息。
  • SML C 层:错误通过返回值与 sml_errcodes.h 中定义的错误码返回,上层可根据错误码进行转换与日志记录。

常见问题定位

现象可能原因排查方法
sml_call timoutsmld 服务未启动或调用阻塞检查 smld 是否正常运行;检查 I2C/MCTP 链路是否通畅
SML_ERR_CTRL_NOT_REGISTERED控制器未注册或已注销确认 register_controller 已成功执行;检查 ctrl_index 是否有效
SML_ERR_CTRL_INDEX_INVALID控制器索引超出范围确认 ctrl_index 与已注册控制器一致
SML_ERR_I2C_READ_WRITE_FAILEDI2C 通信失败检查 hwproxy、I2C 拓扑、RAID 卡在位与供电
SML_ERR_MCTP_READ_WRITE_FAILEDMCTP 通信失败检查 MCTP 端点、EID、Phyaddr、vendor_id 配置;检查 mctp_writeread 回调是否正确注册
Invalid TypeId不支持的 RAID 卡类型检查 type_id 是否在 cmds.lua 的 test_controller_vendor 支持范围内
SML_ERR_CTRL_LIB_LOAD_FAILED厂商 so 加载失败检查对应厂商 so 是否存在、路径是否正确、依赖是否满足
get_ctrl_init_stateSML_ERR_CTRL_STATUS_INVALID控制器初始化未完成先调用 update_ctrl_init_state 更新状态,再调用 get_ctrl_init_state

错误码参考

错误码定义见 storage/src/lualib-src/sml/smlib/sml_errcodes.h,主要分类:

  • SML_ERR_CODE_BASE (0x1000):通用错误(I2C/MCTP 失败、参数无效等)
  • SML_CTRL_ERR_CODE_BASE:控制器相关(未注册、初始化失败、库加载失败等)
  • SML_LD_ERR_CODE_BASE:逻辑盘相关(ID 无效、属性设置不允许等)
  • SML_PD_ERR_CODE_BASE:物理盘相关(设备 ID 无效、热备不允许、SCSI 超时等)
  • SML_CONFIG_ERR_CODE_BASE:配置相关(RAID 参数无效、Array 空间不足等)
  • SML_DIAG_ERR_CODE_BASE:诊断相关(拓扑不匹配、数据超时等)