From patchwork Sat Nov 21 02:45:22 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Martin K. Petersen" X-Patchwork-Id: 38951 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by ozlabs.org (Postfix) with ESMTP id 11A69B7063 for ; Sat, 21 Nov 2009 13:46:46 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753866AbZKUCqd (ORCPT ); Fri, 20 Nov 2009 21:46:33 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753975AbZKUCqd (ORCPT ); Fri, 20 Nov 2009 21:46:33 -0500 Received: from acsinet11.oracle.com ([141.146.126.233]:55384 "EHLO acsinet11.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753866AbZKUCqc (ORCPT ); Fri, 20 Nov 2009 21:46:32 -0500 Received: from rgminet13.oracle.com (rcsinet13.oracle.com [148.87.113.125]) by acsinet11.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id nAL2l9ml004328 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sat, 21 Nov 2009 02:47:10 GMT Received: from acsmt354.oracle.com (acsmt354.oracle.com [141.146.40.154]) by rgminet13.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id nAL2kDtN032527; Sat, 21 Nov 2009 02:46:15 GMT Received: from abhmt002.oracle.com by acsmt358.oracle.com with ESMTP id 512933591258771531; Fri, 20 Nov 2009 20:45:31 -0600 Received: from groovelator.mkp.net (/209.217.122.111) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Fri, 20 Nov 2009 18:45:30 -0800 From: "Martin K. Petersen" To: jens.axboe@oracle.com, james.bottomley@hansenpartnership.com, hch@infradead.org, willy@wil.cx, jgarzik@pobox.com, sandeen@redhat.com, rwheeler@redhat.com, linux-ide@vger.kernel.org, linux-scsi@vger.kernel.org Cc: "Martin K. Petersen" Subject: [PATCH 2/4] sd: WRITE SAME(16) / UNMAP support Date: Fri, 20 Nov 2009 21:45:22 -0500 Message-Id: <1258771524-26673-3-git-send-email-martin.petersen@oracle.com> X-Mailer: git-send-email 1.6.0.6 In-Reply-To: <1258771524-26673-1-git-send-email-martin.petersen@oracle.com> References: <1258771524-26673-1-git-send-email-martin.petersen@oracle.com> X-Source-IP: acsmt354.oracle.com [141.146.40.154] X-Auth-Type: Internal IP X-CT-RefId: str=0001.0A090209.4B075473.0049:SCFMA4539814,ss=1,fgs=0 Sender: linux-ide-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ide@vger.kernel.org Implement a prepare discard function that sends either WRITE SAME(16) or UNMAP(10) depending on parameters indicated by the device in the block limits VPD. Extract unmap constraints and report them to the block layer. Based in part by a patch by Christoph Hellwig . Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/sd.h | 2 + 2 files changed, 107 insertions(+), 0 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9093c72..65538e1 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -264,6 +264,15 @@ sd_show_app_tag_own(struct device *dev, struct device_attribute *attr, return snprintf(buf, 20, "%u\n", sdkp->ATO); } +static ssize_t +sd_show_thin_provisioning(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + + return snprintf(buf, 20, "%u\n", sdkp->thin_provisioning); +} + static struct device_attribute sd_disk_attrs[] = { __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, sd_store_cache_type), @@ -274,6 +283,7 @@ static struct device_attribute sd_disk_attrs[] = { sd_store_manage_start_stop), __ATTR(protection_type, S_IRUGO, sd_show_protection_type, NULL), __ATTR(app_tag_own, S_IRUGO, sd_show_app_tag_own, NULL), + __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), __ATTR_NULL, }; @@ -399,6 +409,57 @@ static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif) } /** + * sd_prepare_discard - unmap blocks on thinly provisioned device + * @rq: Request to prepare + * + * Will issue either UNMAP or WRITE SAME(16) depending on preference + * indicated by target device. + **/ +static int sd_prepare_discard(struct request *rq) +{ + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + struct bio *bio = rq->bio; + sector_t sector = bio->bi_sector; + unsigned int num = bio_sectors(bio); + + if (sdkp->device->sector_size == 4096) { + sector >>= 3; + num >>= 3; + } + + rq->cmd_type = REQ_TYPE_BLOCK_PC; + rq->timeout = SD_TIMEOUT; + + memset(rq->cmd, 0, rq->cmd_len); + + if (sdkp->unmap) { + char *buf = kmap_atomic(bio_page(bio), KM_USER0); + + rq->cmd[0] = UNMAP; + rq->cmd[8] = 24; + rq->cmd_len = 10; + + /* Ensure that data length matches payload */ + rq->__data_len = bio->bi_size = bio->bi_io_vec->bv_len = 24; + + put_unaligned_be16(6 + 16, &buf[0]); + put_unaligned_be16(16, &buf[2]); + put_unaligned_be64(sector, &buf[8]); + put_unaligned_be32(num, &buf[16]); + + kunmap_atomic(buf, KM_USER0); + } else { + rq->cmd[0] = WRITE_SAME_16; + rq->cmd[1] = 0x8; /* UNMAP */ + put_unaligned_be64(sector, &rq->cmd[2]); + put_unaligned_be32(num, &rq->cmd[10]); + rq->cmd_len = 16; + } + + return BLKPREP_OK; +} + +/** * sd_init_command - build a scsi (read or write) command from * information in the request structure. * @SCpnt: pointer to mid-level's per scsi command structure that @@ -418,6 +479,13 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) int ret, host_dif; unsigned char protect; + /* + * Discard request come in as REQ_TYPE_FS but we turn them into + * block PC requests to make life easier. + */ + if (blk_discard_rq(rq)) + ret = sd_prepare_discard(rq); + if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { ret = scsi_setup_blk_pc_cmnd(sdp, rq); goto out; @@ -1432,6 +1500,19 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, sd_printk(KERN_NOTICE, sdkp, "physical block alignment offset: %u\n", alignment); + if (buffer[14] & 0x80) { /* TPE */ + struct request_queue *q = sdp->request_queue; + + sdkp->thin_provisioning = 1; + q->limits.discard_granularity = sdkp->hw_sector_size; + q->limits.max_discard_sectors = 0xffffffff; + + if (buffer[14] & 0x40) /* TPRZ */ + q->limits.discard_zeroes_data = 1; + + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); + } + sdkp->capacity = lba + 1; return sector_size; } @@ -1863,6 +1944,7 @@ void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) */ static void sd_read_block_limits(struct scsi_disk *sdkp) { + struct request_queue *q = sdkp->disk->queue; unsigned int sector_sz = sdkp->device->sector_size; char *buffer; @@ -1877,6 +1959,29 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) blk_queue_io_opt(sdkp->disk->queue, get_unaligned_be32(&buffer[12]) * sector_sz); + /* Thin provisioning enabled and page length indicates TP support */ + if (sdkp->thin_provisioning && buffer[3] == 0x3c) { + unsigned int lba_count, desc_count, granularity; + + lba_count = get_unaligned_be32(&buffer[20]); + desc_count = get_unaligned_be32(&buffer[24]); + + if (lba_count && desc_count) { + sdkp->unmap = 1; + q->limits.max_discard_sectors = + lba_count * sector_sz >> 9; + } + + granularity = get_unaligned_be32(&buffer[28]); + + if (granularity) + q->limits.discard_granularity = granularity * sector_sz; + + if (buffer[32] & 0x80) + q->limits.discard_alignment = + get_unaligned_be32(&buffer[32]) & ~(1 << 31); + } + kfree(buffer); } diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index e374804..43d3caf 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -60,6 +60,8 @@ struct scsi_disk { unsigned RCD : 1; /* state of disk RCD bit, unused */ unsigned DPOFUA : 1; /* state of disk DPOFUA bit */ unsigned first_scan : 1; + unsigned thin_provisioning : 1; + unsigned unmap : 1; }; #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)