@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,642 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tancheff@seagate.com>
+ * XiaoDong Han <xiaodong.h.han@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);
@@ -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
@@ -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)
new file mode 100644
@@ -0,0 +1,51 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tancheff@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 */
@@ -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
new file mode 100644
@@ -0,0 +1,70 @@
+/*
+ * Functions for zone based SMR devices.
+ *
+ * Copyright (C) 2015 Seagate Technology PLC
+ *
+ * Written by:
+ * Shaun Tancheff <shaun.tancheff@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 */
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.tancheff@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