Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 177934
b: refs/heads/master
c: e339c1a
h: refs/heads/master
v: v3
  • Loading branch information
Martin K. Petersen authored and James Bottomley committed Dec 10, 2009
1 parent 1e6db12 commit c3e8d27
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cc9b2e9f6603190c009e5d2629ce8e3f99571346
refs/heads/master: e339c1a7c09ef736dca7b3a4353c7742557d9f8f
107 changes: 107 additions & 0 deletions trunk/drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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,
};

Expand Down Expand Up @@ -398,6 +408,57 @@ static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif)
scsi_set_prot_type(scmd, 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.
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/scsi/sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit c3e8d27

Please sign in to comment.