From patchwork Thu Nov 26 17:00:40 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: 39566 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 291EBB6EEE for ; Fri, 27 Nov 2009 04:03:01 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1760849AbZKZRCk (ORCPT ); Thu, 26 Nov 2009 12:02:40 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1760842AbZKZRCk (ORCPT ); Thu, 26 Nov 2009 12:02:40 -0500 Received: from acsinet12.oracle.com ([141.146.126.234]:42337 "EHLO acsinet12.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1760698AbZKZRCi (ORCPT ); Thu, 26 Nov 2009 12:02:38 -0500 Received: from rgminet15.oracle.com (rcsinet15.oracle.com [148.87.113.117]) by acsinet12.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id nAQH2EJZ016208 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 26 Nov 2009 17:02:15 GMT Received: from acsmt355.oracle.com (acsmt355.oracle.com [141.146.40.155]) by rgminet15.oracle.com (Switch-3.3.1/Switch-3.3.1) with ESMTP id nAPNOORq001516; Thu, 26 Nov 2009 17:02:19 GMT Received: from abhmt002.oracle.com by acsmt357.oracle.com with ESMTP id 636353011259254853; Thu, 26 Nov 2009 11:00:53 -0600 Received: from groovelator.mkp.net (/209.217.122.111) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Thu, 26 Nov 2009 09:00:51 -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/5] sd: WRITE SAME(16) / UNMAP support Date: Thu, 26 Nov 2009 12:00:40 -0500 Message-Id: <1259254843-8326-3-git-send-email-martin.petersen@oracle.com> X-Mailer: git-send-email 1.6.0.6 In-Reply-To: <1259254843-8326-1-git-send-email-martin.petersen@oracle.com> References: <1259254843-8326-1-git-send-email-martin.petersen@oracle.com> X-Source-IP: acsmt355.oracle.com [141.146.40.155] X-Auth-Type: Internal IP X-CT-RefId: str=0001.0A090205.4B0EB497.012D: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 function for handling discard requests 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 | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/sd.h | 2 + 2 files changed, 109 insertions(+), 0 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9093c72..255da53 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,31 @@ 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) { + q->limits.max_discard_sectors = + lba_count * sector_sz >> 9; + + if (desc_count) + sdkp->unmap = 1; + } + + 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)