From patchwork Mon Apr 4 05:06:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shaun Tancheff X-Patchwork-Id: 8737511 Return-Path: X-Original-To: patchwork-linux-scsi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 777199F336 for ; Mon, 4 Apr 2016 05:07:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 67423201FA for ; Mon, 4 Apr 2016 05:07:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 489D1201F5 for ; Mon, 4 Apr 2016 05:07:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752271AbcDDFH0 (ORCPT ); Mon, 4 Apr 2016 01:07:26 -0400 Received: from mail-pa0-f66.google.com ([209.85.220.66]:35439 "EHLO mail-pa0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752670AbcDDFHH (ORCPT ); Mon, 4 Apr 2016 01:07:07 -0400 Received: by mail-pa0-f66.google.com with SMTP id zy2so2553877pac.2; Sun, 03 Apr 2016 22:07:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=ziGmXFqdn9jW2k13+z1jWeH8NkDEebp5JeCfPTEdNn0=; b=oMv/8Zgo/hSJ0Bv1vWWeahe/zP01JTIL/n5zPPPWUW9IbGnjZ7Vne/CvJQoA5EuXqB eg0g+fvgBRjfy5/NNPVFDjWsGmK4Rt2/M11LNpsqSk7jL/AA7cMHo3EuwDDVHpX9AAo1 /k+lSL66GTZfeqvGn1ZfAUM/2cynYkvgM4GVEe1tG23wiHPil7IoBmcEA73bc7Z8kWLU QQmHVlZKAC3YX8cUzN1s9rsC+TeM6SgiuOygpLZQauQnHe39OynZFM9HYU+vEnCeT9Ai V2hu6XDYztARJz4GDqzYKGHmnJYHoJIHcqt87fhVtoQDYUzHQQLvbRF/bsYUd71tvw5L vAZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=ziGmXFqdn9jW2k13+z1jWeH8NkDEebp5JeCfPTEdNn0=; b=RH2ql75Vb6mdl1MFrHI9aVTC6UAhbR0KcG0hFrddap+yku+WjNdnTqa8LGuTCt60Y8 WTIwb0kqI4FIQLDdnADzTFDWQy3T+jJUjSUZHGBZqFWSe9IxZi3nyBR6xAjtid761m/P 0G0414v3HIlVB3u2hFTGw9pVPbJ3dpAZZzol72IrJa3/rKKSozNiXhz3r67RIyFX4+71 CgyDuVMZU26UzCt1jexGoUilyeJUAeQNh1xeddjCCEmz1O++CnQl2bJ8k6wERB7ljho+ rlL/WQGcAqH9E34UkTcmYKo1LlJapC0Dg/K5pAsOvjl2zu35oqDXWAVn1bwMl88DpQDh xVwg== X-Gm-Message-State: AD7BkJK7G4sjs03nY3vI9+YXsUPcRRZC+UnDGj6S/6TIkxDoyBfauzj7FB3O+UoPyCjIAg== X-Received: by 10.66.66.10 with SMTP id b10mr1018132pat.12.1459746426529; Sun, 03 Apr 2016 22:07:06 -0700 (PDT) Received: from localhost.localdomain ([103.47.135.1]) by smtp.gmail.com with ESMTPSA id k65sm35804188pfb.30.2016.04.03.22.07.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 03 Apr 2016 22:07:05 -0700 (PDT) From: Shaun Tancheff To: linux-ide@vger.kernel.org, dm-devel@redhat.com, linux-block@vger.kernel.org, linux-scsi@vger.kernel.org Cc: Jens Axboe , Shaun Tancheff , Shaun Tancheff Subject: [PATCH 12/12] Block Zoned Control for Userspace Date: Mon, 4 Apr 2016 12:06:16 +0700 Message-Id: <1459746376-27983-13-git-send-email-shaun@tancheff.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1459746376-27983-1-git-send-email-shaun@tancheff.com> References: <1459746376-27983-1-git-send-email-shaun@tancheff.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + * XiaoDong Han + * + * 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 +#include +#include +#include +#include +#include + +#include "blk.h" +#include +#include + +/* + * for max sense size + */ +#include +#include +#include +#include +#include + +#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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 + * + * 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 +#include + +/* 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 + * + * 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 + +/* 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 */