前言:
Nvme cli命令之get-log:根據nvme 協議要求和各廠商自己的需求存有不同的日志,get-log命令通過不同logid,用于讀取不同的log日志。
源碼開始:
我們從源碼中的nvme-builtin.h文件可以看到所有的nvme command,在23行找到get-log命令,以及它指向的函數get_log。
ENTRY("get-log", "Generic NVMe get log, returns log in raw format", get_log)
我們打開get-log函數,可以看到以下為get-log的一些參數定義:
int err, fd;
struct config {
__u32 namespace_id;
__u32 log_id;
__u32 log_len;
__u32 aen;
__u64 lpo;
__u8 lsp;
int rae;
int raw_binary;
};
<!-- 這裡config是定義了get-log需要用到的參數 -->
struct config cfg = {
.namespace_id = NVME_NSID_ALL,
.log_id = 0xffffffff,
.log_len = 0,
.lpo = NVME_NO_LOG_LPO,
.lsp = NVME_NO_LOG_LSP,
.rae = 0,
};
<!-- 這裡cfg定義了參數的默認值 -->
const struct argconfig_commandline_options command_line_options[] = {
{"namespace-id", 'n', "NUM", CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace_id},
{"log-id", 'i', "NUM", CFG_POSITIVE, &cfg.log_id, required_argument, log_id},
{"log-len", 'l', "NUM", CFG_POSITIVE, &cfg.log_len, required_argument, log_len},
{"aen", 'a', "NUM", CFG_POSITIVE, &cfg.aen, required_argument, aen},
{"raw-binary", 'b', "", CFG_NONE, &cfg.raw_binary, no_argument, raw_binary},
{"lpo", 'o', "NUM", CFG_LONG, &cfg.lpo, required_argument, lpo},
{"lsp", 's', "NUM", CFG_BYTE, &cfg.lsp, required_argument, lsp},
{"rae", 'r', "", CFG_NONE, &cfg.rae, no_argument, rae},
{NULL}
};
<!-- command_line_options定義了命令行輸入的值對應關系 -->
随後parse_and_open函數對命令行參數進行解析和對nvme設備做打開操作,并将解析結果存在cfg當中:
fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
if (fd < 0)
return fd;
parse_and_open函數中打開nvme 設備:
devicename = basename(dev);
err = open(dev, O_RDONLY);
再往下是對解析結果做判斷處理;
if(cfg.aen){
cfg.log_len = 4096;
cfg.log_id = (cfg.aen >> 16) & 0xff;
}
if(cfg.log_id>0xff){
fprintf(stderr, "Invalid log identifier: %d. Valid range: 0-255\n", cfg.log_id);
err = EINVAL;
goto close_fd;
}
if(!cfg.log_len){
fprintf(stderr, "non-zero log-len is required param\n");
err = EINVAL;
} else {
unsignedchar*log;
log = malloc(cfg.log_len);
if (!log) {
fprintf(stderr, "could not alloc buffer for log: %s\n",
strerror(errno));
err = EINVAL;
goto close_fd;
}
在以上處理完成後,會将cfg結構體當中的參數傳入nvme_get_log13函數中,做進一步的處理和下發命令:
err = nvme_get_log13(fd, cfg.namespace_id, cfg.log_id,cfg.lsp, cfg.lpo, 0, cfg.rae, cfg.log_len, log);
nvme_get_log13函數具體内容:
int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
__u16 lsi, bool rae, __u32 data_len, void *data)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_get_log_page,
.nsid = nsid,
.addr = (__u64)(uintptr_t) data,
.data_len = data_len,
};
__u32 numd = (data_len >> 2) - 1;
__u16 numdu = numd >> 16, numdl = numd & 0xffff;
cmd.cdw10 = log_id | (numdl << 16) | (rae ? 1 << 15 : 0);
if (lsp)
cmd.cdw10 |= lsp << 8;
cmd.cdw11 = numdu | (lsi << 16);
cmd.cdw12 = lpo;
cmd.cdw13 = (lpo >> 32);
return nvme_submit_admin_passthru(fd, &cmd);
}
nvme_get_log13函數定義cmd的結構體,可以看到包含opcode,nsid,addr,data_len幾個參數,函數中間對參數進行處理後通過nvme_submit_admin_passthru函數下發command,并返回執行結果;
首先看cmd結構體裡的參數
再看函數的中間處理:
這裡函數根據nvme 協議中規定,使用cdw10-cdw13幾個字段;
協議中get-log的使用字段
CDW10的處理:
根據協議中CDW10的定義,CDW10是為32bit的值,其中0-7bit代表log_id,8-11bit代表lsp,12-14作為保留,15bit代表RAE,16-31bit代表NUMDL;
get-log的CDW10
所以在代碼中能看到以下處理,NUMDL左移16位,RAE左移15位,LSP左移8位,cdw10等于各參數相加的值;
cmd.cdw10 = log_id | (numdl << 16) | (rae ? 1 << 15 : 0);
if (lsp)
cmd.cdw10 |= lsp << 8;
協議定義numdl為用戶傳入的data_len的dword低16位返回,numdu為高16位,CDW11協議定義0-15bit為NUMDU,numdu和numdl為DWORD,即4字節,協議定義為0 base的值,所以numd = (data_len >> 2) - 1;所以有以下處理,numdu為numd的高16位,往右移16位,numdl為numd的低16位,與0xffff相與:
__u32 numd = (data_len >> 2) - 1;
__u16 numdu = numd >> 16, numdl = numd & 0xffff;
get-log的CDW12-13
cmd.cdw12 = lpo;
cmd.cdw13 = (lpo >> 32);
協議中定義cdw12和cdw13為log offset的dword 高低位,cdw12等于lpo,cdw13先右移2位再右移16位;
最終通過nvme_submit_admin_passthru下發get-log命令。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!