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 | 104 +++++++++++++++++++++++++++++++++++ include/scsi/scsi_block_pr.h | 20 +++++++ include/scsi/scsi_proto.h | 5 ++ 3 files changed, 129 insertions(+) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ad9374b07585..86b602399000 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1695,6 +1695,108 @@ 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] = { PERSISTENT_RESERVE_IN, sa }; + struct scsi_failure failures[] = { + { + .sense = UNIT_ATTENTION, + .asc = SCMD_FAILURE_ASC_ANY, + .ascq = SCMD_FAILURE_ASCQ_ANY, + .allowed = 5, + .result = SAM_STAT_CHECK_CONDITION, + }, + {}, + }; + int result; + + put_unaligned_be16(data_len, &cmd[7]); + + result = scsi_exec_req(((struct scsi_exec_args) { + .sdev = sdev, + .cmd = cmd, + .data_dir = DMA_FROM_DEVICE, + .buf = data, + .buf_len = data_len, + .sshdr = &sshdr, + .timeout = SD_TIMEOUT, + .retries = sdkp->max_retries, + .failures = failures })); + 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, + u32 keys_len) +{ + int result, i, data_offset, num_copy_keys; + int data_len = keys_len + 8; + u8 *data; + + data = kzalloc(data_len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + result = sd_pr_in_command(bdev, READ_KEYS, data, data_len); + if (result) + goto free_data; + + keys_info->generation = get_unaligned_be32(&data[0]); + keys_info->num_keys = get_unaligned_be32(&data[4]) / 8; + + data_offset = 8; + num_copy_keys = min(keys_len / 8, keys_info->num_keys); + + for (i = 0; i < num_copy_keys; i++) { + keys_info->keys[i] = get_unaligned_be64(&data[data_offset]); + data_offset += 8; + } + +free_data: + kfree(data); + 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, enum scsi_pr_type type, u8 flags) { @@ -1787,6 +1889,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_block_pr.h b/include/scsi/scsi_block_pr.h index 6e99f844330d..137bf2247273 100644 --- a/include/scsi/scsi_block_pr.h +++ b/include/scsi/scsi_block_pr.h @@ -33,4 +33,24 @@ static inline enum scsi_pr_type block_pr_type_to_scsi(enum pr_type type) } }; +static inline enum pr_type scsi_pr_type_to_block(enum scsi_pr_type type) +{ + switch (type) { + case SCSI_PR_WRITE_EXCLUSIVE: + return PR_WRITE_EXCLUSIVE; + case SCSI_PR_EXCLUSIVE_ACCESS: + return PR_EXCLUSIVE_ACCESS; + case SCSI_PR_WRITE_EXCLUSIVE_REG_ONLY: + return PR_WRITE_EXCLUSIVE_REG_ONLY; + case SCSI_PR_EXCLUSIVE_ACCESS_REG_ONLY: + return PR_EXCLUSIVE_ACCESS_REG_ONLY; + case SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS: + return PR_WRITE_EXCLUSIVE_ALL_REGS; + case SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS: + return PR_EXCLUSIVE_ACCESS_ALL_REGS; + default: + return 0; + } +} + #endif diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index c03e35fc382c..0fd6e295375a 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