From patchwork Thu May 31 10:31:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Damien Le Moal X-Patchwork-Id: 10440641 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 5B2F1602BD for ; Thu, 31 May 2018 10:31:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4B7DC29206 for ; Thu, 31 May 2018 10:31:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4018A29213; Thu, 31 May 2018 10:31:44 +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=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI, T_DKIM_INVALID 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 9274C29206 for ; Thu, 31 May 2018 10:31:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754551AbeEaKbm (ORCPT ); Thu, 31 May 2018 06:31:42 -0400 Received: from esa5.hgst.iphmx.com ([216.71.153.144]:32969 "EHLO esa5.hgst.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754526AbeEaKbh (ORCPT ); Thu, 31 May 2018 06:31:37 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=wdc.com; i=@wdc.com; q=dns/txt; s=dkim.wdc.com; t=1527762697; x=1559298697; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=rl/E/blTvXJW/3Pq5S06r4meDeET6tZHJNVMwJO6Q3Q=; b=E3GX87fiejaH+8SVHLkaBtzHAAh1nkMFcvrKbn7GhJ1nUGYMjQ49n8hx JmJW8Np7XcNKQtTdLjA043yyQ+pSm/WiP8kYu0h1BfA5Dbgy3aUBUIp1l EV8FsiuMY7IaRzzl2HfTGVY0uX3n1e9Amu/jAdlgNz7he4pH5MPWZR5c6 omXFZEFnFQYAXs33gWQGq1eOxhFO8dx/9ZBNh1hZ8HS2jUV5GTio4esgw scCZDudCbHlmgCTyyumEMg+3mERMLef98T6mRiBg/0lwsbZ3pv1un9guS o1jaHjRTgR9+u1QRu0ZsM5w7PU13ESsUHZNJ8QBhiwbwogl7hO9KWseOW g==; X-IronPort-AV: E=Sophos;i="5.49,463,1520870400"; d="scan'208";a="79708384" Received: from h199-255-45-15.hgst.com (HELO uls-op-cesaep02.wdc.com) ([199.255.45.15]) by ob1.hgst.iphmx.com with ESMTP; 31 May 2018 18:31:36 +0800 Received: from uls-op-cesaip01.wdc.com ([10.248.3.36]) by uls-op-cesaep02.wdc.com with ESMTP; 31 May 2018 03:21:47 -0700 Received: from washi.fujisawa.hgst.com ([10.149.53.254]) by uls-op-cesaip01.wdc.com with ESMTP; 31 May 2018 03:31:36 -0700 From: Damien Le Moal To: linux-scsi@vger.kernel.org, "Martin K . Petersen" Cc: stable@vger.kernel.org, Greg Kroah-Hartman , Bart Van Assche , Hannes Reinecke , Bart Van Assche Subject: [PATCH 2/2] scsi: sd_zbc: Avoid that resetting a zone fails sporadically Date: Thu, 31 May 2018 19:31:24 +0900 Message-Id: <20180531103124.21532-3-damien.lemoal@wdc.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180531103124.21532-1-damien.lemoal@wdc.com> References: <20180531103124.21532-1-damien.lemoal@wdc.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 From: Bart Van Assche [Backport of upstream commit ccce20fc7968d546fb1e8e147bf5cdc8afc4278a] Since SCSI scanning occurs asynchronously, since sd_revalidate_disk() is called from sd_probe_async() and since sd_revalidate_disk() calls sd_zbc_read_zones() it can happen that sd_zbc_read_zones() is called concurrently with blkdev_report_zones() and/or blkdev_reset_zones(). That can cause these functions to fail with -EIO because sd_zbc_read_zones() sets sdkp->nr_zones to zero before restoring it to the actual value, even if no drive characteristics have changed. Avoid that this can happen by modifying making the following changes: - Protect the code that updates zone information with blk_mq_freeze() and blk_mq_unfreeze(). - Modify sd_zbc_setup() such that these functions do not modify struct scsi_disk before all zone information has been obtained. - Reallocate the zone write lock bitmap if the number of zones changed. Note: since commit 055f6e18e08f ("block: Make q_usage_counter also track legacy requests"; kernel v4.15) the request queue freezing mechanism also affects legacy request queues. Fixes: 89d947561077 ("sd: Implement support for ZBC devices") Signed-off-by: Bart Van Assche [Damien] * Backport for 4.14-stable * Updated this commit message Signed-off-by: Damien Le Moal Cc: stable@vger.kernel.org # 4.14 --- drivers/scsi/sd_zbc.c | 98 +++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index bc3cb81a9c7d..ea9e1e0ed5b8 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -423,7 +423,16 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp, #define SD_ZBC_BUF_SIZE 131072 -static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) +/** + * sd_zbc_check_zone_size - Check the device zone sizes + * @sdkp: Target disk + * + * Check that all zones of the device are equal. The last zone can however + * be smaller. The zone size must also be a power of two number of LBAs. + * + * Returns the zone size in bytes upon success or an error code upon failure. + */ +static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp) { u64 zone_blocks = 0; sector_t block = 0; @@ -434,8 +443,6 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) int ret; u8 same; - sdkp->zone_blocks = 0; - /* Get a buffer */ buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL); if (!buf) @@ -470,16 +477,17 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) /* Parse zone descriptors */ while (rec < buf + buf_len) { - zone_blocks = get_unaligned_be64(&rec[8]); - if (sdkp->zone_blocks == 0) { - sdkp->zone_blocks = zone_blocks; - } else if (zone_blocks != sdkp->zone_blocks && - (block + zone_blocks < sdkp->capacity - || zone_blocks > sdkp->zone_blocks)) { + u64 this_zone_blocks = get_unaligned_be64(&rec[8]); + + if (zone_blocks == 0) { + zone_blocks = this_zone_blocks; + } else if (this_zone_blocks != zone_blocks && + (block + this_zone_blocks < sdkp->capacity + || this_zone_blocks > zone_blocks)) { zone_blocks = 0; goto out; } - block += zone_blocks; + block += this_zone_blocks; rec += 64; } @@ -492,8 +500,6 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) } while (block < sdkp->capacity); - zone_blocks = sdkp->zone_blocks; - out: if (!zone_blocks) { if (sdkp->first_scan) @@ -513,8 +519,7 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) "Zone size too large\n"); ret = -ENODEV; } else { - sdkp->zone_blocks = zone_blocks; - sdkp->zone_shift = ilog2(zone_blocks); + ret = zone_blocks; } out_free: @@ -523,23 +528,44 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp) return ret; } -static int sd_zbc_setup(struct scsi_disk *sdkp) +static int sd_zbc_setup(struct scsi_disk *sdkp, u32 zone_blocks) { + struct request_queue *q = sdkp->disk->queue; + u32 zone_shift = ilog2(zone_blocks); + u32 nr_zones; /* chunk_sectors indicates the zone size */ - blk_queue_chunk_sectors(sdkp->disk->queue, - logical_to_sectors(sdkp->device, sdkp->zone_blocks)); - sdkp->zone_shift = ilog2(sdkp->zone_blocks); - sdkp->nr_zones = sdkp->capacity >> sdkp->zone_shift; - if (sdkp->capacity & (sdkp->zone_blocks - 1)) - sdkp->nr_zones++; - - if (!sdkp->zones_wlock) { - sdkp->zones_wlock = kcalloc(BITS_TO_LONGS(sdkp->nr_zones), - sizeof(unsigned long), - GFP_KERNEL); - if (!sdkp->zones_wlock) - return -ENOMEM; + blk_queue_chunk_sectors(q, + logical_to_sectors(sdkp->device, zone_blocks)); + nr_zones = round_up(sdkp->capacity, zone_blocks) >> zone_shift; + + /* + * Initialize the disk zone write lock bitmap if the number + * of zones changed. + */ + if (nr_zones != sdkp->nr_zones) { + unsigned long *zones_wlock = NULL; + + if (nr_zones) { + zones_wlock = kcalloc(BITS_TO_LONGS(nr_zones), + sizeof(unsigned long), + GFP_KERNEL); + if (!zones_wlock) + return -ENOMEM; + } + + blk_mq_freeze_queue(q); + sdkp->zone_blocks = zone_blocks; + sdkp->zone_shift = zone_shift; + sdkp->nr_zones = nr_zones; + swap(sdkp->zones_wlock, zones_wlock); + blk_mq_unfreeze_queue(q); + + kfree(zones_wlock); + + /* READ16/WRITE16 is mandatory for ZBC disks */ + sdkp->device->use_16_for_rw = 1; + sdkp->device->use_10_for_rw = 0; } return 0; @@ -548,6 +574,7 @@ static int sd_zbc_setup(struct scsi_disk *sdkp) int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf) { + int64_t zone_blocks; int ret; if (!sd_is_zoned(sdkp)) @@ -585,19 +612,19 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, * Check zone size: only devices with a constant zone size (except * an eventual last runt zone) that is a power of 2 are supported. */ - ret = sd_zbc_check_zone_size(sdkp); - if (ret) + zone_blocks = sd_zbc_check_zone_size(sdkp); + ret = -EFBIG; + if (zone_blocks != (u32)zone_blocks) + goto err; + ret = zone_blocks; + if (ret < 0) goto err; /* The drive satisfies the kernel restrictions: set it up */ - ret = sd_zbc_setup(sdkp); + ret = sd_zbc_setup(sdkp, zone_blocks); if (ret) goto err; - /* READ16/WRITE16 is mandatory for ZBC disks */ - sdkp->device->use_16_for_rw = 1; - sdkp->device->use_10_for_rw = 0; - return 0; err: @@ -610,6 +637,7 @@ void sd_zbc_remove(struct scsi_disk *sdkp) { kfree(sdkp->zones_wlock); sdkp->zones_wlock = NULL; + sdkp->nr_zones = 0; } void sd_zbc_print_zones(struct scsi_disk *sdkp)