diff options
Diffstat (limited to 'drivers/scsi/sd.c')
| -rw-r--r-- | drivers/scsi/sd.c | 166 | 
1 files changed, 158 insertions, 8 deletions
| diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 523bf2fdc253..65875a598d62 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -48,6 +48,7 @@  #include <linux/blkpg.h>  #include <linux/blk-pm.h>  #include <linux/delay.h> +#include <linux/major.h>  #include <linux/mutex.h>  #include <linux/string_helpers.h>  #include <linux/async.h> @@ -109,7 +110,7 @@ static int  sd_remove(struct device *);  static void sd_shutdown(struct device *);  static int sd_suspend_system(struct device *);  static int sd_suspend_runtime(struct device *); -static int sd_resume(struct device *); +static int sd_resume_system(struct device *);  static int sd_resume_runtime(struct device *);  static void sd_rescan(struct device *);  static blk_status_t sd_init_command(struct scsi_cmnd *SCpnt); @@ -602,9 +603,9 @@ static struct class sd_disk_class = {  static const struct dev_pm_ops sd_pm_ops = {  	.suspend		= sd_suspend_system, -	.resume			= sd_resume, +	.resume			= sd_resume_system,  	.poweroff		= sd_suspend_system, -	.restore		= sd_resume, +	.restore		= sd_resume_system,  	.runtime_suspend	= sd_suspend_runtime,  	.runtime_resume		= sd_resume_runtime,  }; @@ -1756,6 +1757,44 @@ static void sd_rescan(struct device *dev)  	sd_revalidate_disk(sdkp->disk);  } +static int sd_get_unique_id(struct gendisk *disk, u8 id[16], +		enum blk_unique_id type) +{ +	struct scsi_device *sdev = scsi_disk(disk)->device; +	const struct scsi_vpd *vpd; +	const unsigned char *d; +	int ret = -ENXIO, len; + +	rcu_read_lock(); +	vpd = rcu_dereference(sdev->vpd_pg83); +	if (!vpd) +		goto out_unlock; + +	ret = -EINVAL; +	for (d = vpd->data + 4; d < vpd->data + vpd->len; d += d[3] + 4) { +		/* we only care about designators with LU association */ +		if (((d[1] >> 4) & 0x3) != 0x00) +			continue; +		if ((d[1] & 0xf) != type) +			continue; + +		/* +		 * Only exit early if a 16-byte descriptor was found.  Otherwise +		 * keep looking as one with more entropy might still show up. +		 */ +		len = d[3]; +		if (len != 8 && len != 12 && len != 16) +			continue; +		ret = len; +		memcpy(id, d + 4, len); +		if (len == 16) +			break; +	} +out_unlock: +	rcu_read_unlock(); +	return ret; +} +  static char sd_pr_type(enum pr_type type)  {  	switch (type) { @@ -1860,6 +1899,7 @@ static const struct block_device_operations sd_fops = {  	.check_events		= sd_check_events,  	.unlock_native_capacity	= sd_unlock_native_capacity,  	.report_zones		= sd_zbc_report_zones, +	.get_unique_id		= sd_get_unique_id,  	.pr_ops			= &sd_pr_ops,  }; @@ -2607,6 +2647,13 @@ sd_do_mode_sense(struct scsi_disk *sdkp, int dbd, int modepage,  		 unsigned char *buffer, int len, struct scsi_mode_data *data,  		 struct scsi_sense_hdr *sshdr)  { +	/* +	 * If we must use MODE SENSE(10), make sure that the buffer length +	 * is at least 8 bytes so that the mode sense header fits. +	 */ +	if (sdkp->device->use_10_for_ms && len < 8) +		len = 8; +  	return scsi_mode_sense(sdkp->device, dbd, modepage, buffer, len,  			       SD_TIMEOUT, sdkp->max_retries, data,  			       sshdr); @@ -2785,7 +2832,8 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)  			}  		} -		sd_first_printk(KERN_ERR, sdkp, "No Caching mode page found\n"); +		sd_first_printk(KERN_WARNING, sdkp, +				"No Caching mode page found\n");  		goto defaults;  	Page_found: @@ -2840,7 +2888,7 @@ defaults:  				"Assuming drive cache: write back\n");  		sdkp->WCE = 1;  	} else { -		sd_first_printk(KERN_ERR, sdkp, +		sd_first_printk(KERN_WARNING, sdkp,  				"Assuming drive cache: write through\n");  		sdkp->WCE = 0;  	} @@ -3087,6 +3135,86 @@ static void sd_read_security(struct scsi_disk *sdkp, unsigned char *buffer)  		sdkp->security = 1;  } +static inline sector_t sd64_to_sectors(struct scsi_disk *sdkp, u8 *buf) +{ +	return logical_to_sectors(sdkp->device, get_unaligned_be64(buf)); +} + +/** + * sd_read_cpr - Query concurrent positioning ranges + * @sdkp:	disk to query + */ +static void sd_read_cpr(struct scsi_disk *sdkp) +{ +	struct blk_independent_access_ranges *iars = NULL; +	unsigned char *buffer = NULL; +	unsigned int nr_cpr = 0; +	int i, vpd_len, buf_len = SD_BUF_SIZE; +	u8 *desc; + +	/* +	 * We need to have the capacity set first for the block layer to be +	 * able to check the ranges. +	 */ +	if (sdkp->first_scan) +		return; + +	if (!sdkp->capacity) +		goto out; + +	/* +	 * Concurrent Positioning Ranges VPD: there can be at most 256 ranges, +	 * leading to a maximum page size of 64 + 256*32 bytes. +	 */ +	buf_len = 64 + 256*32; +	buffer = kmalloc(buf_len, GFP_KERNEL); +	if (!buffer || scsi_get_vpd_page(sdkp->device, 0xb9, buffer, buf_len)) +		goto out; + +	/* We must have at least a 64B header and one 32B range descriptor */ +	vpd_len = get_unaligned_be16(&buffer[2]) + 3; +	if (vpd_len > buf_len || vpd_len < 64 + 32 || (vpd_len & 31)) { +		sd_printk(KERN_ERR, sdkp, +			  "Invalid Concurrent Positioning Ranges VPD page\n"); +		goto out; +	} + +	nr_cpr = (vpd_len - 64) / 32; +	if (nr_cpr == 1) { +		nr_cpr = 0; +		goto out; +	} + +	iars = disk_alloc_independent_access_ranges(sdkp->disk, nr_cpr); +	if (!iars) { +		nr_cpr = 0; +		goto out; +	} + +	desc = &buffer[64]; +	for (i = 0; i < nr_cpr; i++, desc += 32) { +		if (desc[0] != i) { +			sd_printk(KERN_ERR, sdkp, +				"Invalid Concurrent Positioning Range number\n"); +			nr_cpr = 0; +			break; +		} + +		iars->ia_range[i].sector = sd64_to_sectors(sdkp, desc + 8); +		iars->ia_range[i].nr_sectors = sd64_to_sectors(sdkp, desc + 16); +	} + +out: +	disk_set_independent_access_ranges(sdkp->disk, iars); +	if (nr_cpr && sdkp->nr_actuators != nr_cpr) { +		sd_printk(KERN_NOTICE, sdkp, +			  "%u concurrent positioning ranges\n", nr_cpr); +		sdkp->nr_actuators = nr_cpr; +	} + +	kfree(buffer); +} +  /*   * Determine the device's preferred I/O size for reads and writes   * unless the reported value is unreasonably small, large, not a @@ -3202,6 +3330,7 @@ static int sd_revalidate_disk(struct gendisk *disk)  		sd_read_app_tag_own(sdkp, buffer);  		sd_read_write_same(sdkp, buffer);  		sd_read_security(sdkp, buffer); +		sd_read_cpr(sdkp);  	}  	/* @@ -3449,7 +3578,13 @@ static int sd_probe(struct device *dev)  		pm_runtime_set_autosuspend_delay(dev,  			sdp->host->hostt->rpm_autosuspend_delay);  	} -	device_add_disk(dev, gd, NULL); + +	error = device_add_disk(dev, gd, NULL); +	if (error) { +		put_device(&sdkp->dev); +		goto out; +	} +  	if (sdkp->capacity)  		sd_dif_config_host(sdkp); @@ -3497,7 +3632,6 @@ static int sd_remove(struct device *dev)  	sdkp = dev_get_drvdata(dev);  	scsi_autopm_get_device(sdkp->device); -	async_synchronize_full_domain(&scsi_sd_pm_domain);  	device_del(&sdkp->dev);  	del_gendisk(sdkp->disk);  	sd_shutdown(dev); @@ -3654,6 +3788,9 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)  static int sd_suspend_system(struct device *dev)  { +	if (pm_runtime_suspended(dev)) +		return 0; +  	return sd_suspend_common(dev, true);  } @@ -3680,10 +3817,23 @@ static int sd_resume(struct device *dev)  	return ret;  } +static int sd_resume_system(struct device *dev) +{ +	if (pm_runtime_suspended(dev)) +		return 0; + +	return sd_resume(dev); +} +  static int sd_resume_runtime(struct device *dev)  {  	struct scsi_disk *sdkp = dev_get_drvdata(dev); -	struct scsi_device *sdp = sdkp->device; +	struct scsi_device *sdp; + +	if (!sdkp)	/* E.g.: runtime resume at the start of sd_probe() */ +		return 0; + +	sdp = sdkp->device;  	if (sdp->ignore_media_change) {  		/* clear the device's sense data */ | 
