Index: sbin/nvmecontrol/Makefile =================================================================== --- sbin/nvmecontrol/Makefile +++ sbin/nvmecontrol/Makefile @@ -10,6 +10,7 @@ SRCS+= passthru.c SRCS+= identify_ext.c nvme_util.c nc_util.c SRCS+= selftest.c +SRCS+= feature.c MAN= nvmecontrol.8 LDFLAGS+= -rdynamic LIBADD+= util Index: sbin/nvmecontrol/feature.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/feature.c @@ -0,0 +1,417 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2021 Wanpeng Qian + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +/* Tables for command line parsing */ + +static cmd_fn_t feat; +static cmd_fn_t featget; +static cmd_fn_t featset; + +#define NONE8 0xffu +#define NONE32 0xffffffffu +#define NONE64 0xffffffffffffffffull +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } +#define OPT_END { NULL, 0, arg_none, NULL, NULL } + +/* Convert a zero-based value into a one-based value */ +#define ONE_BASED(zero) ((zero) + 1) +/* Convert a one-based value into a zero-based value */ +#define ZERO_BASED(one) ((one) - 1) + +#define NVME_FEAT_GET_SEL_SHIFT (8) +#define NVME_FEAT_GET_SEL_MASK (0x7) +#define NVME_FEAT_GET_FID_SHIFT (0) +#define NVME_FEAT_GET_FID_MASK (0xFF) + +#define NVME_FEAT_SET_SV_SHIFT (31) +#define NVME_FEAT_SET_SV_MASK (0x1) +#define NVME_FEAT_SET_FID_SHIFT (0) +#define NVME_FEAT_SET_FID_MASK (0xFF) + +#define NVME_FEAT_ARBITRATION_HPW_SHIFT (24) +#define NVME_FEAT_ARBITRATION_HPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_MPW_SHIFT (16) +#define NVME_FEAT_ARBITRATION_MPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_LPW_SHIFT (8) +#define NVME_FEAT_ARBITRATION_LPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_AB_SHIFT (0) +#define NVME_FEAT_ARBITRATION_AB_MASK (0x7) + +#define NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT (5) +#define NVME_FEAT_POWER_MANAGEMENT_WH_MASK (0x7) +#define NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT (0) +#define NVME_FEAT_POWER_MANAGEMENT_PS_MASK (0x1F) + +#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT (20) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK (0x3) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT (16) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK (0xFF) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT (0) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK (0xFFFF) + +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT (16) +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK (0xFFFF) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT (0) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK (0xFFFF) + +static const char * +feat_name_res[] = { + [0] = "Reserved", + [1] = "Arbitration", + [2] = "Power Management", + [3] = "LBA Range Type", + [4] = "Temperature Threshold", + [5] = "Error Recovery", + [6] = "Volatile Write Cache", + [7] = "Number of Queues", + [8] = "Interrupt Coalescing", + [9] = "Interrupt Vector Configuration", + [10] = "Write Atomicity Normal", + [11] = "Asynchronous Event Configuration", + [12] = "Autonomous Power State Transition", + [13] = "Host Memory Buffer", + [14] = "Timestamp", + [15] = "Keep Alive Timer", + [16] = "Host Controlled Thermal Management", + [17] = "Non-Operational Power State Config", +}; +static uint32_t feat_name_res_max = nitems(feat_name_res); + +static struct cmd feat_cmd = { + .name = "feature", + .fn = feat, + .descr = "Get Feature & Set Feature for specified device", + .ctx_size = 0, + .opts = NULL, + .args = NULL, +}; + +CMD_COMMAND(feat_cmd); + +static struct get_options { + uint8_t fid; + uint8_t sel; + uint32_t cdw11; + const char *dev; +} get_opt = { + .fid = NONE8, + .sel = 0, + .cdw11 = 0, + .dev = NULL, +}; + +static const struct opts get_opts[] = { + OPT("fid", 'f', arg_uint8, get_opt, fid, + "feature id (required)"), + OPT("sel", 's', arg_uint8, get_opt, sel, + "[0-3]current/default/saved/supported"), + OPT("cdw11", '1', arg_uint32, get_opt, cdw11, + "dword 11 value"), + OPT_END +}; + +static const struct args get_args[] = { + { arg_string, &get_opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd get_cmd = { + .name = "get", + .fn = featget, + .descr = "Get Feature for specified device", + .ctx_size = sizeof(get_opt), + .opts = get_opts, + .args = get_args, +}; + +CMD_SUBCOMMAND(feat_cmd, get_cmd); + +static struct set_options { + uint8_t fid; + bool save; + uint32_t cdw11; + uint32_t cdw12; + uint32_t cdw13; + uint32_t cdw14; + uint32_t cdw15; + const char *dev; +} set_opt = { + .fid = NONE8, + .save = false, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .dev = NULL, +}; + +static const struct opts set_opts[] = { + OPT("fid", 'f', arg_uint8, set_opt, fid, + "feature id (required)"), + OPT("save", 's', arg_none, set_opt, save, + "the controller shall save the attribute"), + OPT("cdw11", '1', arg_uint32, set_opt, cdw11, + "dword 11 value"), + OPT("cdw12", '2', arg_uint32, set_opt, cdw12, + "dword 12 value"), + OPT("cdw13", '3', arg_uint32, set_opt, cdw13, + "dword 13 value"), + OPT("cdw14", '4', arg_uint32, set_opt, cdw14, + "dword 14 value"), + OPT("cdw15", '5', arg_uint32, set_opt, cdw15, + "dword 15 value"), + OPT_END +}; + +static const struct args set_args[] = { + { arg_string, &set_opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd set_cmd = { + .name = "set", + .fn = featset, + .descr = "Set Feature for specified device", + .ctx_size = sizeof(set_opt), + .opts = set_opts, + .args = set_args, +}; + +CMD_SUBCOMMAND(feat_cmd, set_cmd); + +static void +print_feat_arbitration(uint32_t cdw0) +{ + + printf("High Priority Weight (HPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_HPW_SHIFT) & + NVME_FEAT_ARBITRATION_HPW_MASK)); + printf("Medium Priority Weight (MPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_MPW_SHIFT) & + NVME_FEAT_ARBITRATION_MPW_MASK)); + printf("Low Priority Weight (LPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_LPW_SHIFT) & + NVME_FEAT_ARBITRATION_LPW_MASK)); + printf("Arbitration Burst (AB): "); + if ((cdw0 & NVME_FEAT_ARBITRATION_AB_MASK) == NVME_FEAT_ARBITRATION_AB_MASK) + printf("no limit\n"); + else + printf("%d\n", 1 << (cdw0 & NVME_FEAT_ARBITRATION_AB_MASK)); +} + +static void +print_feat_power_management(uint32_t cdw0) +{ + + printf("Workload Hint (WH): %#x\n", + (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT) & + NVME_FEAT_POWER_MANAGEMENT_WH_MASK); + printf("Power State (PS): %#x\n", + (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT) & + NVME_FEAT_POWER_MANAGEMENT_PS_MASK); +} + +static void +print_feat_temperature_threshold(uint32_t cdw0) +{ + + printf("Threshold Type Select (THSEL): %#x\n", + (cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK); + printf("Threshold Temperature Select (TMPSEL): %#x\n", + (cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK); + printf("Temperature Threshold (TMPTH): "); + print_temp((cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK); +} + +static void +print_feat_number_of_queues(uint32_t cdw0) +{ + + printf("Number of I/O Completion Queues Allocated (NCQA): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT) & + NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK)); + printf("Number of I/O Submission Queues Allocated (NSQA): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT) & + NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK)); +} + +static void +featget(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + uint8_t fid, sel; + uint32_t cdw0, cdw11; + int fd; + + if (arg_parse(argc, argv, f)) + return; + + if (get_opt.fid == NONE8) { + fprintf(stderr, + "feature id not specified\n"); + arg_help(argc, argv, f); + } + if (get_opt.sel > 0x3) { + fprintf(stderr, + "select value over range\n"); + arg_help(argc, argv, f); + } + + fid = get_opt.fid; + sel = get_opt.sel; + cdw11 = get_opt.cdw11; + + open_dev(get_opt.dev, &fd, 1, 1); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_GET_FEATURES; + pt.cmd.cdw10 = htole32((sel << NVME_FEAT_GET_SEL_SHIFT) | + (fid << NVME_FEAT_GET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(cdw11); + pt.buf = NULL; + pt.len = 0; + pt.is_read = 1; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(EX_IOERR, "passthrough request failed"); + + printf("Get Feature: %s(0x%x)\n", + (fid < feat_name_res_max ? feat_name_res[fid] : "\0"), fid); + printf("=======================\n"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(EX_IOERR, "get feature returned error"); + + cdw0 = pt.cpl.cdw0; + + switch (fid) { + case NVME_FEAT_ARBITRATION: + print_feat_arbitration(cdw0); + break; + case NVME_FEAT_POWER_MANAGEMENT: + print_feat_power_management(cdw0); + break; + case NVME_FEAT_TEMPERATURE_THRESHOLD: + print_feat_temperature_threshold(cdw0); + break; + case NVME_FEAT_NUMBER_OF_QUEUES: + print_feat_number_of_queues(cdw0); + break; + default: + printf("cdw0 = %#x\n", cdw0); + break; + } + + close(fd); + exit(0); +} + +static void +featset(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + uint8_t fid; + bool save; + uint32_t cdw11, cdw12, cdw13, cdw14, cdw15; + int fd; + + if (arg_parse(argc, argv, f)) + return; + + if (set_opt.fid == NONE8) { + fprintf(stderr, + "feature id not specified\n"); + arg_help(argc, argv, f); + } + + fid = set_opt.fid; + save = set_opt.save; + cdw11 = set_opt.cdw11; + cdw12 = set_opt.cdw12; + cdw13 = set_opt.cdw13; + cdw14 = set_opt.cdw14; + cdw15 = set_opt.cdw15; + + open_dev(set_opt.dev, &fd, 1, 1); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_SET_FEATURES; + pt.cmd.cdw10 = htole32((save << NVME_FEAT_SET_SV_SHIFT) | + (fid << NVME_FEAT_SET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(cdw11); + pt.cmd.cdw12 = htole32(cdw12); + pt.cmd.cdw13 = htole32(cdw13); + pt.cmd.cdw14 = htole32(cdw14); + pt.cmd.cdw15 = htole32(cdw15); + pt.buf = NULL; + pt.len = 0; + pt.is_read = 0; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(EX_IOERR, "passthrough request failed"); + + printf("Set Feature: %s(0x%x)\n", + (fid < feat_name_res_max ? feat_name_res[fid] : "\0"), fid); + printf("=======================\n"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(EX_IOERR, "set feature returned error"); + else + printf("set feature success\n"); + + close(fd); + exit(0); +} + +static void +feat(const struct cmd *nf __unused, int argc, char *argv[]) +{ + + cmd_dispatch(argc, argv, &feat_cmd); +}