本文档介绍 openUBMC 中 SML(Storage Management Library,存储设备管理库)的 API 使用说明、应用场景、限制条件、接口样例、功能定制扩展、日志及常见问题定位方法。
SML 是华为自研的存储设备管理库,用于带外管理 RAID 控制器、物理盘、逻辑盘、阵列、电池等存储资源。上层业务通过 Lua 模块 sml 调用,底层由 l_sml(C++ Lua 绑定层)对接 SML 动态库,支持 LSI、PMC、华为等厂商 RAID 卡。
目录结构
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.luaAPI 使用说明
接口层次与 smlib 头文件
SML 接口分为三层:Lua 层(sml 模块)→ l_sml 层(C++ Lua 绑定)→ smlib 层(C 接口,定义于 storage/src/lualib-src/sml/smlib/ 下的 .h 文件)。l_sml 通过 l_sml_adapter 的 dlsym 动态解析 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.h | OOB 操作相关结构体:SML_PD_OPERTATION_S、SML_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_S、SML_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_adapter 的 g_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 上创建 LD | sml.create_ld_on_new_array |
sml_create_ld_on_existed_array(SML_RAID_ON_EXISTED_ARRAY_PARAM_S*) | 在已有 Array 上创建 LD | sml.add_ld_on_exist_array |
sml_create_ld_as_cachecade(SML_RAID_CACHECADE_PARAM_S*) | 创建 CacheCade LD | sml.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 是否关联 CacheCade | sml.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)
// 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)
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;模块引入
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 类型与常量
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 读写。
整体注册流程
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.cpp 的 set_interface_type 中,根据 OOB 类型将 C++ 静态回调函数写入 SML_CTRL_OOB_INFO_S:
| OOB 类型 | 回调字段 | 实现函数 |
|---|---|---|
OOB_TYPE_OVER_I2C | register_info.over_i2c.i2c_write_func | l_callbacks::i2c_write_cb |
OOB_TYPE_OVER_I2C | register_info.over_i2c.i2c_writeread_func | l_callbacks::i2c_writeread_cb |
OOB_TYPE_OVER_PCIE | register_info.over_mctp.mctp_writeread_func | l_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 访问时调用
报文收发实现:
- hwproxy 插件(
include/hwproxy/plugins/sml/init.lua)提供i2c_write、i2c_read、i2c_writeread,通过running_chip(BlockIO)与 RAID 卡通信。 - l_callbacks 的
i2c_write_cb/i2c_writeread_cb调用params::block_write_read,通过skynet_send将请求发往 hwproxy 的 sml 插件。 - 插件在
init_register中注册sml_writeread_${ctrl_id}协议,收到消息后执行local_write_read或queue_write_read,完成 I2C 读写。 - 插件通过
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 访问时调用
报文收发实现:
- l_callbacks::mctp_writeread_cb 从
s_params_map[obj_index]取出mctp_writeread_cb(Lua 函数),调用(*s_params.mctp_writeread_cb)(obj_index, data, wait_response, timeout)。 - Lua 层(
sml_callbacks.lua)实现mctp_writeread,内部通过mctp_infos.get_mctp_info(obj_index)获取 phy_addr、vendor_id,再调用mctp_lib.get_pcie_endpoint和ep:Request/ep:Send完成 MCTP 收发。 - 回调注册时机:
sml.init的timeout_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.so | lsi_init_ctrl_manage(ctrl_id, i2c_write, i2c_writeread) | StoreLib 内部,通过 I2C 回调访问 RAID 卡 |
| PMC | /usr/lib64/libsml_pmc.so | pmc_init_ctrl_manage(ctrl_id, mctp_writeread) | load_storagecore → pfn_register_mctp_func(mctp_writeread) |
| 华为 | /usr/lib64/libsml_histore.so | histore_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_list、get_ctrl_ld_list、get_ctrl_array_list获取控制器下的物理盘、逻辑盘、阵列列表,用于 Redfish/Web 等界面展示。 - 硬盘健康监控:通过
get_pd_info、get_pd_sas_smart_info、get_pd_slow_disk_info获取硬盘健康、SMART、慢盘信息,用于告警与亚健康检测。 - 逻辑盘配置:通过
create_ld_on_new_array、add_ld_on_exist_array、set_ld_*等接口创建、修改、删除逻辑盘。 - 硬盘定位与热备:通过
pd_operation实现硬盘定位灯控制、全局/专用热备设置。 - 固件升级:通过
pd_upgrade_firmware、pd_active_firmware实现硬盘固件升级与激活。 - 故障诊断:通过
pd_diag_*、get_ctrl_sas_phy_err、pd_diag_link_phy_error进行 PHY 误码、链路故障诊断。
限制条件
- 厂商支持:当前支持 LSI(StoreLib)、PMC(storageCore)、华为(historelib)三类 RAID 卡,新增厂商需在
sml_base下实现对应 vendor 适配。 - OOB 通道:LSI 通过 I2C + hwproxy 插件访问;华为、PMC 通过 MCTP over PCIe VDPCI 访问,需正确配置 MCTP 端点与
sml_callbacks中的mctp_writeread回调。 - 控制器索引:
ctrl_index从 0 开始,需与业务层(如 controller_object)的控制器 ID 对应。 - 超时:所有接口通过
timeout_call调用 smld,存在默认超时,超时会抛出sml_call timout异常。 - 并发:同一控制器的读写由 SML 内部加锁保护,但多控制器并发调用需注意 smld 服务负载。
接口使用样例
注册 I2C 通道 RAID 卡
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 卡
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
})获取控制器与物理盘信息
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 对象,含健康状态、容量、序列号等创建逻辑盘
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, -- 写策略
...
)物理盘操作(定位、热备)
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 卡支持
- 在
sml_base/src/vendor/下新增厂商目录(如vendor_xxx)。 - 实现
vendor.h中定义的接口:控制器注册/注销、PD/LD/Array 信息获取、操作执行等。 - 在
sml_base的适配逻辑中根据type_id选择对应 vendor。 - 在
l_sml_adapter中确保新厂商的 so 符号可被dlsym正确解析。
扩展控制器类型 ID
在 storage/include/hwproxy/plugins/sml/cmds.lua 中扩展 get_oob_interface_type 与 test_controller_vendor:
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 通道使用:
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 timout | smld 服务未启动或调用阻塞 | 检查 smld 是否正常运行;检查 I2C/MCTP 链路是否通畅 |
SML_ERR_CTRL_NOT_REGISTERED | 控制器未注册或已注销 | 确认 register_controller 已成功执行;检查 ctrl_index 是否有效 |
SML_ERR_CTRL_INDEX_INVALID | 控制器索引超出范围 | 确认 ctrl_index 与已注册控制器一致 |
SML_ERR_I2C_READ_WRITE_FAILED | I2C 通信失败 | 检查 hwproxy、I2C 拓扑、RAID 卡在位与供电 |
SML_ERR_MCTP_READ_WRITE_FAILED | MCTP 通信失败 | 检查 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_state 报 SML_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:诊断相关(拓扑不匹配、数据超时等)