diff options
-rw-r--r-- | drivers/block/nvme.c | 128 | ||||
-rw-r--r-- | include/linux/nvme.h | 34 |
2 files changed, 63 insertions, 99 deletions
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f5e51a6116e3..9e3c724b95c3 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c | |||
@@ -1033,51 +1033,6 @@ static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, | |||
1033 | put_page(sg_page(&sg[i])); | 1033 | put_page(sg_page(&sg[i])); |
1034 | } | 1034 | } |
1035 | 1035 | ||
1036 | static int nvme_submit_user_admin_command(struct nvme_dev *dev, | ||
1037 | unsigned long addr, unsigned length, | ||
1038 | struct nvme_command *cmd) | ||
1039 | { | ||
1040 | int err, nents, tmplen = length; | ||
1041 | struct scatterlist *sg; | ||
1042 | struct nvme_prps *prps; | ||
1043 | |||
1044 | nents = nvme_map_user_pages(dev, 0, addr, length, &sg); | ||
1045 | if (nents < 0) | ||
1046 | return nents; | ||
1047 | prps = nvme_setup_prps(dev, &cmd->common, sg, &tmplen, GFP_KERNEL); | ||
1048 | if (tmplen != length) | ||
1049 | err = -ENOMEM; | ||
1050 | else | ||
1051 | err = nvme_submit_admin_cmd(dev, cmd, NULL); | ||
1052 | nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); | ||
1053 | nvme_free_prps(dev, prps); | ||
1054 | return err ? -EIO : 0; | ||
1055 | } | ||
1056 | |||
1057 | static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) | ||
1058 | { | ||
1059 | struct nvme_command c; | ||
1060 | |||
1061 | memset(&c, 0, sizeof(c)); | ||
1062 | c.identify.opcode = nvme_admin_identify; | ||
1063 | c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); | ||
1064 | c.identify.cns = cpu_to_le32(cns); | ||
1065 | |||
1066 | return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); | ||
1067 | } | ||
1068 | |||
1069 | static int nvme_get_range_type(struct nvme_ns *ns, unsigned long addr) | ||
1070 | { | ||
1071 | struct nvme_command c; | ||
1072 | |||
1073 | memset(&c, 0, sizeof(c)); | ||
1074 | c.features.opcode = nvme_admin_get_features; | ||
1075 | c.features.nsid = cpu_to_le32(ns->ns_id); | ||
1076 | c.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); | ||
1077 | |||
1078 | return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); | ||
1079 | } | ||
1080 | |||
1081 | static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) | 1036 | static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) |
1082 | { | 1037 | { |
1083 | struct nvme_dev *dev = ns->dev; | 1038 | struct nvme_dev *dev = ns->dev; |
@@ -1096,10 +1051,11 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) | |||
1096 | switch (io.opcode) { | 1051 | switch (io.opcode) { |
1097 | case nvme_cmd_write: | 1052 | case nvme_cmd_write: |
1098 | case nvme_cmd_read: | 1053 | case nvme_cmd_read: |
1054 | case nvme_cmd_compare: | ||
1099 | nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, | 1055 | nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, |
1100 | length, &sg); | 1056 | length, &sg); |
1101 | default: | 1057 | default: |
1102 | return -EFAULT; | 1058 | return -EINVAL; |
1103 | } | 1059 | } |
1104 | 1060 | ||
1105 | if (nents < 0) | 1061 | if (nents < 0) |
@@ -1137,70 +1093,66 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) | |||
1137 | return status; | 1093 | return status; |
1138 | } | 1094 | } |
1139 | 1095 | ||
1140 | static int nvme_download_firmware(struct nvme_ns *ns, | 1096 | static int nvme_user_admin_cmd(struct nvme_ns *ns, |
1141 | struct nvme_dlfw __user *udlfw) | 1097 | struct nvme_admin_cmd __user *ucmd) |
1142 | { | 1098 | { |
1143 | struct nvme_dev *dev = ns->dev; | 1099 | struct nvme_dev *dev = ns->dev; |
1144 | struct nvme_dlfw dlfw; | 1100 | struct nvme_admin_cmd cmd; |
1145 | struct nvme_command c; | 1101 | struct nvme_command c; |
1146 | int nents, status, length; | 1102 | int status, length, nents = 0; |
1147 | struct scatterlist *sg; | 1103 | struct scatterlist *sg; |
1148 | struct nvme_prps *prps; | 1104 | struct nvme_prps *prps = NULL; |
1149 | 1105 | ||
1150 | if (copy_from_user(&dlfw, udlfw, sizeof(dlfw))) | 1106 | if (!capable(CAP_SYS_ADMIN)) |
1107 | return -EACCES; | ||
1108 | if (copy_from_user(&cmd, ucmd, sizeof(cmd))) | ||
1151 | return -EFAULT; | 1109 | return -EFAULT; |
1152 | if (dlfw.length >= (1 << 30)) | ||
1153 | return -EINVAL; | ||
1154 | length = dlfw.length * 4; | ||
1155 | |||
1156 | nents = nvme_map_user_pages(dev, 1, dlfw.addr, length, &sg); | ||
1157 | if (nents < 0) | ||
1158 | return nents; | ||
1159 | 1110 | ||
1160 | memset(&c, 0, sizeof(c)); | 1111 | memset(&c, 0, sizeof(c)); |
1161 | c.dlfw.opcode = nvme_admin_download_fw; | 1112 | c.common.opcode = cmd.opcode; |
1162 | c.dlfw.numd = cpu_to_le32(dlfw.length); | 1113 | c.common.flags = cmd.flags; |
1163 | c.dlfw.offset = cpu_to_le32(dlfw.offset); | 1114 | c.common.nsid = cpu_to_le32(cmd.nsid); |
1164 | prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); | 1115 | c.common.cdw2[0] = cpu_to_le32(cmd.cdw2); |
1165 | if (length != dlfw.length * 4) | 1116 | c.common.cdw2[1] = cpu_to_le32(cmd.cdw3); |
1117 | c.common.cdw10[0] = cpu_to_le32(cmd.cdw10); | ||
1118 | c.common.cdw10[1] = cpu_to_le32(cmd.cdw11); | ||
1119 | c.common.cdw10[2] = cpu_to_le32(cmd.cdw12); | ||
1120 | c.common.cdw10[3] = cpu_to_le32(cmd.cdw13); | ||
1121 | c.common.cdw10[4] = cpu_to_le32(cmd.cdw14); | ||
1122 | c.common.cdw10[5] = cpu_to_le32(cmd.cdw15); | ||
1123 | |||
1124 | length = cmd.data_len; | ||
1125 | if (cmd.data_len) { | ||
1126 | nents = nvme_map_user_pages(dev, 1, cmd.addr, length, &sg); | ||
1127 | if (nents < 0) | ||
1128 | return nents; | ||
1129 | prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); | ||
1130 | } | ||
1131 | |||
1132 | if (length != cmd.data_len) | ||
1166 | status = -ENOMEM; | 1133 | status = -ENOMEM; |
1167 | else | 1134 | else |
1168 | status = nvme_submit_admin_cmd(dev, &c, NULL); | 1135 | status = nvme_submit_admin_cmd(dev, &c, NULL); |
1169 | nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); | 1136 | if (cmd.data_len) { |
1170 | nvme_free_prps(dev, prps); | 1137 | nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, sg, |
1138 | nents); | ||
1139 | nvme_free_prps(dev, prps); | ||
1140 | } | ||
1171 | return status; | 1141 | return status; |
1172 | } | 1142 | } |
1173 | 1143 | ||
1174 | static int nvme_activate_firmware(struct nvme_ns *ns, unsigned long arg) | ||
1175 | { | ||
1176 | struct nvme_dev *dev = ns->dev; | ||
1177 | struct nvme_command c; | ||
1178 | |||
1179 | memset(&c, 0, sizeof(c)); | ||
1180 | c.common.opcode = nvme_admin_activate_fw; | ||
1181 | c.common.rsvd10[0] = cpu_to_le32(arg); | ||
1182 | |||
1183 | return nvme_submit_admin_cmd(dev, &c, NULL); | ||
1184 | } | ||
1185 | |||
1186 | static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, | 1144 | static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, |
1187 | unsigned long arg) | 1145 | unsigned long arg) |
1188 | { | 1146 | { |
1189 | struct nvme_ns *ns = bdev->bd_disk->private_data; | 1147 | struct nvme_ns *ns = bdev->bd_disk->private_data; |
1190 | 1148 | ||
1191 | switch (cmd) { | 1149 | switch (cmd) { |
1192 | case NVME_IOCTL_IDENTIFY_NS: | 1150 | case NVME_IOCTL_ID: |
1193 | return nvme_identify(ns, arg, 0); | 1151 | return ns->ns_id; |
1194 | case NVME_IOCTL_IDENTIFY_CTRL: | 1152 | case NVME_IOCTL_ADMIN_CMD: |
1195 | return nvme_identify(ns, arg, 1); | 1153 | return nvme_user_admin_cmd(ns, (void __user *)arg); |
1196 | case NVME_IOCTL_GET_RANGE_TYPE: | ||
1197 | return nvme_get_range_type(ns, arg); | ||
1198 | case NVME_IOCTL_SUBMIT_IO: | 1154 | case NVME_IOCTL_SUBMIT_IO: |
1199 | return nvme_submit_io(ns, (void __user *)arg); | 1155 | return nvme_submit_io(ns, (void __user *)arg); |
1200 | case NVME_IOCTL_DOWNLOAD_FW: | ||
1201 | return nvme_download_firmware(ns, (void __user *)arg); | ||
1202 | case NVME_IOCTL_ACTIVATE_FW: | ||
1203 | return nvme_activate_firmware(ns, arg); | ||
1204 | default: | 1156 | default: |
1205 | return -ENOTTY; | 1157 | return -ENOTTY; |
1206 | } | 1158 | } |
diff --git a/include/linux/nvme.h b/include/linux/nvme.h index a19304fefa7d..c96ab0f5ef6f 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h | |||
@@ -153,11 +153,11 @@ struct nvme_common_command { | |||
153 | __u8 flags; | 153 | __u8 flags; |
154 | __u16 command_id; | 154 | __u16 command_id; |
155 | __le32 nsid; | 155 | __le32 nsid; |
156 | __u64 rsvd2; | 156 | __u32 cdw2[2]; |
157 | __le64 metadata; | 157 | __le64 metadata; |
158 | __le64 prp1; | 158 | __le64 prp1; |
159 | __le64 prp2; | 159 | __le64 prp2; |
160 | __u32 rsvd10[6]; | 160 | __u32 cdw10[6]; |
161 | }; | 161 | }; |
162 | 162 | ||
163 | struct nvme_rw_command { | 163 | struct nvme_rw_command { |
@@ -388,17 +388,29 @@ struct nvme_user_io { | |||
388 | __u16 appmask; | 388 | __u16 appmask; |
389 | }; | 389 | }; |
390 | 390 | ||
391 | struct nvme_dlfw { | 391 | struct nvme_admin_cmd { |
392 | __u8 opcode; | ||
393 | __u8 flags; | ||
394 | __u16 rsvd1; | ||
395 | __u32 nsid; | ||
396 | __u32 cdw2; | ||
397 | __u32 cdw3; | ||
398 | __u64 metadata; | ||
392 | __u64 addr; | 399 | __u64 addr; |
393 | __u32 length; /* In dwords */ | 400 | __u32 metadata_len; |
394 | __u32 offset; /* In dwords */ | 401 | __u32 data_len; |
402 | __u32 cdw10; | ||
403 | __u32 cdw11; | ||
404 | __u32 cdw12; | ||
405 | __u32 cdw13; | ||
406 | __u32 cdw14; | ||
407 | __u32 cdw15; | ||
408 | __u32 timeout_ms; | ||
409 | __u32 result; | ||
395 | }; | 410 | }; |
396 | 411 | ||
397 | #define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) | 412 | #define NVME_IOCTL_ID _IO('N', 0x40) |
398 | #define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) | 413 | #define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd) |
399 | #define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) | 414 | #define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io) |
400 | #define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x43, struct nvme_user_io) | ||
401 | #define NVME_IOCTL_DOWNLOAD_FW _IOW('N', 0x44, struct nvme_dlfw) | ||
402 | #define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45) | ||
403 | 415 | ||
404 | #endif /* _LINUX_NVME_H */ | 416 | #endif /* _LINUX_NVME_H */ |