Skip to content

Commit 84fef62

Browse files
Keith Buschaxboe
authored andcommitted
nvme: check admin passthru command effects
The NVMe standard provides a command effects log page so the host may be aware of special requirements it may need to do for a particular command. For example, the command may need to run with IO quiesced to prevent timeouts or undefined behavior, or it may change the logical block formats that determine how the host needs to construct future commands. This patch saves the nvme command effects log page if the controller supports it, and performs appropriate actions before and after an admin passthrough command is completed. If the controller does not support the command effects log page, the driver will define the effects for known opcodes. The nvme format and santize are the only commands in this patch with known effects. Signed-off-by: Keith Busch <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent c627c48 commit 84fef62

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

drivers/nvme/host/core.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ static DEFINE_IDA(nvme_instance_ida);
7272
static dev_t nvme_chr_devt;
7373
static struct class *nvme_class;
7474

75+
static void nvme_ns_remove(struct nvme_ns *ns);
76+
static int nvme_revalidate_disk(struct gendisk *disk);
77+
7578
static __le32 nvme_get_log_dw10(u8 lid, size_t size)
7679
{
7780
return cpu_to_le32((((size / 4) - 1) << 16) | lid);
@@ -992,12 +995,87 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
992995
metadata, meta_len, io.slba, NULL, 0);
993996
}
994997

