diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 131 |
1 files changed, 86 insertions, 45 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index b6bb4e0ce0e3..bea36adeee17 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -50,6 +50,7 @@ #include <linux/string_helpers.h> #include <linux/async.h> #include <linux/slab.h> +#include <linux/sed-opal.h> #include <linux/pm_runtime.h> #include <linux/pr.h> #include <linux/t10-pi.h> @@ -155,7 +156,7 @@ static ssize_t cache_type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int i, ct = -1, rcd, wce, sp; + int ct, rcd, wce, sp; struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; char buffer[64]; @@ -178,16 +179,10 @@ cache_type_store(struct device *dev, struct device_attribute *attr, sdkp->cache_override = 0; } - for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) { - len = strlen(sd_cache_types[i]); - if (strncmp(sd_cache_types[i], buf, len) == 0 && - buf[len] == '\n') { - ct = i; - break; - } - } + ct = sysfs_match_string(sd_cache_types, buf); if (ct < 0) return -EINVAL; + rcd = ct & 0x01 ? 1 : 0; wce = (ct & 0x02) && !sdkp->write_prot ? 1 : 0; @@ -227,7 +222,7 @@ manage_start_stop_show(struct device *dev, struct device_attribute *attr, struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; - return snprintf(buf, 20, "%u\n", sdp->manage_start_stop); + return sprintf(buf, "%u\n", sdp->manage_start_stop); } static ssize_t @@ -251,7 +246,7 @@ allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart); + return sprintf(buf, "%u\n", sdkp->device->allow_restart); } static ssize_t @@ -279,7 +274,7 @@ cache_type_show(struct device *dev, struct device_attribute *attr, char *buf) struct scsi_disk *sdkp = to_scsi_disk(dev); int ct = sdkp->RCD + 2*sdkp->WCE; - return snprintf(buf, 40, "%s\n", sd_cache_types[ct]); + return sprintf(buf, "%s\n", sd_cache_types[ct]); } static DEVICE_ATTR_RW(cache_type); @@ -288,7 +283,7 @@ FUA_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->DPOFUA); + return sprintf(buf, "%u\n", sdkp->DPOFUA); } static DEVICE_ATTR_RO(FUA); @@ -298,7 +293,7 @@ protection_type_show(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->protection_type); + return sprintf(buf, "%u\n", sdkp->protection_type); } static ssize_t @@ -341,9 +336,9 @@ protection_mode_show(struct device *dev, struct device_attribute *attr, } if (!dif && !dix) - return snprintf(buf, 20, "none\n"); + return sprintf(buf, "none\n"); - return snprintf(buf, 20, "%s%u\n", dix ? "dix" : "dif", dif); + return sprintf(buf, "%s%u\n", dix ? "dix" : "dif", dif); } static DEVICE_ATTR_RO(protection_mode); @@ -352,7 +347,7 @@ app_tag_own_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->ATO); + return sprintf(buf, "%u\n", sdkp->ATO); } static DEVICE_ATTR_RO(app_tag_own); @@ -362,10 +357,11 @@ thin_provisioning_show(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->lbpme); + return sprintf(buf, "%u\n", sdkp->lbpme); } static DEVICE_ATTR_RO(thin_provisioning); +/* sysfs_match_string() requires dense arrays */ static const char *lbp_mode[] = { [SD_LBP_FULL] = "full", [SD_LBP_UNMAP] = "unmap", @@ -381,7 +377,7 @@ provisioning_mode_show(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%s\n", lbp_mode[sdkp->provisioning_mode]); + return sprintf(buf, "%s\n", lbp_mode[sdkp->provisioning_mode]); } static ssize_t @@ -390,6 +386,7 @@ provisioning_mode_store(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; + int mode; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -402,23 +399,17 @@ provisioning_mode_store(struct device *dev, struct device_attribute *attr, if (sdp->type != TYPE_DISK) return -EINVAL; - if (!strncmp(buf, lbp_mode[SD_LBP_UNMAP], 20)) - sd_config_discard(sdkp, SD_LBP_UNMAP); - else if (!strncmp(buf, lbp_mode[SD_LBP_WS16], 20)) - sd_config_discard(sdkp, SD_LBP_WS16); - else if (!strncmp(buf, lbp_mode[SD_LBP_WS10], 20)) - sd_config_discard(sdkp, SD_LBP_WS10); - else if (!strncmp(buf, lbp_mode[SD_LBP_ZERO], 20)) - sd_config_discard(sdkp, SD_LBP_ZERO); - else if (!strncmp(buf, lbp_mode[SD_LBP_DISABLE], 20)) - sd_config_discard(sdkp, SD_LBP_DISABLE); - else + mode = sysfs_match_string(lbp_mode, buf); + if (mode < 0) return -EINVAL; + sd_config_discard(sdkp, mode); + return count; } static DEVICE_ATTR_RW(provisioning_mode); +/* sysfs_match_string() requires dense arrays */ static const char *zeroing_mode[] = { [SD_ZERO_WRITE] = "write", [SD_ZERO_WS] = "writesame", @@ -432,7 +423,7 @@ zeroing_mode_show(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%s\n", zeroing_mode[sdkp->zeroing_mode]); + return sprintf(buf, "%s\n", zeroing_mode[sdkp->zeroing_mode]); } static ssize_t @@ -440,21 +431,17 @@ zeroing_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct scsi_disk *sdkp = to_scsi_disk(dev); + int mode; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (!strncmp(buf, zeroing_mode[SD_ZERO_WRITE], 20)) - sdkp->zeroing_mode = SD_ZERO_WRITE; - else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS], 20)) - sdkp->zeroing_mode = SD_ZERO_WS; - else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS16_UNMAP], 20)) - sdkp->zeroing_mode = SD_ZERO_WS16_UNMAP; - else if (!strncmp(buf, zeroing_mode[SD_ZERO_WS10_UNMAP], 20)) - sdkp->zeroing_mode = SD_ZERO_WS10_UNMAP; - else + mode = sysfs_match_string(zeroing_mode, buf); + if (mode < 0) return -EINVAL; + sdkp->zeroing_mode = mode; + return count; } static DEVICE_ATTR_RW(zeroing_mode); @@ -465,7 +452,7 @@ max_medium_access_timeouts_show(struct device *dev, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->max_medium_access_timeouts); + return sprintf(buf, "%u\n", sdkp->max_medium_access_timeouts); } static ssize_t @@ -491,7 +478,7 @@ max_write_same_blocks_show(struct device *dev, struct device_attribute *attr, { struct scsi_disk *sdkp = to_scsi_disk(dev); - return snprintf(buf, 20, "%u\n", sdkp->max_ws_blocks); + return sprintf(buf, "%u\n", sdkp->max_ws_blocks); } static ssize_t @@ -643,6 +630,26 @@ static void scsi_disk_put(struct scsi_disk *sdkp) mutex_unlock(&sd_ref_mutex); } +#ifdef CONFIG_BLK_SED_OPAL +static int sd_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, + size_t len, bool send) +{ + struct scsi_device *sdev = data; + u8 cdb[12] = { 0, }; + int ret; + + cdb[0] = send ? SECURITY_PROTOCOL_OUT : SECURITY_PROTOCOL_IN; + cdb[1] = secp; + put_unaligned_be16(spsp, &cdb[2]); + put_unaligned_be32(len, &cdb[6]); + + ret = scsi_execute_req(sdev, cdb, + send ? DMA_TO_DEVICE : DMA_FROM_DEVICE, + buffer, len, NULL, SD_TIMEOUT, SD_MAX_RETRIES, NULL); + return ret <= 0 ? ret : -EIO; +} +#endif /* CONFIG_BLK_SED_OPAL */ + static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, unsigned int dix, unsigned int dif) { @@ -696,6 +703,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) switch (mode) { + case SD_LBP_FULL: case SD_LBP_DISABLE: blk_queue_max_discard_sectors(q, 0); queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q); @@ -1454,6 +1462,9 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, if (error) goto out; + if (is_sed_ioctl(cmd)) + return sed_ioctl(sdkp->opal_dev, cmd, p); + /* * Send SCSI addressing ioctls directly to mid level, send other * ioctls to block level and then onto mid level if they can't be @@ -1818,8 +1829,9 @@ static void sd_eh_reset(struct scsi_cmnd *scmd) static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp) { struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk); + struct scsi_device *sdev = scmd->device; - if (!scsi_device_online(scmd->device) || + if (!scsi_device_online(sdev) || !scsi_medium_access_command(scmd) || host_byte(scmd->result) != DID_TIME_OUT || eh_disp != SUCCESS) @@ -1845,7 +1857,9 @@ static int sd_eh_action(struct scsi_cmnd *scmd, int eh_disp) if (sdkp->medium_access_timed_out >= sdkp->max_medium_access_timeouts) { scmd_printk(KERN_ERR, scmd, "Medium access timeout failure. Offlining disk!\n"); - scsi_device_set_state(scmd->device, SDEV_OFFLINE); + mutex_lock(&sdev->state_mutex); + scsi_device_set_state(sdev, SDEV_OFFLINE); + mutex_unlock(&sdev->state_mutex); return SUCCESS; } @@ -3014,6 +3028,20 @@ static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer) sdkp->ws10 = 1; } +static void sd_read_security(struct scsi_disk *sdkp, unsigned char *buffer) +{ + struct scsi_device *sdev = sdkp->device; + + if (!sdev->security_supported) + return; + + if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, + SECURITY_PROTOCOL_IN) == 1 && + scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, + SECURITY_PROTOCOL_OUT) == 1) + sdkp->security = 1; +} + /** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. @@ -3067,6 +3095,7 @@ static int sd_revalidate_disk(struct gendisk *disk) sd_read_cache_type(sdkp, buffer); sd_read_app_tag_own(sdkp, buffer); sd_read_write_same(sdkp, buffer); + sd_read_security(sdkp, buffer); } sdkp->first_scan = 0; @@ -3227,6 +3256,12 @@ static void sd_probe_async(void *data, async_cookie_t cookie) sd_revalidate_disk(gd); + if (sdkp->security) { + sdkp->opal_dev = init_opal_dev(sdp, &sd_sec_submit); + if (sdkp->opal_dev) + sd_printk(KERN_NOTICE, sdkp, "supports TCG Opal\n"); + } + sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", sdp->removable ? "removable " : ""); scsi_autopm_put_device(sdp); @@ -3376,6 +3411,8 @@ static int sd_remove(struct device *dev) sd_zbc_remove(sdkp); + free_opal_dev(sdkp->opal_dev); + blk_register_region(devt, SD_MINORS, NULL, sd_default_probe, NULL, NULL); @@ -3528,6 +3565,7 @@ static int sd_suspend_runtime(struct device *dev) static int sd_resume(struct device *dev) { struct scsi_disk *sdkp = dev_get_drvdata(dev); + int ret; if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */ return 0; @@ -3536,7 +3574,10 @@ static int sd_resume(struct device *dev) return 0; sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); - return sd_start_stop_device(sdkp, 1); + ret = sd_start_stop_device(sdkp, 1); + if (!ret) + opal_unlock_from_suspend(sdkp->opal_dev); + return ret; } /** |