ZBLOG

详细解释static int get_health_logs(struct spdk_nvme_ctrlr *ctrlr, struct health_entry *health)函数

函数名称:get_health_logs

函数功能:获取NVMe设备的健康信息日志

参数说明:

  • struct spdk_nvme_ctrlr *ctrlr:指向NVMe控制器的指针
  • struct health_entry *health:指向存储健康信息的结构体指针

返回值:int类型,成功返回0,失败返回负数错误码

函数详细解释:

该函数用于获取NVMe设备的健康信息日志。在NVMe设备中,有一个称为“SMART(Self-Monitoring, Analysis and Reporting Technology)”的技术,可以用于监测和报告设备的健康状况和其他相关信息。该函数通过NVMe控制器发送NVMe Admin命令获取健康信息,并将其存储在给定的结构体中。

函数流程:

  1. 首先检查输入参数是否有效,并分配命令和数据缓冲区。

  2. 构造NVMe Admin命令,设置命令的操作码、命名空间ID和CNS(Controller NVM Subsystem),并将命令发送给NVMe控制器。

  3. 如果命令发送成功,则等待命令完成及响应结果;否则返回错误码。

  4. 解析响应结果,将健康信息复制到给定的结构体中。

  5. 释放命令和数据缓冲区,返回0表示成功。

具体实现代码如下:

static int
get_health_logs(struct spdk_nvme_ctrlr *ctrlr, struct health_entry *health)
{
    int rc;
    struct spdk_nvme_cmd cmd = {};
    struct spdk_nvme_health_information_page health_page = {};
    void *buffer = NULL;

    /* 检查输入参数 */
    if (ctrlr == NULL || health == NULL) {
        return -1;
    }

    /* 分配命令和数据缓冲区 */
    buffer = spdk_zmalloc(sizeof(struct spdk_nvme_health_information_page),
                          SPDK_CACHE_LINE_SIZE, NULL, SPDK_ENV_SOCKET_ID_ANY,
                          SPDK_MALLOC_DMA);
    if (buffer == NULL) {
        SPDK_ERRLOG("Failed to allocate memory for health information page.\n");
        return -ENOMEM;
    }

    /* 构造NVMe Admin命令 */
    memset(&cmd, 0, sizeof(cmd));
    cmd.opc = SPDK_NVME_OPC_GET_LOG_PAGE;      //获取日志页操作码
    cmd.nsid = 0xFFFFFFFF;                    //特殊值,表示所有的命名空间
    cmd.cdw10 = SPDK_NVME_LOG_HEALTH_INFORMATION;   //健康信息日志页
    cmd.cdw11 = 0x00000000;                    //保留字段
    cmd.cdw12 = 0x00000000;                    //保留字段
    cmd.cdw13 = ((sizeof(health_page) / sizeof(uint32_t)) - 1);     //日志页大小

    /* 发送NVMe Admin命令 */
    rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, buffer, sizeof(health_page),
                                       get_health_logs_completion, health);
    if (rc != 0) {
        SPDK_ERRLOG("Failed to send NVMe Admin command: rc=%d\n", rc);
        spdk_free(buffer);
        return rc;
    }

    /* 等待命令完成及响应结果 */
    spdk_thread_poll(ctrlr->adminq_thread, 0, 0);

    spdk_free(buffer);

    return 0;
}

其中,spdk_nvme_ctrlr_cmd_admin_raw()函数用于发送NVMe Admin命令,并等待命令完成及响应结果。该函数的第5个参数为命令完成回调函数,用于在命令完成时调用。

健康信息日志页的格式如下:

struct spdk_nvme_health_information_page {
        uint16_t temperature;   //温度
        uint8_t  avail_spare;   //可用空间
        uint8_t  spare_thresh;  //空间阈值
        uint8_t  percent_used;  //使用率百分比
        uint8_t  reserved[26];  //保留字段
        struct spdk_nvme_health_information_controller_data ctrlr_data;   //控制器数据
        struct spdk_nvme_health_information_namespace_data ns_data[];     //命名空间数据
} __attribute__((packed));

其中,控制器数据和命名空间数据的结构体定义如下:

struct spdk_nvme_health_information_controller_data {
        uint8_t  critical_warning;               //关键警告状态
        uint8_t  temperature_1;                  //温度1
        uint8_t  temperature_2;                  //温度2
        uint8_t  temperature_3;                  //温度3
        uint16_t media_errors;                    //媒体错误数
        uint8_t  num_err_log_entries;             //错误日志记录数
        uint32_t warning_temp_time;               //警告温度时间
        uint32_t critical_temp_time;              //关键温度时间
        uint16_t temp_sensor1;                     //温度传感器1
        uint16_t temp_sensor2;                     //温度传感器2
        uint16_t temp_sensor3;                     //温度传感器3
        uint8_t  reserved[302];                   //保留字段
} __attribute__((packed));

