forked from mirrors/linux
		
	scsi: sd: add concurrent positioning ranges support
Add the sd_read_cpr() function to the sd scsi disk driver to discover if a device has multiple concurrent positioning ranges (i.e. multiple actuators on an HDD). The existence of VPD page B9h indicates if a device has multiple concurrent positioning ranges. The page content describes each range supported by the device. sd_read_cpr() is called from sd_revalidate_disk() and uses the block layer functions disk_alloc_independent_access_ranges() and disk_set_independent_access_ranges() to represent the set of actuators of the device as independent access ranges. The format of the Concurrent Positioning Ranges VPD page B9h is defined in section 6.6.6 of SBC-5. Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Reviewed-by: Keith Busch <kbusch@kernel.org> Link: https://lore.kernel.org/r/20211027022223.183838-3-damien.lemoal@wdc.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
		
							parent
							
								
									a2247f19ee
								
							
						
					
					
						commit
						e815d36548
					
				
					 2 changed files with 82 additions and 0 deletions
				
			
		| 
						 | 
					@ -3088,6 +3088,86 @@ static void sd_read_security(struct scsi_disk *sdkp, unsigned char *buffer)
 | 
				
			||||||
		sdkp->security = 1;
 | 
							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
 | 
					 * Determine the device's preferred I/O size for reads and writes
 | 
				
			||||||
 * unless the reported value is unreasonably small, large, not a
 | 
					 * unless the reported value is unreasonably small, large, not a
 | 
				
			||||||
| 
						 | 
					@ -3203,6 +3283,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
 | 
				
			||||||
		sd_read_app_tag_own(sdkp, buffer);
 | 
							sd_read_app_tag_own(sdkp, buffer);
 | 
				
			||||||
		sd_read_write_same(sdkp, buffer);
 | 
							sd_read_write_same(sdkp, buffer);
 | 
				
			||||||
		sd_read_security(sdkp, buffer);
 | 
							sd_read_security(sdkp, buffer);
 | 
				
			||||||
 | 
							sd_read_cpr(sdkp);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,6 +106,7 @@ struct scsi_disk {
 | 
				
			||||||
	u8		protection_type;/* Data Integrity Field */
 | 
						u8		protection_type;/* Data Integrity Field */
 | 
				
			||||||
	u8		provisioning_mode;
 | 
						u8		provisioning_mode;
 | 
				
			||||||
	u8		zeroing_mode;
 | 
						u8		zeroing_mode;
 | 
				
			||||||
 | 
						u8		nr_actuators;		/* Number of actuators */
 | 
				
			||||||
	unsigned	ATO : 1;	/* state of disk ATO bit */
 | 
						unsigned	ATO : 1;	/* state of disk ATO bit */
 | 
				
			||||||
	unsigned	cache_override : 1; /* temp override of WCE,RCD */
 | 
						unsigned	cache_override : 1; /* temp override of WCE,RCD */
 | 
				
			||||||
	unsigned	WCE : 1;	/* state of disk WCE bit */
 | 
						unsigned	WCE : 1;	/* state of disk WCE bit */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue