Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
scsi: Add support for block PR read keys/reservation
This adds support in sd.c for the block PR read keys and read reservation
callouts.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
  • Loading branch information
mikechristie authored and intel-lab-lkp committed Oct 26, 2022
1 parent 6a8583c commit f8aa965
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
104 changes: 104 additions & 0 deletions drivers/scsi/sd.c
Expand Up @@ -1682,6 +1682,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)
{
Expand Down Expand Up @@ -1756,6 +1858,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)
Expand Down
20 changes: 20 additions & 0 deletions include/scsi/scsi_block_pr.h
Expand Up @@ -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
5 changes: 5 additions & 0 deletions include/scsi/scsi_proto.h
Expand Up @@ -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
Expand Down

0 comments on commit f8aa965

Please sign in to comment.