diff options
author | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-05-20 13:03:42 -0400 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-11-04 15:53:03 -0400 |
commit | 6bbf1acddeed0bfb345a5578f9fcada16f1e514f (patch) | |
tree | 965663c2a38ff10bc88fb2096fc0adbef500b11d /drivers/block/nvme.c | |
parent | eac623ba7a91474a688eb5d0fcd0eaa6a56dc41c (diff) |
NVMe: Rework ioctls
Remove the special-purpose IDENTIFY, GET_RANGE_TYPE, DOWNLOAD_FIRMWARE
and ACTIVATE_FIRMWARE commands. Replace them with a generic ADMIN_CMD
ioctl that can submit any admin command.
Add a new ID ioctl that returns the namespace ID of the queried device.
It corresponds to the SCSI Idlun ioctl.
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme.c')
-rw-r--r-- | drivers/block/nvme.c | 128 |
1 files changed, 40 insertions, 88 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 | } |