TEMPORARY:

Restore access to SD IOCTLs for sd-tools until they can be
re-written and/or upstreamed with the util-linux blk* tools.

Once sd-tools and zdm-tools are using the BIO based blk* tools then
this patch should be reverted.

Signed-off-by: Shaun Tancheff <shaun.tanch...@seagate.com>
---
 block/Kconfig                       |  12 +
 block/Makefile                      |   1 +
 block/blk-zoned-ctrl.c              | 642 ++++++++++++++++++++++++++++++++++++
 block/scsi_ioctl.c                  |   6 +
 drivers/scsi/sd.c                   |  19 ++
 include/linux/blk-zoned-ctrl.h      |  51 +++
 include/uapi/linux/Kbuild           |   1 +
 include/uapi/linux/blk-zoned-ctrl.h |  70 ++++
 8 files changed, 802 insertions(+)
 create mode 100644 block/blk-zoned-ctrl.c
 create mode 100644 include/linux/blk-zoned-ctrl.h
 create mode 100644 include/uapi/linux/blk-zoned-ctrl.h

diff --git a/block/Kconfig b/block/Kconfig
index 0363cd7..c7324ee 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -113,6 +113,18 @@ config BLK_DEV_THROTTLING
 
        See Documentation/cgroups/blkio-controller.txt for more information.
 
+config BLK_ZONED_CTRL
+       bool "DEPRECATED. Support sending zac/zbc commands..."
+       default y
+       ---help---
+       Support sending close/finish/open/reset wp, and report zones
+       to zoned based devices. Support sending ZAC/ZBC commands to
+       block devices.
+
+       Say yes here if you have a storage device that is has T10 ZBC
+       or T13 ZAC command support for zoned based storage.
+       If in doubt, say N.
+
 config BLK_CMDLINE_PARSER
        bool "Block device command line partition parser"
        default n
diff --git a/block/Makefile b/block/Makefile
index 9eda232..f921ecc 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_BOUNCE)  += bounce.o
 obj-$(CONFIG_BLK_DEV_BSG)      += bsg.o
 obj-$(CONFIG_BLK_DEV_BSGLIB)   += bsg-lib.o
 obj-$(CONFIG_BLK_CGROUP)       += blk-cgroup.o
+obj-$(CONFIG_BLK_ZONED_CTRL)   += blk-zoned-ctrl.o
 obj-$(CONFIG_BLK_DEV_THROTTLING)       += blk-throttle.o
 obj-$(CONFIG_IOSCHED_NOOP)     += noop-iosched.o
 obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o
