linux下 nvme操作代码示例

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、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

/*
 * 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

#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的代码中提取,仅供参考。

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值