From patchwork Thu Nov 2 09:32:31 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vic Thor X-Patchwork-Id: 10038603 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 0A1DA6032D for ; Thu, 2 Nov 2017 12:05:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EA1FE28879 for ; Thu, 2 Nov 2017 12:05:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DBB4028EF6; Thu, 2 Nov 2017 12:05:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D027528879 for ; Thu, 2 Nov 2017 12:05:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751931AbdKBMFH (ORCPT ); Thu, 2 Nov 2017 08:05:07 -0400 Received: from mx1.tmdhosting.com ([108.178.0.170]:58012 "EHLO mx1.tmdhosting.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750924AbdKBMFF (ORCPT ); Thu, 2 Nov 2017 08:05:05 -0400 X-Greylist: delayed 9149 seconds by postgrey-1.27 at vger.kernel.org; Thu, 02 Nov 2017 08:05:05 EDT Received: from [184.154.216.246] (helo=s610.tmd.cloud) by mx1.tmdhosting.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89) (envelope-from ) id 1eABrX-0007Hd-5J for linux-scsi@vger.kernel.org; Thu, 02 Nov 2017 04:32:36 -0500 Received: from li1490-145.members.linode.com ([139.162.173.145]:42132 helo=sadr.members.linode.com) by s610.tmd.cloud with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.89) (envelope-from ) id 1eABrV-0005jf-Ei for linux-scsi@vger.kernel.org; Thu, 02 Nov 2017 05:32:33 -0400 Date: Thu, 2 Nov 2017 11:32:31 +0200 From: Vic Thor To: "linux-scsi@vger.kernel.org" Subject: [PATCH] Added Format Unit and Sanitize commands Message-ID: <20171102093231.GA26177@sadr.members.linode.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-Originating-IP: 184.154.216.246 X-SpamExperts-Domain: smtp-out.s610.tmd.cloud X-SpamExperts-Username: 184.154.216.246 Authentication-Results: tmdhosting.com; auth=pass smtp.auth=184.154.216.246@smtp-out.s610.tmd.cloud X-SpamExperts-Outgoing-Class: ham X-SpamExperts-Outgoing-Evidence: SB/global_tokens (0.00334536806027) X-Recommended-Action: accept X-Filter-ID: EX5BVjFpneJeBchSMxfU5maRggqpw0BAGfbg/2B+5AcXv9krsgRhBn0ayn6qsUc7Qd8pSQ5vgaRA QXJShcsaKUg9svS3KOOmAOKcrEo0JgYsPvC6cNaSQ67pCFtePIWVAmUqDTbWN/hJGzmIJQoMVrv0 Yi0fjkd/Xs+nVeeV9aUUBuq7Cy6hwuac55q94ngTA2dK58icqtUUL2Z9UVAyhqxdWtEmdGotvbFO pQXnA+YlwhBj+FttCPgcFg0MwnqbpfD/z2NyNomdiREfUcbDj/Ke8s8utGHT8Q5pJWi2MVr7Evam 8tYC7wyZQ4fzB8++GEpacme5pSUtZmdJvsQc+FCMeySUz0J9e5W6en50ijLtMx3XnFbzBDrx+I7E +HqIi9XquvzecAQjkxNWwaZ9bF9IlKha13El6tTzAeqsLP5rBz0V0g4dFqQD34faMjgGB37PoFQS pVEDhQ9PW1KmOsRDyLoKmGeSm3J08w57uQw+EVJ+yx63fy3IM/zVwN/q++DuIQUs/5JJj4C/n4CI Lou2zv4tuoHY++79UIkJhzhpZSwar1F+XKzhT19IBQiK8PooiwRjNjaA4qeyMQ8brDrYA/8N4DSK TZhQ68CUdqBWLDeHuGtWn+pyYlXFIT/RDfZ0QkFPpWrRPEm9ODafjxEVniihuDwEGDcmr6e3OPQO dwadcLj6w0nF9gLS354hvat1oVhy+SFDxK8b2DMEFvIqpOxxjzruvS0GxGPo8wK8kp6MylXqgRrO 17wOg4BTf2pDOU6kfolB3agW32h8ZX6m+UeFXprlCOm3BAEbJtA53x+a3AExc2qzVeXcGPoRuyDb SUc3VMIe9VX0WP+7h2D7E4GRZxVDWEta924mrhLaMlib3NqeKTKwqjp83dQkiNWY7F/AzuaCMJZU IB7kemkike+QhstmkY43a6HkE3uag5H0o83CJY8RyY0VxMJa X-Report-Abuse-To: spam@mx1.tmdhosting.com Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds support for Sanitize and Format unit commands. Sanitize block just zeroes the buffer. Sanitize overwrite use the pattern given. Sanitize crypto fills the buffer with random data. Obviously a real drive doesn't do this, but it looks like it. FORMAT UNIT either zeroes the drive or fills it with random if the suitable SI bit is set. It doesn't support resizing or changing the block size. I don't know which F_* flag to use. I've used F_M_ACCESS for both, please advice. Should we notify the kernel to rescan for partitions? Signed-off-by: Victor Gonzalo --- drivers/scsi/scsi_debug.c | 289 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 271 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 09ba494f8896..74eb6a7dc9eb 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -340,14 +341,16 @@ enum sdeb_opcode_index { SDEB_I_WRITE_SAME = 27, /* 10, 16 */ SDEB_I_SYNC_CACHE = 28, /* 10 only */ SDEB_I_COMP_WRITE = 29, - SDEB_I_LAST_ELEMENT = 30, /* keep this last */ + SDEB_I_FORMAT_UNIT = 30, + SDEB_I_SANITIZE = 31, + SDEB_I_LAST_ELEMENT = 32, /* keep this last */ }; static const unsigned char opcode_ind_arr[256] = { /* 0x0; 0x0->0x1f: 6 byte cdbs */ SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE, - 0, 0, 0, 0, + SDEB_I_FORMAT_UNIT, 0, 0, 0, SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0, 0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE, @@ -360,7 +363,7 @@ static const unsigned char opcode_ind_arr[256] = { 0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0, /* 0x40; 0x40->0x5f: 10 byte cdbs */ 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0, + SDEB_I_SANITIZE, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0, 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE, 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0, @@ -408,6 +411,8 @@ static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_format_unit(struct scsi_cmnd *, struct sdebug_dev_info *); +static int resp_sanitize(struct scsi_cmnd *, struct sdebug_dev_info *); static const struct opcode_info_t msense_iarr[1] = { {0, 0x1a, 0, F_D_IN, NULL, NULL, @@ -565,6 +570,12 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { 0, 0xff, 0x1f, 0xc7} }, /* COMPARE AND WRITE */ /* 30 */ + {0, 0x04, 0, F_M_ACCESS /*TODO:???*/, resp_format_unit, NULL, + {6, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0} }, /* FORMAT UNIT */ + {0, 0x48, 0, F_M_ACCESS /*TODO:???*/, resp_sanitize, NULL, + {10, 0xff, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, + 0, 0, 0, 0} }, /* Sanitize */ {0xff, 0, 0, 0, NULL, NULL, /* terminating element */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; @@ -1309,7 +1320,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) int lu_id_num, port_group_id, target_dev_id, len; char lu_id_str[6]; int host_no = devip->sdbg_host->shost->host_no; - + port_group_id = (((host_no + 1) & 0x7f) << 8) + (devip->channel & 0x7f); if (sdebug_vpd_use_hostno == 0) @@ -2336,6 +2347,256 @@ static int resp_log_sense(struct scsi_cmnd * scp, min(len, SDEBUG_MAX_INQ_ARR_SZ)); } +static int clear_fake_store(unsigned char *pattern, bool random) +{ + unsigned long sz, iflags = 0, i; + + if (sdebug_fake_rw != 0) + return 0; + + write_lock_irqsave(&atomic_rw, iflags); + + sz = (unsigned long)sdebug_dev_size_mb * 1048576; + + if (random) { + /*Not really what disks do, but close enough for simulation porpuses*/ + get_random_bytes(fake_storep, sz); + } else { + if (pattern) { + for (i = 0; i < sdebug_store_sectors; i++) + memcpy(fake_storep + i * sdebug_sector_size, pattern, sdebug_sector_size); + } else + memset(fake_storep, 0, sz); + } + write_unlock_irqrestore(&atomic_rw, iflags); + + return 0; +} + +static void create_pattern_from_ip( + unsigned char *pattern, unsigned char *ip, size_t ip_length) +{ + size_t filled = 0, sz = 0; + + while (filled < sdebug_sector_size) { + sz = (filled + ip_length > sdebug_sector_size) ? + (sdebug_sector_size - filled) : ip_length; + memcpy(pattern + filled, ip, sz); + filled += sz; + } +} + +static int resp_format_unit( + struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + bool longlist = scp->cmnd[1] & 0x20; + bool fmtdata = scp->cmnd[1] & 0x10; + bool ip = false, si = false, immed = false; + unsigned char *buf = NULL, *pattern = NULL; + int len = 0, buf_len = 0, retval = 0, ip_offset = 0; + unsigned char ip_modifier = 0, ip_type = 0; + u16 ip_length = 0; + u32 defect_list_length = 0; + + ip_offset = (longlist ? 8 : 4); + + buf_len = scsi_bufflen(scp); + buf = kzalloc(buf_len, GFP_ATOMIC); + if (!buf) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); + retval = check_condition_result; + goto out; + } + + if (fmtdata) { + len = fetch_to_dev_buffer(scp, buf, buf_len); + if (len < ip_offset) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + + if (longlist) + defect_list_length = get_unaligned(buf + 4); + else + defect_list_length = get_unaligned_be16(buf+2); + + immed = buf[1] & 0x20; + ip = buf[1] & 0x80; + if (ip) { + if (len < ip_offset + 4) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + + ip_modifier = (buf[ip_offset] & 0xc0) >> 6; + si = buf[ip_offset] & 0x20; + ip_type = buf[ip_offset + 1]; + ip_length = get_unaligned_be16(buf + ip_offset + 2); + + switch (ip_type) { + case 0: + if (ip_length) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + pattern = NULL; + break; + case 1: + if ((!ip_length) || + (ip_length > sdebug_sector_size) || + (len != ip_offset + 4 + ip_length)) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + + pattern = kzalloc(sdebug_sector_size, GFP_ATOMIC); + if (!pattern) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); + retval = check_condition_result; + goto out; + } + + create_pattern_from_ip(pattern, buf + ip_offset + 4, ip_length); + break; + default: + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + } + } + + if (sdebug_verbose) { + sdev_printk(KERN_DEBUG, scp->device, ": Format unit\n"); + if (fmtdata) { + sdev_printk(KERN_DEBUG, scp->device, + ": Format unit parameters (%s header), %s, with%s IP, with%s SI\n", + longlist ? "long" : "short", + immed ? "return inmediately" : "wait to complete", + ip ? "" : "out", + si ? "" : "out"); + + if (ip_length) + print_hex_dump_bytes("Format Unit Initialization pattern: ", + DUMP_PREFIX_NONE, buf + ip_offset + 4, ip_length); + + /*Not doing anything with these, but at least listing them debugging*/ + if (defect_list_length) { + print_hex_dump_bytes("Format Unit defect list: ", + DUMP_PREFIX_NONE, buf + ip_offset + 4 + ip_length, defect_list_length); + } + + if (ip_modifier) + sdev_printk(KERN_DEBUG, scp->device, + ": Format unit ip modifier (%x) used, but it's obsolete in SBC-3\n", + ip_modifier); + } + } + + clear_fake_store(pattern, si); + +out: + kfree(buf); + kfree(pattern); + + return retval; +} +static int sanitize_overwrite( + struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + unsigned char *buf = NULL; + int buf_len = 0, len = 0; + unsigned char *pattern = NULL; + u16 ip_length = 0; + u16 parameter_list_length = get_unaligned_be16(scp->cmnd + 7); + int retval = 0; + + buf_len = scsi_bufflen(scp); + if ((parameter_list_length < 4) || + (parameter_list_length > sdebug_sector_size + 5) || + (parameter_list_length != buf_len)) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } + + buf = kzalloc(buf_len, GFP_ATOMIC); + if (!buf) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); + retval = check_condition_result; + goto out; + } + + len = fetch_to_dev_buffer(scp, buf, buf_len); + ip_length = get_unaligned_be16(buf + 2); + + if (!ip_length || ip_length > sdebug_sector_size) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); + retval = check_condition_result; + goto out; + } + + pattern = kzalloc(sdebug_sector_size, GFP_ATOMIC); + if (!pattern) { + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); + retval = check_condition_result; + goto out; + } + + create_pattern_from_ip(pattern, buf + 4, ip_length); + + clear_fake_store(pattern, false); + + if (sdebug_verbose) { + sdev_printk(KERN_DEBUG, scp->device, + ": Sanitize Overwrite With%s Invert, Test: %x, Overwrite count: %d\n", + (buf[0] & 0x80) ? "" : "out ", + ((buf[0] & 0x60) >> 5), + (buf[0] & 0x1f)); + print_hex_dump_bytes("Sanitize Overwrite Initialization Pattern: ", + DUMP_PREFIX_NONE, buf + 4, ip_length); + } + +out: + kfree(buf); + kfree(pattern); + + return retval; +} + +static int resp_sanitize( + struct scsi_cmnd *scp, struct sdebug_dev_info *devip) +{ + unsigned char service_action = scp->cmnd[1] & 0x1f; + + switch (service_action) { + case 0x01: + return sanitize_overwrite(scp, devip); + case 0x02: + if (sdebug_verbose) + sdev_printk(KERN_DEBUG, scp->device, ": Sanitize Block\n"); + clear_fake_store(NULL, false); + break; + case 0x03: + if (sdebug_verbose) + sdev_printk(KERN_DEBUG, scp->device, ": Sanitize Cryptographic Erase\n"); + /*This is not what a real disk does, but it's good enough for testing */ + clear_fake_store(NULL, true); + break; + case 0x1f: + /*Exit Failure Mode*/ + break; + default: + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); + return check_condition_result; + } + + return 0; +} + static int check_device_access_params(struct scsi_cmnd *scp, unsigned long long lba, unsigned int num) { @@ -2995,8 +3256,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, memset(fake_storep + lba_off, 0, sdebug_sector_size); ret = 0; } else - ret = fetch_to_dev_buffer(scp, fake_storep + lba_off, - sdebug_sector_size); + ret = fetch_to_dev_buffer(scp, fake_storep + lba_off, sdebug_sector_size); if (-1 == ret) { write_unlock_irqrestore(&atomic_rw, iflags); @@ -3109,12 +3369,9 @@ static int resp_write_buffer(struct scsi_cmnd *scp, break; case 0x7: /* download MC with offsets, save, and ACT */ /* set UA on all devices (LUs) in this target */ - list_for_each_entry(dp, - &devip->sdbg_host->dev_info_list, - dev_list) + list_for_each_entry(dp, &devip->sdbg_host->dev_info_list, dev_list) if (dp->target == sdp->id) - set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET, - dp->uas_bm); + set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET, dp->uas_bm); break; default: /* do nothing for this command for other mode values */ @@ -4480,17 +4737,14 @@ static ssize_t fake_rw_show(struct device_driver *ddp, char *buf) static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf, size_t count) { - int n; + int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { n = (n > 0); sdebug_fake_rw = (sdebug_fake_rw > 0); if (sdebug_fake_rw != n) { if ((0 == n) && (NULL == fake_storep)) { - unsigned long sz = - (unsigned long)sdebug_dev_size_mb * - 1048576; - + unsigned long sz = (unsigned long)sdebug_dev_size_mb * 1048576; fake_storep = vmalloc(sz); if (NULL == fake_storep) { pr_err("out of memory, 9\n"); @@ -4988,8 +5242,7 @@ static int __init scsi_debug_init(void) pr_err("submit_queues must be 1 or more\n"); return -EINVAL; } - sdebug_q_arr = kcalloc(submit_queues, sizeof(struct sdebug_queue), - GFP_KERNEL); + sdebug_q_arr = kcalloc(submit_queues, sizeof(struct sdebug_queue), GFP_KERNEL); if (sdebug_q_arr == NULL) return -ENOMEM; for (k = 0; k < submit_queues; ++k)