diff --git a/block/blk-zoned-ctrl.c b/block/blk-zoned-ctrl.c
new file mode 100644
index 0000000..078b0cd
--- /dev/null
+++ b/block/blk-zoned-ctrl.c
@@ -0,0 +1,642 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tanch...@seagate.com>
+ * XiaoDong Han <xiaodong.h....@seagate.com>
+ *
+ * This file is licensed under  the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+#include <linux/sched/sysctl.h>
+
+#include "blk.h"
+#include <linux/blk-zoned-ctrl.h>
+#include <linux/ata.h>
+
+/*
+ * for max sense size
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dbg.h>
+#include <linux/ata.h>
+
+#define ZBC_TIMEOUT               (30 * HZ)
+#define ZBC_MAX_RETRIES                     5
+#define CMD_LEN                            16
+#define INQUIRY_CMDLEN              6
+
+/**
+ * ATA pass through 16.
+ */
+#define ZAC_PASS_THROUGH16_OPCODE  ATA_16
+#define ZAC_PASS_THROUGH16_CDB_LEN 16
+
+/**
+ * ATA commands.
+ */
+#define ZAC_ATA_OPCODE_IDENTIFY    ATA_CMD_ID_ATA
+
+static inline void _len_to_cmd_zbc(u8 *cmd, u32 _len)
+{
+       __be32 len = cpu_to_be32(_len);
+
+       memcpy(cmd, &len, sizeof(len));
+}
+
+static inline void _lba_to_cmd_zbc(u8 *cmd, u64 _lba)
+{
+       __be64 lba = cpu_to_be64(_lba);
+
+       memcpy(cmd, &lba, sizeof(lba));
+}
+
+static inline u16 zc_get_word(u8 *buf)
+{
+       u16 w = buf[1];
+
+       w <<= 8;
+       w |= buf[0];
+       return w;
+}
+
+static void pr_debug_scsi_cmd(const char *func_name, u8 *cmd)
+{
+#define FMT_LBA "lba:%02x%02x%02x%02x%02x%02x%02x%02x "
+#define FMT_LEN "len:%02x%02x%02x%02x"
+
+       pr_debug("%s: %02x:%02x " FMT_LBA FMT_LEN " %02x %02x\n",
+               func_name, cmd[0],  cmd[1],
+               cmd[2],  cmd[3],  cmd[4],  cmd[5],
+               cmd[6],  cmd[7],  cmd[8],  cmd[9],
+               cmd[10], cmd[11], cmd[12], cmd[13],
+               cmd[14], cmd[15]);
+
+}
+
+/* NOTE: this is basically scsi_execute */
+int blk_cmd_execute(struct request_queue *queue,
+                          const unsigned char *cmd,
+                          int data_direction,
+                          void *buffer,
+                          unsigned bufflen,
+                          unsigned char *sense,
+                          int timeout,
+                          int retries,
+                          u64 flags,
+                          int *resid)
+{
+       struct request *req;
+       int write = (data_direction == DMA_TO_DEVICE);
+       int ret = DRIVER_ERROR << 24;
+
+       req = blk_get_request(queue, write, GFP_KERNEL);
+       if (IS_ERR(req))
+               return ret;
+       blk_rq_set_block_pc(req);
+
+       if (bufflen &&  blk_rq_map_kern(queue, req,
+                                       buffer, bufflen, GFP_KERNEL))
+               goto out;
+
+       req->cmd_len = COMMAND_SIZE(cmd[0]);
+       memcpy(req->cmd, cmd, req->cmd_len);
+       req->sense = sense;
+       req->sense_len = 0;
+       req->retries = retries;
+       req->timeout = timeout;
+       req->cmd_flags |= flags | REQ_QUIET | REQ_PREEMPT;
+
+       /*
+        * head injection *required* here otherwise quiesce won't work
+        */
+       blk_execute_rq(req->q, NULL, req, 1);
+
+       /*
+        * Some devices (USB mass-storage in particular) may transfer
+        * garbage data together with a residue indicating that the data
+        * is invalid.  Prevent the garbage from being misinterpreted
+        * and prevent security leaks by zeroing out the excess data.
+        */
+       if (unlikely(req->resid_len > 0 && req->resid_len <= bufflen))
+               memset(buffer + (bufflen - req->resid_len), 0, req->resid_len);
+
+       if (resid)
+               *resid = req->resid_len;
+       ret = req->errors;
+ out:
+       blk_put_request(req);
+
+       return ret;
+}
+EXPORT_SYMBOL(blk_cmd_execute);
+
+int blk_cmd_with_sense(struct gendisk *disk,
+       u8 *cmd, int data_direction,
+       void *buf, u32 buf_len, u8 *sense_buffer)
+{
+       struct request_queue *queue = disk->queue;
+       int rc;
+       struct scsi_sense_hdr sshdr = { 0 };
+
+       if (!sense_buffer) {
+               pr_err("scsi cmd exec: sense buffer is NULL\n");
+               return -1;
+       }
+
+       rc = blk_cmd_execute(queue, cmd, data_direction, buf, buf_len,
+               sense_buffer, ZBC_TIMEOUT, ZBC_MAX_RETRIES, 0, NULL);
+
+       pr_debug("%s: %s -> 0x%08x [h:%02x d:%02x m:%02x s:%02x]\n", __func__,
+                disk->disk_name, rc, host_byte(rc), driver_byte(rc),
+                msg_byte(rc), status_byte(rc));
+
+       scsi_normalize_sense(sense_buffer, SCSI_SENSE_BUFFERSIZE, &sshdr);
+       if (host_byte(rc)
+           || (driver_byte(rc) && (driver_byte(rc) != DRIVER_SENSE))
+           || (status_byte(rc) && (status_byte(rc) != CHECK_CONDITION))) {
+               pr_err("exec scsi cmd failed,opcode:%d\n", cmd[0]);
+               if (driver_byte(rc) & DRIVER_SENSE)
+                       pr_err("%s: %s", __func__, disk->disk_name);
+
+               return -1;
+       } else if ((driver_byte(rc) == DRIVER_SENSE)
+                   && ((cmd[0] == ATA_16) || (cmd[0] == ATA_12))) {
+               if (sense_buffer[21] != 0x50) {
+                       pr_err("%s: ATA pass through command failed\n",
+                               __func__);
+                       return -1;
+               }
+       } else if (rc) {
+               if ((driver_byte(rc) == DRIVER_SENSE)
+                   && (status_byte(rc) == CHECK_CONDITION)
+                   && (sense_buffer[0] != 0)) {
+                       pr_err("%s: Something else failed\n", __func__);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(blk_cmd_with_sense);
+
+int blk_zoned_report(struct gendisk *disk,
+                       u64 start_lba,
+                       u8 opt,
+                       struct bdev_zone_report *buf,
+                       size_t bufsz)
+{
+       int ret = 0;
+       u8 cmd[CMD_LEN] = {0};
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = {0};
+
+       cmd[0] = ZBC_REPORT_ZONES;
+       cmd[1] = ZBC_REPORT_OPT;
+
+       _lba_to_cmd_zbc(&cmd[2],  start_lba);
+       _len_to_cmd_zbc(&cmd[10], (u32)bufsz);
+
+       cmd[14] = opt;
+
+       pr_debug_scsi_cmd(__func__, cmd);
+       ret = blk_cmd_with_sense(disk, cmd, DMA_FROM_DEVICE, buf, bufsz,
+                                       &sense_buf[0]);
+       return ret;
+}
+EXPORT_SYMBOL(blk_zoned_report);
+
+int blk_zoned_inquiry(struct gendisk *disk, u8 evpd, u8 pg_op,
+       u16 mx_resp_len, u8 *buf)
+{
+       int ret = 0;
+       u8 cmd[INQUIRY_CMDLEN] = {0};
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = {0};
+       __be16 slen = cpu_to_be16(mx_resp_len);
+       u16 len = (__force u16)slen;
+
+       if (pg_op != 0xb1) {
+               pr_err("Unsupported Page Code %02x. Expected 0xb1\n", pg_op);
+               return -1;
+       }
+
+       cmd[0] = INQUIRY;
+       if (evpd)
+               cmd[1] |= 1;
+       cmd[2] = pg_op;
+       cmd[3] = len & 0xff;
+       cmd[4] = (len >> 8) & 0xff;
+
+       pr_debug("%s: cmd: %02x:%02x:%02x:%02x:%02x:%02x\n",
+               __func__, cmd[0],  cmd[1], cmd[2],  cmd[3],  cmd[4],  cmd[5]);
+
+       ret = blk_cmd_with_sense(disk, cmd, DMA_FROM_DEVICE,
+                                       buf, mx_resp_len, &sense_buf[0]);
+       if (ret != 0) {
+               pr_err("%s: inquiry failed\n", disk->disk_name);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL(blk_zoned_inquiry);
+
+static int blk_zoned_cmd(struct gendisk *disk, u64 start_lba, u8 command)
+{
+       int ret = 0;
+       u8 cmd[CMD_LEN] = {0};
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = {0};
+       u8 all_bit = 0;
+
+       pr_debug("zoned cmd (%x): on %s, start_lba %lld\n",
+               command, disk->disk_name, start_lba);
+
+       if (start_lba == ~0ul) {
+               all_bit = 1;
+               start_lba = 0;
+       }
+
+       cmd[0] = ZBC_ACTION;
+       cmd[1] = command;
+
+       _lba_to_cmd_zbc(&cmd[2], start_lba);
+
+       cmd[14] = all_bit;
+       pr_debug_scsi_cmd(__func__, cmd);
+
+       ret = blk_cmd_with_sense(disk, cmd, DMA_FROM_DEVICE, NULL, 0,
+               &sense_buf[0]);
+       if (ret != 0) {
+               pr_err("%s: zone command %d failed\n",
+                       disk->disk_name, command);
+               return -1;
+       }
+       return ret;
+}
+
+int blk_zoned_close(struct gendisk *disk, u64 start_lba)
+{
+       return blk_zoned_cmd(disk, start_lba, ZBC_SA_ZONE_CLOSE);
+}
+EXPORT_SYMBOL(blk_zoned_close);
+
+int blk_zoned_finish(struct gendisk *disk, u64 start_lba)
+{
+       return blk_zoned_cmd(disk, start_lba, ZBC_SA_ZONE_FINISH);
+}
+EXPORT_SYMBOL(blk_zoned_finish);
+
+int blk_zoned_open(struct gendisk *disk, u64 start_lba)
+{
+       return blk_zoned_cmd(disk, start_lba, ZBC_SA_ZONE_OPEN);
+}
+EXPORT_SYMBOL(blk_zoned_open);
+
+int blk_zoned_reset_wp(struct gendisk *disk, u64 start_lba)
+{
+       return blk_zoned_cmd(disk, start_lba, ZBC_SA_RESET_WP);
+}
+EXPORT_SYMBOL(blk_zoned_reset_wp);
+
+static inline void _lba_to_cmd_ata(u8 *cmd, u64 _lba)
+{
+       cmd[1] =  _lba        & 0xff;
+       cmd[3] = (_lba >>  8) & 0xff;
+       cmd[5] = (_lba >> 16) & 0xff;
+       cmd[0] = (_lba >> 24) & 0xff;
+       cmd[2] = (_lba >> 32) & 0xff;
+       cmd[4] = (_lba >> 40) & 0xff;
+}
+
+/*
+ * ata-16 passthrough byte 1:
+ *   multiple [bits 7:5]
+ *   protocol [bits 4:1]
+ *   ext      [bit    0]
+ */
+static inline u8 ata16byte1(u8 multiple, u8 protocol, u8 ext)
+{
+       return ((multiple & 0x7) << 5) | ((protocol & 0xF) << 1) | (ext & 0x01);
+}
+
+int blk_zoned_report_ata(struct gendisk *disk, u64 start_lba, u8 opt,
+                        struct bdev_zone_report *buf, size_t bufsz)
+{
+       int ret = 0;
+       u8 cmd[ZAC_PASS_THROUGH16_CDB_LEN] = { 0 };
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = { 0 };
+
+       cmd[0] = ZAC_PASS_THROUGH16_OPCODE;
+       cmd[1] = ata16byte1(0, ATAPI_PROT_PIO, 1);
+       cmd[2] = 0x0e;
+
+       cmd[4] = ATA_SUBCMD_REP_ZONES;
+       cmd[3] = opt;
+
+       cmd[5] = (bufsz / 512) >> 8;
+       cmd[6] = (bufsz / 512) & 0xff;
+
+       _lba_to_cmd_ata(&cmd[7], start_lba);
+
+       cmd[13] = 1 << 6;
+       cmd[14] = ATA_CMD_ZONE_MAN_IN;
+
+       pr_debug_scsi_cmd(__func__, cmd);
+
+       ret = blk_cmd_with_sense(disk, cmd, DMA_FROM_DEVICE, buf, bufsz,
+                                       &sense_buf[0]);
+       return ret;
+}
+EXPORT_SYMBOL(blk_zoned_report_ata);
+
+int blk_zoned_identify_ata(struct gendisk *disk, struct zoned_identity *ident)
+{
+       int ret = 0;
+       u8 identify_cmd[ZAC_PASS_THROUGH16_CDB_LEN] = { 0 };
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = { 0 };
+       u8 buffer[512] = { 0 };
+       int flag = 0;
+
+       ident->type_id = NOT_ZONED;
+
+       if (!disk)
+               return -1;
+
+       identify_cmd[0] = ZAC_PASS_THROUGH16_OPCODE;
+       identify_cmd[1] = ata16byte1(0, ATA_PROT_NCQ, 1);
+       identify_cmd[2] = 0xe;
+       identify_cmd[6] = 0x1;
+       identify_cmd[8] = 0x1;
+       identify_cmd[14] = ZAC_ATA_OPCODE_IDENTIFY;
+
+       ret = blk_cmd_with_sense(disk, identify_cmd, DMA_FROM_DEVICE,
+                                buffer, sizeof(buffer), &sense_buf[0]);
+       if (ret != 0) {
+               pr_err("%s: identify failed.\n", disk->disk_name);
+               goto out;
+       }
+
+       flag = zc_get_word(&buffer[138]);
+       if ((flag & 0x3) == 0x1) {
+               ident->type_id = HOST_AWARE;
+               pr_debug("%s: is SMR-HostAware\n", disk->disk_name);
+       } else {
+               ret = -1;
+               pr_debug("%s: not HostAware\n", disk->disk_name);
+       }
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL(blk_zoned_identify_ata);
+
+static int _blk_zoned_command_ata(struct gendisk *disk, u64 start_lba,
+                                 u8 sub_command)
+{
+       int ret = 0;
+       u8 cmd[ZAC_PASS_THROUGH16_CDB_LEN] = { 0 };
+       u8 sense_buf[SCSI_SENSE_BUFFERSIZE] = { 0 };
+
+       pr_debug("zoned command (%x): %s, start_lba %llx\n",
+                sub_command, disk->disk_name, start_lba);
+
+       cmd[0] = ZAC_PASS_THROUGH16_OPCODE;
+       cmd[1] = ata16byte1(0, 3, 1);
+       cmd[4] = sub_command; /* [feature] */
+
+       if (start_lba == ~0ul || start_lba == ~1ul)
+               cmd[3] = 0x1; /* [hob_feature] apply command to all zones */
+       else
+               _lba_to_cmd_ata(&cmd[7], start_lba);
+
+       cmd[13] = 1 << 6;
+       cmd[14] = ATA_CMD_ZONE_MAN_OUT;
+
+       pr_debug_scsi_cmd(__func__, cmd);
+
+       ret = blk_cmd_with_sense(disk, cmd, DMA_NONE, NULL, 0, &sense_buf[0]);
+       if (ret != 0) {
+               pr_err("%s: command %d failed\n", disk->disk_name, sub_command);
+               return -1;
+       }
+       return ret;
+}
+
+int blk_zoned_close_ata(struct gendisk *disk, u64 start_lba)
+{
+       return _blk_zoned_command_ata(disk, start_lba, ATA_SUBCMD_CLOSE_ZONES);
+}
+EXPORT_SYMBOL(blk_zoned_close_ata);
+
+int blk_zoned_finish_ata(struct gendisk *disk, u64 start_lba)
+{
+       return _blk_zoned_command_ata(disk, start_lba, ATA_SUBCMD_FINISH_ZONES);
+}
+EXPORT_SYMBOL(blk_zoned_finish_ata);
+
+int blk_zoned_open_ata(struct gendisk *disk, u64 start_lba)
+{
+       return _blk_zoned_command_ata(disk, start_lba, ATA_SUBCMD_OPEN_ZONES);
+}
+EXPORT_SYMBOL(blk_zoned_open_ata);
+
+int blk_zoned_reset_wp_ata(struct gendisk *disk, u64 start_lba)
+{
+       return _blk_zoned_command_ata(disk, start_lba, ATA_SUBCMD_RESET_WP);
+}
+EXPORT_SYMBOL(blk_zoned_reset_wp_ata);
+
+int _inquiry_ioctl(struct gendisk *disk, void __user *parg)
+{
+       int error = 0;
+       size_t result_size = 0;
+       size_t alloc_size = PAGE_SIZE;
+       struct zoned_inquiry *inq = kmalloc(alloc_size, GFP_KERNEL);
+       u8 extended;
+
+       if (!inq) {
+               error = -ENOMEM;
+               goto out;
+       }
+       if (copy_from_user(inq, parg, sizeof(*inq))) {
+               error = -EFAULT;
+               goto out;
+       }
+       result_size = inq->mx_resp_len + offsetof(struct zoned_inquiry, result);
+       if (result_size > alloc_size) {
+               void *tmp;
+
+               alloc_size = result_size;
+               tmp = krealloc(inq, alloc_size, GFP_KERNEL);
+               if (!tmp) {
+                       error = -ENOMEM;
+                       goto out;
+               }
+               inq = tmp;
+       }
+
+       extended = inq->evpd & 0x7f;
+       if (inq->evpd & ZOPT_USE_ATA_PASS) {
+               struct zoned_identity ident;
+
+               pr_debug("%s: using ata passthrough.\n", __func__);
+               error = blk_zoned_identify_ata(disk, &ident);
+               inq->result[8] = ident.type_id << 4;
+       } else {
+               error = blk_zoned_inquiry(disk, extended,   inq->pg_op,
+                                         inq->mx_resp_len, inq->result);
+       }
+       if (error) {
+               error = -EFAULT;
+               goto out;
+       }
+       if (copy_to_user(parg, inq, result_size)) {
+               error = -EFAULT;
+               goto out;
+       }
+
+out:
+       kfree(inq);
+
+       return error;
+}
+EXPORT_SYMBOL(_inquiry_ioctl);
+
+int _zone_close_ioctl(struct gendisk *disk, unsigned long arg)
+{
+       int error = -EFAULT;
+
+       if (arg & 1) {
+               if (arg != ~0ul)
+                       arg &= ~1ul; /* ~1 :: 0xFF...FE */
+               error = blk_zoned_close_ata(disk, arg);
+       } else {
+               if (arg == ~1ul)
+                       arg = ~0ul;
+               error = blk_zoned_close(disk, arg);
+       }
+
+       return error;
+}
+EXPORT_SYMBOL(_zone_close_ioctl);
+
+int _zone_finish_ioctl(struct gendisk *disk, unsigned long arg)
+{
+       int error = -EFAULT;
+
+       if (arg & 1) {
+               if (arg != ~0ul)
+                       arg &= ~1ul; /* ~1 :: 0xFF...FE */
+               error = blk_zoned_finish_ata(disk, arg);
+       } else {
+               if (arg == ~1ul)
+                       arg = ~0ul;
+               error = blk_zoned_finish(disk, arg);
+       }
+
+       return error;
+}
+EXPORT_SYMBOL(_zone_finish_ioctl);
+
+int _zone_open_ioctl(struct gendisk *disk, unsigned long arg)
+{
+       int error = -EFAULT;
+
+       if (arg & 1) {
+               if (arg != ~0ul)
+                       arg &= ~1ul; /* ~1 :: 0xFF...FE */
+               error = blk_zoned_open_ata(disk, arg);
+       } else {
+               if (arg == ~1ul)
+                       arg = ~0ul;
+               error = blk_zoned_open(disk, arg);
+       }
+
+       return error;
+}
+EXPORT_SYMBOL(_zone_open_ioctl);
+
+int _reset_wp_ioctl(struct gendisk *disk, unsigned long arg)
+{
+       int error = -EFAULT;
+
+       if (arg & 1) {
+               if (arg != ~0ul)
+                       arg &= ~1ul; /* ~1 :: 0xFF...FE */
+               error = blk_zoned_reset_wp_ata(disk, arg);
+       } else {
+               if (arg == ~1ul)
+                       arg = ~0ul;
+               error = blk_zoned_reset_wp(disk, arg);
+       }
+
+       return error;
+}
+EXPORT_SYMBOL(_reset_wp_ioctl);
+
+int _report_zones_ioctl(struct gendisk *disk, void __user *parg)
+{
+       int error = -EFAULT;
+       struct bdev_zone_report_io *zone_iodata = NULL;
+       u32 alloc_size = max(PAGE_SIZE, sizeof(*zone_iodata));
+       u8 opt = 0;
+
+       zone_iodata = kmalloc(alloc_size, GFP_KERNEL);
+       if (!zone_iodata) {
+               error = -ENOMEM;
+               goto report_zones_out;
+       }
+       if (copy_from_user(zone_iodata, parg, sizeof(*zone_iodata))) {
+               error = -EFAULT;
+               goto report_zones_out;
+       }
+       if (zone_iodata->data.in.return_page_count > alloc_size) {
+               void *tmp;
+
+               alloc_size = zone_iodata->data.in.return_page_count;
+               if (alloc_size < KMALLOC_MAX_SIZE) {
+                       tmp = krealloc(zone_iodata, alloc_size, GFP_KERNEL);
+                       if (!tmp) {
+                               error = -ENOMEM;
+                               goto report_zones_out;
+                       }
+                       zone_iodata = tmp;
+               } else {
+                       /* Result requires DMA capable memory */
+                       pr_err("Not enough memory available for request.\n");
+                       error = -ENOMEM;
+                       goto report_zones_out;
+               }
+       }
+       opt = zone_iodata->data.in.report_option & 0x7F;
+       if (zone_iodata->data.in.report_option & ZOPT_USE_ATA_PASS)
+               error = blk_zoned_report_ata(disk,
+                               zone_iodata->data.in.zone_locator_lba,
+                               opt, &zone_iodata->data.out, alloc_size);
+       else
+               error = blk_zoned_report(disk,
+                               zone_iodata->data.in.zone_locator_lba,
+                               opt, &zone_iodata->data.out, alloc_size);
+
+       if (error)
+               goto report_zones_out;
+
+       if (copy_to_user(parg, zone_iodata, alloc_size))
+               error = -EFAULT;
+
+report_zones_out:
+       kfree(zone_iodata);
+       return error;
+}
+EXPORT_SYMBOL(_report_zones_ioctl);
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 0774799..5790f59 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -21,6 +21,7 @@
 #include <linux/string.h>
 #include <linux/module.h>
 #include <linux/blkdev.h>
+#include <linux/blk-zoned-ctrl.h>
 #include <linux/capability.h>
 #include <linux/completion.h>
 #include <linux/cdrom.h>
@@ -705,6 +706,11 @@ int scsi_verify_blk_ioctl(struct block_device *bd, 
unsigned int cmd)
        case SG_GET_RESERVED_SIZE:
        case SG_SET_RESERVED_SIZE:
        case SG_EMULATED_HOST:
+       case SCSI_IOCTL_INQUIRY:
+       case SCSI_IOCTL_REPORT_ZONES:
+       case SCSI_IOCTL_RESET_WP:
+       case SCSI_IOCTL_OPEN_ZONE:
+       case SCSI_IOCTL_CLOSE_ZONE:
                return 0;
        case CDROM_GET_CAPABILITY:
                /* Keep this until we remove the printk below.  udev sends it
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 3cb0200..726544c 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -45,6 +45,7 @@
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/blkpg.h>
+#include <linux/blk-zoned-ctrl.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/string_helpers.h>
@@ -1479,6 +1480,24 @@ static int sd_ioctl(struct block_device *bdev, fmode_t 
mode,
                case SCSI_IOCTL_GET_BUS_NUMBER:
                        error = scsi_ioctl(sdp, cmd, p);
                        break;
+               case SCSI_IOCTL_INQUIRY:
+                       error = _inquiry_ioctl(disk, p);
+                       break;
+               case SCSI_IOCTL_CLOSE_ZONE:
+                       error = _zone_close_ioctl(disk, arg);
+                       break;
+               case SCSI_IOCTL_FINISH_ZONE:
+                       error = _zone_finish_ioctl(disk, arg);
+                       break;
+               case SCSI_IOCTL_OPEN_ZONE:
+                       error = _zone_open_ioctl(disk, arg);
+                       break;
+               case SCSI_IOCTL_RESET_WP:
+                       error = _reset_wp_ioctl(disk, arg);
+                       break;
+               case SCSI_IOCTL_REPORT_ZONES:
+                       error = _report_zones_ioctl(disk, p);
+                       break;
                default:
                        error = scsi_cmd_blk_ioctl(bdev, mode, cmd, p);
                        if (error != -ENOTTY)
diff --git a/include/linux/blk-zoned-ctrl.h b/include/linux/blk-zoned-ctrl.h
new file mode 100644
index 0000000..fb1d494
--- /dev/null
+++ b/include/linux/blk-zoned-ctrl.h
@@ -0,0 +1,51 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tanch...@seagate.com>
+ *
+ * This file is licensed under  the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef BLK_ZONED_CTRL_H
+#define BLK_ZONED_CTRL_H
+
+#include <linux/blkzoned_api.h>
+#include <uapi/linux/blk-zoned-ctrl.h>
+
+/* this is basically scsi_execute */
+int blk_cmd_execute(struct request_queue *, const unsigned char *, int, void *,
+                   unsigned, unsigned char *, int, int, u64, int *);
+int blk_cmd_with_sense(struct gendisk *, u8 *, int, void *, u32 len, u8 *);
+
+/* scsi zbc commands */
+int blk_zoned_inquiry(struct gendisk *, u8, u8, u16, u8 *);
+int blk_zoned_close(struct gendisk *disk, u64 start_lba);
+int blk_zoned_finish(struct gendisk *disk, u64 start_lba);
+int blk_zoned_open(struct gendisk *disk, u64 start_lba);
+int blk_zoned_reset_wp(struct gendisk *disk, u64 start_lba);
+int blk_zoned_report(struct gendisk *, u64, u8,
+                    struct bdev_zone_report *, size_t);
+
+/* ata zac variants */
+int blk_zoned_identify_ata(struct gendisk *disk, struct zoned_identity *);
+int blk_zoned_close_ata(struct gendisk *disk, u64 start_lba);
+int blk_zoned_finish_ata(struct gendisk *disk, u64 start_lba);
+int blk_zoned_open_ata(struct gendisk *disk, u64 start_lba);
+int blk_zoned_reset_wp_ata(struct gendisk *disk, u64 start_lba);
+int blk_zoned_report_ata(struct gendisk *disk, u64, u8,
+                        struct bdev_zone_report *, size_t);
+
+/* for testing from userspace via ioctl */
+int _inquiry_ioctl(struct gendisk *disk, void __user *parg);
+int _zone_close_ioctl(struct gendisk *disk, unsigned long arg);
+int _zone_finish_ioctl(struct gendisk *disk, unsigned long arg);
+int _zone_open_ioctl(struct gendisk *disk, unsigned long arg);
+int _reset_wp_ioctl(struct gendisk *disk, unsigned long arg);
+int _report_zones_ioctl(struct gendisk *disk, void __user *parg);
+
+#endif /* BLK_ZONED_CTRL_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 52e2d1a..ad0084a 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -71,6 +71,7 @@ header-y += binfmts.h
 header-y += blkpg.h
 header-y += blktrace_api.h
 header-y += blkzoned_api.h
+header-y += blk-zoned-ctrl.h
 header-y += bpf_common.h
 header-y += bpf.h
 header-y += bpqether.h
diff --git a/include/uapi/linux/blk-zoned-ctrl.h 
b/include/uapi/linux/blk-zoned-ctrl.h
new file mode 100644
index 0000000..559c10d
--- /dev/null
+++ b/include/uapi/linux/blk-zoned-ctrl.h
@@ -0,0 +1,70 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tanch...@seagate.com>
+ *
+ * This file is licensed under  the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _UAPI_BLK_ZONED_CTRL_H
+#define _UAPI_BLK_ZONED_CTRL_H
+
+#include <linux/types.h>
+
+/* Used for Zone based SMR devices */
+#define SCSI_IOCTL_INQUIRY             0x10000
+#define SCSI_IOCTL_CLOSE_ZONE          0x10001
+#define SCSI_IOCTL_FINISH_ZONE         0x10002
+#define SCSI_IOCTL_OPEN_ZONE           0x10003
+#define SCSI_IOCTL_RESET_WP            0x10004
+#define SCSI_IOCTL_REPORT_ZONES                0x10005
+
+/**
+ * struct zoned_inquiry - deterine if device is block zoned.
+ *
+ * @evpd:
+ * @pg_op:
+ * @mx_resp_len:
+ * @result:
+ *
+ */
+struct zoned_inquiry {
+       __u8  evpd;
+       __u8  pg_op;
+       __u16 mx_resp_len;
+       __u8  result[0];
+} __packed;
+
+/**
+ * enum zoned_identity_type_id - Zoned, HA or HM.
+ *
+ * @NOT_ZONED: Not a zoned device.
+ * @HOST_AWARE: ZAC/ZBC device reports as Host Aware.
+ * @HOST_MANAGE: ZAC/ZBC device reports as Host Managed.
+ *
+ */
+enum zoned_identity_type_id {
+       NOT_ZONED    = 0x00,
+       HOST_AWARE   = 0x01,
+       HOST_MANAGE  = 0x02,
+};
+
+/**
+ * struct zoned_identity
+ *
+ * @type_id: See zoned_identity_type_id
+ * @reserved: reserved.
+ *
+ * Used for ata pass through to detected devices which support ZAC.
+ */
+struct zoned_identity {
+       __u8 type_id;
+       __u8 reserved[3];
+} __packed;
+
+#endif /* _UAPI_BLK_ZONED_CTRL_H */
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to