This adds support in sd.c for the block PR read keys and read reservation callouts. Signed-off-by: Mike Christie <michael.christie@xxxxxxxxxx> --- drivers/scsi/sd.c | 88 +++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_proto.h | 5 +++ 2 files changed, 93 insertions(+) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 765fca0f38c7..71283aaf3e82 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1684,6 +1684,92 @@ static int sd_get_unique_id(struct gendisk *disk, u8 id[16], return ret; } +static int sd_pr_in_command(struct block_device *bdev, u8 sa, + unsigned char *data, int data_len) +{ + struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); + struct scsi_device *sdev = sdkp->device; + struct scsi_sense_hdr sshdr; + u8 cmd[10] = { 0, }; + int result; + + cmd[0] = PERSISTENT_RESERVE_IN; + cmd[1] = sa; + put_unaligned_be16(data_len, &cmd[7]); + + result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, data, data_len, + &sshdr, SD_TIMEOUT, sdkp->max_retries, NULL); + if (scsi_status_is_check_condition(result) && + 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_read_keys(struct block_device *bdev, struct pr_keys *keys_info, + int keys_info_len) +{ + u8 *data = (u8 *)keys_info; + int result, i, data_offset; + + if (keys_info_len < 8) + return -EINVAL; + + result = sd_pr_in_command(bdev, READ_KEYS, data, keys_info_len); + if (result) + return result; + /* + * The device gives us the info in BE, but users want it in a easier + * to use format so we convert it for them. + */ + keys_info->generation = get_unaligned_be32(&data[0]); + keys_info->num_keys = get_unaligned_be32(&data[4]) / 8; + + data_offset = 8; + for (i = 0; i < keys_info->num_keys; i++) { + if (data_offset + 8 > keys_info_len - 8) + return -EOVERFLOW; + + keys_info->keys[i] = get_unaligned_be64(&data[data_offset]); + data_offset += 8; + } + + return result; +} + +static int sd_pr_read_reservation(struct block_device *bdev, + struct pr_held_reservation *rsv) +{ + struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); + struct scsi_device *sdev = sdkp->device; + u8 data[24] = { 0, }; + int result, len; + + result = sd_pr_in_command(bdev, READ_RESERVATION, data, sizeof(data)); + if (result) + return result; + + memset(rsv, 0, sizeof(*rsv)); + len = get_unaligned_be32(&data[4]); + if (!len) + return result; + + /* Make sure we have at least the key and type */ + if (len < 14) { + sdev_printk(KERN_INFO, sdev, + "READ RESERVATION failed due to short return buffer of %d bytes\n", + len); + return -EINVAL; + } + + rsv->generation = get_unaligned_be32(&data[0]); + rsv->key = get_unaligned_be64(&data[8]); + rsv->type = scsi_pr_type_to_block(data[21] & 0x0f); + return 0; +} + static int sd_pr_out_command(struct block_device *bdev, u8 sa, u64 key, u64 sa_key, u8 type, u8 flags) { @@ -1758,6 +1844,8 @@ static const struct pr_ops sd_pr_ops = { .pr_release = sd_pr_release, .pr_preempt = sd_pr_preempt, .pr_clear = sd_pr_clear, + .pr_read_keys = sd_pr_read_keys, + .pr_read_reservation = sd_pr_read_reservation, }; static void scsi_disk_free_disk(struct gendisk *disk) diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index f017843a8124..07ff33ea27e9 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -151,6 +151,11 @@ #define ZO_FINISH_ZONE 0x02 #define ZO_OPEN_ZONE 0x03 #define ZO_RESET_WRITE_POINTER 0x04 +/* values for PR in service action */ +#define READ_KEYS 0x00 +#define READ_RESERVATION 0x01 +#define REPORT_CAPABILITES 0x02 +#define READ_FULL_STATUS 0x03 /* values for variable length command */ #define XDREAD_32 0x03 #define XDWRITE_32 0x04 -- 2.25.1