struct spdk_nvme_health_information_namespace_data {
        uint64_t nsze;                          //命名空间大小
        uint64_t ncap;                          //命名空间容量
        uint64_t nuse;                          //命名空间使用量
        uint64_t ndlbws;                        //命名空间数据读带宽
        uint64_t ndlbs;                         //命名空间数据读请求数量
        uint64_t ndlwbs;                        //命名空间数据写带宽
        uint64_t ndlws;                         //命名空间数据写请求数量
        uint64_t nvmcap[2];                     //NVM总能力
        uint8_t  reserved[40];                  //保留字段
} __attribute__((packed));

解析健康信息日志页的代码如下:

static void
get_health_logs_completion(void *arg, const struct spdk_nvme_cpl *cpl)
{
    struct health_entry *health = arg;
    struct spdk_nvme_health_information_page *health_page = health->buf;
    struct spdk_nvme_health_information_controller_data *ctrlr_data = &health_page->ctrlr_data;
    struct spdk_nvme_health_information_namespace_data *ns_data;
    uint32_t nsid;
    int i;

    /* 检查响应结果 */
    if (spdk_nvme_cpl_is_error(cpl)) {
        SPDK_ERRLOG("Failed to get health information page: cdw0=%#x\n", cpl->cdw0);
        health->rc = -EIO;
        return;
    }

    /* 复制健康信息到结构体中 */
    health->temperature = health_page->temperature;
    health->avail_spare = health_page->avail_spare;
    health->spare_thresh = health_page->spare_thresh;
    health->percent_used = health_page->percent_used;

    ctrlr_data->critical_warning &= 0x70;   //保留关键警告状态的高4位
    health->ctrlr_warning = ctrlr_data->critical_warning;
    health->temp_sensor1 = ctrlr_data->temp_sensor1;
    health->temp_sensor2 = ctrlr_data->temp_sensor2;
    health->temp_sensor3 = ctrlr_data->temp_sensor3;
    health->media_errors = ctrlr_data->media_errors;
    health->num_err_log_entries = ctrlr_data->num_err_log_entries;

    for (i = 0; i < SPDK_NVME_MAX_NS_SUPPORTED; i++) {
        ns_data = &health_page->ns_data[i];
        nsid = spdk_nvme_ctrlr_get_ns(ctrlr, i + 1)->id;

        if (nsid == 0) {
            break;
        }

        health->ns[i].id = nsid;
        health->ns[i].size = ns_data->nsze;
        health->ns[i].capacity = ns_data->ncap;
        health->ns[i].used = ns_data->nuse;
        health->ns[i].data_read_bandwidth = ns_data->ndlbws;
        health->ns[i].data_read_requests = ns_data->ndlbs;
        health->ns[i].data_write_bandwidth = ns_data->ndlwbs;
        health->ns[i].data_write_requests = ns_data->ndlws;
    }
}

该函数首先检查NVMe Admin命令的响应结果,如果出错则返回错误码。否则,将健康信息日志页中的温度、可用空间、空间阈值和使用率复制到结构体中,并解析控制器数据和命名空间数据,将其存储在相应的结构体中。

最后,调用该函数时,需要传入一个health_entry类型的结构体指针,用于存储健康信息。其中,结构体定义如下:

struct health_entry {
    void *buf;                                      //缓冲区指针
    int rc;                                         //返回码
    uint16_t temperature;                           //温度
    uint8_t avail_spare;                            //可用空间
    uint8_t spare_thresh;                           //空间阈值
    uint8_t percent_used;                           //使用率百分比
    uint8_t ctrlr_warning;                          //控制器警告状态
    uint16_t temp_sensor1;                          //温度传感器1
    uint16_t temp_sensor2;                          //温度传感器2
    uint16_t temp_sensor3;                          //温度传感器3
    uint16_t media_errors;                          //媒体错误数
    uint8_t num_err_log_entries;                    //错误日志记录数
    struct {
        uint32_t id;                                //命名空间ID
        uint64_t size;                              //命名空间大小
        uint64_t capacity;                          //命名空间容量
        uint64_t used;                              //命名空间使用量
        uint64_t data_read_bandwidth;               //命名空间数据读带宽
        uint64_t data_read_requests;                //命名空间数据读请求数量
        uint64_t data_write_bandwidth;              //命名空间数据写带宽
        uint64_t data_write_requests;               //命名空间数据写请求数量
    } ns[SPDK_NVME_MAX_NS_SUPPORTED];               //命名空间数组
} __attribute__((aligned(SPDK_CACHE_LINE_SIZE)));

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=52

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?