998+
static u32 nvme_known_admin_effects(u8 opcode)
999+
{
1000+
switch (opcode) {
1001+
case nvme_admin_format_nvm:
1002+
return NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
1003+
NVME_CMD_EFFECTS_CSE_MASK;
1004+
case nvme_admin_sanitize_nvm:
1005+
return NVME_CMD_EFFECTS_CSE_MASK;
1006+
default:
1007+
break;
1008+
}
1009+
return 0;
1010+
}
1011+
1012+
static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
1013+
u8 opcode)
1014+
{
1015+
u32 effects = 0;
1016+
1017+
if (ns) {
1018+
if (ctrl->effects)
1019+
effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
1020+
if (effects & ~NVME_CMD_EFFECTS_CSUPP)
1021+
dev_warn(ctrl->device,
1022+
"IO command:%02x has unhandled effects:%08x\n",
1023+
opcode, effects);
1024+
return 0;
1025+
}
1026+
1027+
if (ctrl->effects)
1028+
effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
1029+
else
1030+
effects = nvme_known_admin_effects(opcode);
1031+
1032+
/*
1033+
* For simplicity, IO to all namespaces is quiesced even if the command
1034+
* effects say only one namespace is affected.
1035+
*/
1036+
if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) {
1037+
nvme_start_freeze(ctrl);
1038+
nvme_wait_freeze(ctrl);
1039+
}
1040+
return effects;
1041+
}
1042+
1043+
static void nvme_update_formats(struct nvme_ctrl *ctrl)
1044+
{
1045+
struct nvme_ns *ns;
1046+
1047+
mutex_lock(&ctrl->namespaces_mutex);
1048+
list_for_each_entry(ns, &ctrl->namespaces, list) {
1049+
if (ns->disk && nvme_revalidate_disk(ns->disk))
1050+
nvme_ns_remove(ns);
1051+
}
1052+
mutex_unlock(&ctrl->namespaces_mutex);
1053+
}
1054+
1055+
static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects)
1056+
{
1057+
/*
1058+
* Revalidate LBA changes prior to unfreezing. This is necessary to
1059+
* prevent memory corruption if a logical block size was changed by
1060+
* this command.
1061+
*/
1062+
if (effects & NVME_CMD_EFFECTS_LBCC)
1063+
nvme_update_formats(ctrl);
1064+
if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK))
1065+
nvme_unfreeze(ctrl);
1066+
if (effects & NVME_CMD_EFFECTS_CCC)
1067+
nvme_init_identify(ctrl);
1068+
if (effects & (NVME_CMD_EFFECTS_NIC | NVME_CMD_EFFECTS_NCC))
1069+
nvme_queue_scan(ctrl);
1070+
}
1071+
9951072
static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
9961073
struct nvme_passthru_cmd __user *ucmd)
9971074
{
9981075
struct nvme_passthru_cmd cmd;
9991076
struct nvme_command c;
10001077
unsigned timeout = 0;
1078+
u32 effects;
10011079
int status;
10021080

10031081
if (!capable(CAP_SYS_ADMIN))
@@ -1023,10 +1101,13 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
10231101
if (cmd.timeout_ms)
10241102
timeout = msecs_to_jiffies(cmd.timeout_ms);
10251103

1104+
effects = nvme_passthru_start(ctrl, ns, cmd.opcode);
10261105
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
10271106
(void __user *)(uintptr_t)cmd.addr, cmd.data_len,
10281107
(void __user *)(uintptr_t)cmd.metadata, cmd.metadata,
10291108
0, &cmd.result, timeout);
1109+
nvme_passthru_end(ctrl, effects);
1110+
10301111
if (status >= 0) {
10311112
if (put_user(cmd.result, &ucmd->result))
10321113
return -EFAULT;
@@ -1759,6 +1840,25 @@ static int nvme_get_log(struct nvme_ctrl *ctrl, u8 log_page, void *log,
17591840
return nvme_submit_sync_cmd(ctrl->admin_q, &c, log, size);
17601841
}
17611842

1843+
static int nvme_get_effects_log(struct nvme_ctrl *ctrl)
1844+
{
1845+
int ret;
1846+
1847+
if (!ctrl->effects)
1848+
ctrl->effects = kzalloc(sizeof(*ctrl->effects), GFP_KERNEL);
1849+
1850+
if (!ctrl->effects)
1851+
return 0;
1852+
1853+
ret = nvme_get_log(ctrl, NVME_LOG_CMD_EFFECTS, ctrl->effects,
1854+
sizeof(*ctrl->effects));
1855+
if (ret) {
1856+
kfree(ctrl->effects);
1857+
ctrl->effects = NULL;
1858+
}
1859+
return ret;
1860+
}
1861+
17621862
/*
17631863
* Initialize the cached copies of the Identify data and various controller
17641864
* register in our nvme_ctrl structure. This should be called as soon as
@@ -1794,6 +1894,12 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
17941894
return -EIO;
17951895
}
17961896

1897+
if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) {
1898+
ret = nvme_get_effects_log(ctrl);
1899+
if (ret < 0)
1900+
return ret;
1901+
}
1902+
17971903
nvme_init_subnqn(ctrl, id);
17981904

17991905
if (!ctrl->identified) {
@@ -2713,6 +2819,7 @@ static void nvme_free_ctrl(struct device *dev)
27132819

27142820
ida_simple_remove(&nvme_instance_ida, ctrl->instance);
27152821
ida_destroy(&ctrl->ns_ida);
2822+
kfree(ctrl->effects);
27162823

27172824
ctrl->ops->free_ctrl(ctrl);
27182825
}

drivers/nvme/host/nvme.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ struct nvme_ctrl {
174174
bool subsystem;
175175
unsigned long quirks;
176176
struct nvme_id_power_state psd[32];
177+
struct nvme_effects_log *effects;
177178
struct work_struct scan_work;
178179
struct work_struct async_event_work;
179180
struct delayed_work ka_work;

include/linux/nvme.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ enum {
267267
NVME_CTRL_OACS_SEC_SUPP = 1 << 0,
268268
NVME_CTRL_OACS_DIRECTIVES = 1 << 5,
269269
NVME_CTRL_OACS_DBBUF_SUPP = 1 << 8,
270+
NVME_CTRL_LPA_CMD_EFFECTS_LOG = 1 << 1,
270271
};
271272

272273
struct nvme_lbaf {
@@ -395,6 +396,21 @@ struct nvme_fw_slot_info_log {
395396
__u8 rsvd64[448];
396397
};
397398

399+
enum {
400+
NVME_CMD_EFFECTS_CSUPP = 1 << 0,
401+
NVME_CMD_EFFECTS_LBCC = 1 << 1,
402+
NVME_CMD_EFFECTS_NCC = 1 << 2,
403+
NVME_CMD_EFFECTS_NIC = 1 << 3,
404+
NVME_CMD_EFFECTS_CCC = 1 << 4,
405+
NVME_CMD_EFFECTS_CSE_MASK = 3 << 16,
406+
};
407+
408+
struct nvme_effects_log {
409+
__le32 acs[256];
410+
__le32 iocs[256];
411+
__u8 resv[2048];
412+
};
413+
398414
enum {
399415
NVME_SMART_CRIT_SPARE = 1 << 0,
400416
NVME_SMART_CRIT_TEMPERATURE = 1 << 1,
@@ -681,6 +697,7 @@ enum nvme_admin_opcode {
681697
nvme_admin_format_nvm = 0x80,
682698
nvme_admin_security_send = 0x81,
683699
nvme_admin_security_recv = 0x82,
700+
nvme_admin_sanitize_nvm = 0x84,
684701
};
685702

686703
enum {
@@ -712,6 +729,7 @@ enum {
712729
NVME_LOG_ERROR = 0x01,
713730
NVME_LOG_SMART = 0x02,
714731
NVME_LOG_FW_SLOT = 0x03,
732+
NVME_LOG_CMD_EFFECTS = 0x05,
715733
NVME_LOG_DISC = 0x70,
716734
NVME_LOG_RESERVATION = 0x80,
717735
NVME_FWACT_REPL = (0 << 3),

0 commit comments

Comments
 (0)