提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、nvme是什么?
NVMe (Non-Volatile Memory Express) 是一种专为闪存和其他非易失性存储介质设计的协议,它被设计用来提高存储设备的性能,尤其是那些通过 PCI Express (PCIe) 总线连接的固态硬盘 (SSD)。
下面的代码示例主要操作了 nvme的admin命令,读取nvme硬盘的信息
1,nvme_read_id_ctrl
2,nvme_read_id_ns
3,nvme_read_smart_log
二、代码示例
nvmecmds.h
c
/*
* nvmecmds.h
*
* Home page of code is: https://www.smartmontools.org
*
* Copyright (C) 2016-23 Christian Franke
*
* Original code from <linux/nvme.h>:
* Copyright (C) 2011-2014 Intel Corporation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef NVMECMDS_H
#define NVMECMDS_H
#define NVMECMDS_H_CVSID "$Id: nvmecmds.h 5471 2023-05-29 12:22:41Z chrfranke $"
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
// The code below was originally imported from <linux/nvme.h> include file from
// Linux kernel sources. Types from <linux/types.h> were replaced.
// Symbol names are unchanged but placed in a namespace to allow inclusion
// of the original <linux/nvme.h>.
// BEGIN: From <linux/nvme.h>
#define STATIC_ASSERT_H_CVSID "$Id: static_assert.h 4934 2019-07-01 20:54:14Z chrfranke $"
#if __cplusplus >= 201103 || _MSVC_LANG >= 201103
#define STATIC_ASSERT(x) static_assert((x), #x)
#elif __STDC_VERSION__ >= 201112
#define STATIC_ASSERT(x) _Static_assert((x), #x)
#elif __GNUC__ >= 4
#define STATIC_ASSERT(x) typedef char static_assertion[(x) ? 1 : -1] \
__attribute__((unused))
#else
#define STATIC_ASSERT(x) typedef char static_assertion[(x) ? 1 : -1]
#endif
struct nvme_error_log_page {
uint64_t error_count;
unsigned short sqid;
unsigned short cmdid;
unsigned short status_field;
unsigned short parm_error_location;
uint64_t lba;
unsigned int nsid;
unsigned char vs;
unsigned char resv[35];
};
STATIC_ASSERT(sizeof(struct nvme_error_log_page) == 64);
struct nvme_id_power_state {
unsigned short max_power; // centiwatts
unsigned char rsvd2;
unsigned char flags;
unsigned int entry_lat; // microseconds
unsigned int exit_lat; // microseconds
unsigned char read_tput;
unsigned char read_lat;
unsigned char write_tput;
unsigned char write_lat;
unsigned short idle_power;
unsigned char idle_scale;
unsigned char rsvd19;
unsigned short active_power;
unsigned char active_work_scale;
unsigned char rsvd23[9];
};
STATIC_ASSERT(sizeof(struct nvme_id_power_state) == 32);
struct nvme_id_ctrl {
unsigned short vid;
unsigned short ssvid;
char sn[20];
char mn[40];
char fr[8];
unsigned char rab;
unsigned char ieee[3];
unsigned char cmic;
unsigned char mdts;
unsigned short cntlid;
unsigned int ver;
unsigned int rtd3r;
unsigned int rtd3e;
unsigned int oaes;
unsigned int ctratt;
unsigned char rsvd100[156];
unsigned short oacs;
unsigned char acl;
unsigned char aerl;
unsigned char frmw;
unsigned char lpa;
unsigned char elpe;
unsigned char npss;
unsigned char avscc;
unsigned char apsta;
unsigned short wctemp;
unsigned short cctemp;
unsigned short mtfa;
unsigned int hmpre;
unsigned int hmmin;
unsigned char tnvmcap[16];
unsigned char unvmcap[16];
unsigned int rpmbs;
unsigned short edstt;
unsigned char dsto;
unsigned char fwug;
unsigned short kas;
unsigned short hctma;
unsigned short mntmt;
unsigned short mxtmt;
unsigned int sanicap;
unsigned char rsvd332[180];
unsigned char sqes;
unsigned char cqes;
unsigned short maxcmd;
unsigned int nn;
unsigned short oncs;
unsigned short fuses;
unsigned char fna;
unsigned char vwc;
unsigned short awun;
unsigned short awupf;
unsigned char nvscc;
unsigned char rsvd531;
unsigned short acwu;
unsigned char rsvd534[2];
unsigned int sgls;
unsigned char rsvd540[228];
char subnqn[256];
unsigned char rsvd1024[768];
unsigned int ioccsz;
unsigned int iorcsz;
unsigned short icdoff;
unsigned char ctrattr;
unsigned char msdbd;
unsigned char rsvd1804[244];
struct nvme_id_power_state psd[32];
unsigned char vs[1024];
};
STATIC_ASSERT(sizeof(struct nvme_id_ctrl) == 4096);
struct nvme_lbaf {
unsigned short ms;
unsigned char ds;
unsigned char rp;
};
STATIC_ASSERT(sizeof(struct nvme_lbaf) == 4);
struct nvme_id_ns {
uint64_t nsze;
uint64_t ncap;
uint64_t nuse;
unsigned char nsfeat;
unsigned char nlbaf;
unsigned char flbas;
unsigned char mc;
unsigned char dpc;
unsigned char dps;
unsigned char nmic;
unsigned char rescap;
unsigned char fpi;
unsigned char rsvd33;
unsigned short nawun;
unsigned short nawupf;
unsigned short nacwu;
unsigned short nabsn;
unsigned short nabo;
unsigned short nabspf;
unsigned char rsvd46[2];
unsigned char nvmcap[16];
unsigned char rsvd64[40];
unsigned char nguid[16];
unsigned char eui64[8];
struct nvme_lbaf lbaf[16];
unsigned char rsvd192[192];
unsigned char vs[3712];
};
STATIC_ASSERT(sizeof(struct nvme_id_ns) == 4096);
struct nvme_smart_log {
unsigned char critical_warning;
unsigned char temperature[2];
unsigned char avail_spare;
unsigned char spare_thresh;
unsigned char percent_used;
unsigned char rsvd6[26];
unsigned char data_units_read[16];
unsigned char data_units_written[16];
unsigned char host_reads[16];
unsigned char host_writes[16];
unsigned char ctrl_busy_time[16];
unsigned char power_cycles[16];
unsigned char power_on_hours[16];
unsigned char unsafe_shutdowns[16];
unsigned char media_errors[16];
unsigned char num_err_log_entries[16];
unsigned int warning_temp_time;
unsigned int critical_comp_time;
unsigned short temp_sensor[8];
unsigned int thm_temp1_trans_count;
unsigned int thm_temp2_trans_count;
unsigned int thm_temp1_total_time;
unsigned int thm_temp2_total_time;
unsigned char rsvd232[280];
};
STATIC_ASSERT(sizeof(struct nvme_smart_log) == 512);
enum nvme_admin_opcode {
//nvme_admin_delete_sq = 0x00,
//nvme_admin_create_sq = 0x01,
nvme_admin_get_log_page = 0x02,
//nvme_admin_delete_cq = 0x04,
//nvme_admin_create_cq = 0x05,
nvme_admin_identify = 0x06,
//nvme_admin_abort_cmd = 0x08,
//nvme_admin_set_features = 0x09,
//nvme_admin_get_features = 0x0a,
//nvme_admin_async_event = 0x0c,
//nvme_admin_ns_mgmt = 0x0d,
//nvme_admin_activate_fw = 0x10,
//nvme_admin_download_fw = 0x11,
nvme_admin_dev_self_test = 0x14, // NVMe 1.3
//nvme_admin_ns_attach = 0x15,
//nvme_admin_format_nvm = 0x80,
//nvme_admin_security_send = 0x81,
//nvme_admin_security_recv = 0x82,
};
// END: From <linux/nvme.h>
// Figure 213 of NVM Express(TM) Base Specification, revision 2.0a, July 2021
struct nvme_self_test_result {
uint8_t self_test_status;
uint8_t segment;
uint8_t valid;
uint8_t rsvd3;
uint8_t power_on_hours[8]; // unaligned LE 64
uint32_t nsid;
uint8_t lba[8]; // unaligned LE 64
uint8_t status_code_type;
uint8_t status_code;
uint8_t vendor_specific[2];
};
STATIC_ASSERT(sizeof(struct nvme_self_test_result) == 28);
// Figure 212 of NVM Express(TM) Base Specification, revision 2.0a, July 2021
struct nvme_self_test_log {
uint8_t current_operation;
uint8_t current_completion;
uint8_t rsvd2[2];
struct nvme_self_test_result results[20]; // [0] = newest
};
STATIC_ASSERT(sizeof(struct nvme_self_test_log) == 564);
#endif // NVMECMDS_H
nvme_test.c
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/nvme_ioctl.h>
#include "nvmecmds.h"
#define MY_DEV "/dev/nvme0"
void print_bin(char *data, int size)
{
int i;
if(data == NULL || size <= 0)
{
printf("data == NULL || size <= 0\n");
return;
}
printf("result: \n");
for (i = 0; i < size; i++) {
printf("%2.2x", data[i] & 0xff);
}
printf("\n");
return;
}
void print_nvme_smart_log(struct nvme_smart_log * sl)
{
int i;
if(sl == NULL)
{
return;
}
printf("sl->critical_warning = 0x%x\n"
"sl->temperature[0] = 0x%x\n"
"sl->temperature[1] = 0x%x\n"
"sl->avail_spare = %d\n"
"sl->spare_thresh = %d\n"
"sl->percent_used = %d\n"
,sl->critical_warning //临界警告状态。smart自测结果
,sl->temperature[0] // nvme当前温度 = temperature[1]*256 + temperature[0] - 273
,sl->temperature[1] //
,sl->avail_spare // 可用备用块百分比。
,sl->spare_thresh // 备用阈值百分比。
,sl->percent_used // 使用百分比。
);
}
void print_nvme_self_test_log(struct nvme_self_test_log * stl)
{
int i;
if(stl == NULL)
{
return;
}
printf("stl->current_operation = 0x%x\n"
"stl->current_completion = 0x%x\n"
,stl->current_operation
,stl->current_completion
);
for(i = 0; i < 20; i++)
{
printf("i = %d, results.self_test_status = %d\n"
"results.valid = %d\n"
"results.power_on_hours = %s\n"
, i, stl->results[i].self_test_status
, stl->results[i].valid
, stl->results[i].power_on_hours
);
}
}
void print_nvme_ns(struct nvme_id_ns * ns)
{
int i;
if(ns == NULL)
{
return;
}
printf("ns->nsze = %lld\n"
"ns->ncap = %lld\n"
"ns->nuse = %lld\n"
"ns->nsfeat = 0x%x\n"
"ns->nlbaf = 0x%x\n"
"ns->flbas = 0x%x\n"
,ns->nsze // 数据需要转换,暂未转换
,ns->ncap // 数据需要转换,暂未转换
,ns->nuse // 数据需要转换,暂未转换
,ns->nsfeat
,ns->nlbaf
,ns->flbas
);
}
void print_nvme_id_ctrl(struct nvme_id_ctrl * ctl)
{
int i;
if(ctl == NULL)
{
return;
}
printf("ctl->vid = 0x%x\n"
"ctl->ssvid = 0x%x\n"
"ctl->sn = %s\n"
"ctl->mn = %s\n"
"ctl->fr = %s\n"
"ctl->cntlid = 0x%x\n"
"ctl->ver = 0x%x\n"
"ctl->wctemp = %d\n"
"ctl->cctemp = %d\n"
"ctl->mtfa = %d\n"
"ctl->tnvmcap = %s\n"
"ctl->unvmcap = %s\n"
"ctl->nn = 0x%x\n"
"ctl->oacs = 0x%x\n"
,ctl->vid
,ctl->ssvid
,ctl->sn
,ctl->mn
,ctl->fr
,ctl->cntlid
,ctl->ver //nvme版本
,ctl->wctemp // warning 温度 需要 +(-273)
,ctl->cctemp // crit 温度 需要 +(-273)
,ctl->mtfa
,ctl->tnvmcap
,ctl->unvmcap
,ctl->nn //命名空间数量
,ctl->oacs //samrt支持相关
);
int self_test_sup = !!(ctl->oacs & 0x0010);
printf("self_test_sup = %d\n", self_test_sup);
for(i = 0; i < 32; i++)
{
printf("i = %d, psd.max_power = %d\n", i, ctl->psd[i].max_power);
}
print_bin(ctl->tnvmcap, 16);
print_bin(ctl->unvmcap, 16);
}
int nvme_read_smart_log()
{
int ret = 0;
int fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}
struct nvme_passthru_cmd n_cmd = {0};
struct nvme_smart_log n_sl = {0};
n_cmd.opcode = nvme_admin_get_log_page;
n_cmd.nsid = 0xffffffff;
n_cmd.addr = &n_sl;
n_cmd.data_len = sizeof(n_sl);
n_cmd.cdw10 = 0x02 | (((sizeof(n_sl) / 4) - 1) << 16);
n_cmd.cdw12 = 0x00;
ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &n_cmd);
if (ret < 0)
{
printf("ret = %d\n", ret);
}
printf("n_cmd.result = %d\n", n_cmd.result);
print_nvme_smart_log(&n_sl);
close(fd);
return ret;
}
//未测试成功,命令执行成功,结构体返回数据解析不太对
int nvme_read_selftest_log()
{
int ret = 0;
int fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}
struct nvme_passthru_cmd n_cmd = {0};
struct nvme_self_test_log n_s_t = {0};
n_cmd.opcode = nvme_admin_get_log_page;
n_cmd.nsid = 0xffffffff;
n_cmd.addr = &n_s_t;
n_cmd.data_len = sizeof(n_s_t);
n_cmd.cdw10 = 0x02 | (((sizeof(n_s_t) / 4) - 1) << 16);
n_cmd.cdw12 = 0x00;
ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &n_cmd);
if (ret < 0)
{
printf("ret = %d\n", ret);
}
printf("n_cmd.result = %d\n", n_cmd.result);
print_nvme_self_test_log(&n_s_t);
close(fd);
return ret;
}
//未测试通过,命令执行成功,结构体返回都是0
int nvme_do_selftest()
{
int ret = 0;
int fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}
struct nvme_passthru_cmd n_cmd = {0};
struct nvme_self_test_log n_s_t = {0};
n_cmd.opcode = nvme_admin_dev_self_test;
n_cmd.nsid = 0xffffffff;
n_cmd.addr = &n_s_t;
n_cmd.data_len = sizeof(n_s_t);
n_cmd.cdw10 = 0x02; // 0 = no test, 1 = short, 2 = extended(long), 0xf = abort
ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &n_cmd);
if (ret < 0)
{
printf("ret = %d\n", ret);
}
printf("n_cmd.result = %d\n", n_cmd.result);
print_nvme_self_test_log(&n_s_t);
close(fd);
return ret;
}
int nvme_read_id_ns()
{
int ret = 0;
int fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}
struct nvme_passthru_cmd n_cmd = {0};
struct nvme_id_ns n_id_ns = {0};
n_cmd.opcode = nvme_admin_identify;
n_cmd.nsid = 0x1;
n_cmd.addr = &n_id_ns;
n_cmd.data_len = sizeof(n_id_ns);
n_cmd.cdw10 = 0x00;
ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &n_cmd);
if (ret < 0)
{
printf("ret = %d\n", ret);
}
printf("n_cmd.result = %d\n", n_cmd.result);
print_nvme_ns(&n_id_ns);
close(fd);
return ret;
}
int nvme_read_id_ctrl()
{
int ret = 0;
int fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}
struct nvme_passthru_cmd n_cmd = {0};
struct nvme_id_ctrl n_id_ctl = {0};
n_cmd.opcode = nvme_admin_identify;
n_cmd.nsid = 0;
n_cmd.addr = &n_id_ctl;
n_cmd.data_len = sizeof(n_id_ctl);
n_cmd.cdw10 = 0x01;
ret = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &n_cmd);
if (ret < 0)
{
printf("ret = %d\n", ret);
}
printf("n_cmd.result = %d\n", n_cmd.result);
print_nvme_id_ctrl(&n_id_ctl);
close(fd);
return ret;
}
int main()
{
int cmd = 0;
int fd = -1;
int ret = 0;
char input[256] = {0};
/* fd = open(MY_DEV,O_RDWR);
if(fd == -1)
{
printf("open %s failed\n", MY_DEV);
return -1;
}*/
while(1)
{
cmd = 0;
printf("support cmd:\n");
printf("input cmd:\n");
if (fgets(input, sizeof(input), stdin) == NULL)
continue;
input[strcspn(input, "\n")] = 0;
printf("input = %s\n",input);
sscanf(input, "%d", &cmd);
printf("cmd = %d\n",cmd);
switch(cmd)
{
case 0:
return 0;
break;
case 1:
nvme_read_id_ctrl();
break;
case 2:
nvme_read_id_ns();
break;
case 3:
nvme_do_selftest();
break;
case 4:
nvme_read_selftest_log();
break;
case 5:
nvme_read_smart_log();
break;
default:
break;
}
}
close(fd);
return 0;
}
总结
nvme磁盘信息读取的代码示例,暂不完善,从开源工具smartmontools的代码中提取,仅供参考。