diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 170 |
1 files changed, 148 insertions, 22 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3f370228b..4e08d1cd7 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -51,6 +51,7 @@ #include <linux/async.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/pr.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -204,6 +205,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr, buffer_data[2] &= ~0x05; buffer_data[2] |= wce << 2 | rcd; sp = buffer_data[0] & 0x80 ? 1 : 0; + buffer_data[0] &= ~0x80; if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT, SD_MAX_RETRIES, &data, &sshdr)) { @@ -636,11 +638,24 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) unsigned int max_blocks = 0; q->limits.discard_zeroes_data = 0; - q->limits.discard_alignment = sdkp->unmap_alignment * - logical_block_size; - q->limits.discard_granularity = - max(sdkp->physical_block_size, - sdkp->unmap_granularity * logical_block_size); + + /* + * When LBPRZ is reported, discard alignment and granularity + * must be fixed to the logical block size. Otherwise the block + * layer will drop misaligned portions of the request which can + * lead to data corruption. If LBPRZ is not set, we honor the + * device preference. + */ + if (sdkp->lbprz) { + q->limits.discard_alignment = 0; + q->limits.discard_granularity = 1; + } else { + q->limits.discard_alignment = sdkp->unmap_alignment * + logical_block_size; + q->limits.discard_granularity = + max(sdkp->physical_block_size, + sdkp->unmap_granularity * logical_block_size); + } sdkp->provisioning_mode = mode; @@ -1535,6 +1550,100 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, } #endif +static char sd_pr_type(enum pr_type type) +{ + switch (type) { + case PR_WRITE_EXCLUSIVE: + return 0x01; + case PR_EXCLUSIVE_ACCESS: + return 0x03; + case PR_WRITE_EXCLUSIVE_REG_ONLY: + return 0x05; + case PR_EXCLUSIVE_ACCESS_REG_ONLY: + return 0x06; + case PR_WRITE_EXCLUSIVE_ALL_REGS: + return 0x07; + case PR_EXCLUSIVE_ACCESS_ALL_REGS: + return 0x08; + default: + return 0; + } +}; + +static int sd_pr_command(struct block_device *bdev, u8 sa, + u64 key, u64 sa_key, u8 type, u8 flags) +{ + struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; + struct scsi_sense_hdr sshdr; + int result; + u8 cmd[16] = { 0, }; + u8 data[24] = { 0, }; + + cmd[0] = PERSISTENT_RESERVE_OUT; + cmd[1] = sa; + cmd[2] = type; + put_unaligned_be32(sizeof(data), &cmd[5]); + + put_unaligned_be64(key, &data[0]); + put_unaligned_be64(sa_key, &data[8]); + data[20] = flags; + + result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, &data, sizeof(data), + &sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL); + + if ((driver_byte(result) & DRIVER_SENSE) && + (scsi_sense_valid(&sshdr))) { + sdev_printk(KERN_INFO, sdev, "PR command failed: %d\n", result); + scsi_print_sense_hdr(sdev, NULL, &sshdr); + } + + return result; +} + +static int sd_pr_register(struct block_device *bdev, u64 old_key, u64 new_key, + u32 flags) +{ + if (flags & ~PR_FL_IGNORE_KEY) + return -EOPNOTSUPP; + return sd_pr_command(bdev, (flags & PR_FL_IGNORE_KEY) ? 0x06 : 0x00, + old_key, new_key, 0, + (1 << 0) /* APTPL */ | + (1 << 2) /* ALL_TG_PT */); +} + +static int sd_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type, + u32 flags) +{ + if (flags) + return -EOPNOTSUPP; + return sd_pr_command(bdev, 0x01, key, 0, sd_pr_type(type), 0); +} + +static int sd_pr_release(struct block_device *bdev, u64 key, enum pr_type type) +{ + return sd_pr_command(bdev, 0x02, key, 0, sd_pr_type(type), 0); +} + +static int sd_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key, + enum pr_type type, bool abort) +{ + return sd_pr_command(bdev, abort ? 0x05 : 0x04, old_key, new_key, + sd_pr_type(type), 0); +} + +static int sd_pr_clear(struct block_device *bdev, u64 key) +{ + return sd_pr_command(bdev, 0x03, key, 0, 0, 0); +} + +static const struct pr_ops sd_pr_ops = { + .pr_register = sd_pr_register, + .pr_reserve = sd_pr_reserve, + .pr_release = sd_pr_release, + .pr_preempt = sd_pr_preempt, + .pr_clear = sd_pr_clear, +}; + static const struct block_device_operations sd_fops = { .owner = THIS_MODULE, .open = sd_open, @@ -1547,6 +1656,7 @@ static const struct block_device_operations sd_fops = { .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, + .pr_ops = &sd_pr_ops, }; /** @@ -2224,11 +2334,8 @@ got_data: } } - if (sdkp->capacity > 0xffffffff) { + if (sdkp->capacity > 0xffffffff) sdp->use_16_for_rw = 1; - sdkp->max_xfer_blocks = SD_MAX_XFER_BLOCKS; - } else - sdkp->max_xfer_blocks = SD_DEF_XFER_BLOCKS; /* Rescale capacity to 512-byte units */ if (sector_size == 4096) @@ -2545,7 +2652,6 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) { unsigned int sector_sz = sdkp->device->sector_size; const int vpd_len = 64; - u32 max_xfer_length; unsigned char *buffer = kmalloc(vpd_len, GFP_KERNEL); if (!buffer || @@ -2553,14 +2659,11 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len)) goto out; - max_xfer_length = get_unaligned_be32(&buffer[8]); - if (max_xfer_length) - sdkp->max_xfer_blocks = max_xfer_length; - blk_queue_io_min(sdkp->disk->queue, get_unaligned_be16(&buffer[6]) * sector_sz); - blk_queue_io_opt(sdkp->disk->queue, - get_unaligned_be32(&buffer[12]) * sector_sz); + + sdkp->max_xfer_blocks = get_unaligned_be32(&buffer[8]); + sdkp->opt_xfer_blocks = get_unaligned_be32(&buffer[12]); if (buffer[3] == 0x3c) { unsigned int lba_count, desc_count; @@ -2709,6 +2812,11 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) return 0; } +static inline u32 logical_to_sectors(struct scsi_device *sdev, u32 blocks) +{ + return blocks << (ilog2(sdev->sector_size) - 9); +} + /** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. @@ -2718,8 +2826,9 @@ static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device; + struct request_queue *q = sdkp->disk->queue; unsigned char *buffer; - unsigned int max_xfer; + unsigned int dev_max, rw_max; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_revalidate_disk\n")); @@ -2767,11 +2876,29 @@ static int sd_revalidate_disk(struct gendisk *disk) */ sd_set_flush_flag(sdkp); - max_xfer = sdkp->max_xfer_blocks; - max_xfer <<= ilog2(sdp->sector_size) - 9; + /* Initial block count limit based on CDB TRANSFER LENGTH field size. */ + dev_max = sdp->use_16_for_rw ? SD_MAX_XFER_BLOCKS : SD_DEF_XFER_BLOCKS; + + /* Some devices report a maximum block count for READ/WRITE requests. */ + dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks); + q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max); + + /* + * Use the device's preferred I/O size for reads and writes + * unless the reported value is unreasonably small, large, or + * garbage. + */ + if (sdkp->opt_xfer_blocks && + sdkp->opt_xfer_blocks <= dev_max && + sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS && + sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE) + rw_max = q->limits.io_opt = + logical_to_sectors(sdp, sdkp->opt_xfer_blocks); + else + rw_max = BLK_DEF_MAX_SECTORS; - sdkp->disk->queue->limits.max_sectors = - min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), max_xfer); + /* Combine with controller limits */ + q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q)); set_capacity(disk, sdkp->capacity); sd_config_write_same(sdkp); @@ -3068,7 +3195,6 @@ static void scsi_disk_release(struct device *dev) ida_remove(&sd_index_ida, sdkp->index); spin_unlock(&sd_index_lock); - blk_integrity_unregister(disk); disk->private_data = NULL; put_disk(disk); put_device(&sdkp->device->sdev_gendev); |