scsi: Add support for block PR read keys/reservation
authorMike Christie <michael.christie@oracle.com>
Fri, 7 Apr 2023 20:05:38 +0000 (15:05 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 12 Apr 2023 01:55:35 +0000 (21:55 -0400)
This adds support in sd.c for the block PR read keys and read reservation
callouts, so upper layers like LIO can get the PR info that's been setup
using the existing pr callouts and return it to initiators.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
Link: https://lore.kernel.org/r/20230407200551.12660-6-michael.christie@oracle.com
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_common.c
drivers/scsi/sd.c
include/scsi/scsi_common.h
include/scsi/scsi_proto.h

index 11bf6c275d4e5c4cd6487db6db4b7a00f227669e..b7a7a2eea887dc0593884b921222afe5ec91ab38 100644 (file)
@@ -64,6 +64,27 @@ const char *scsi_device_type(unsigned type)
 }
 EXPORT_SYMBOL(scsi_device_type);
 
+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;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_pr_type_to_block);
+
 enum scsi_pr_type block_pr_type_to_scsi(enum pr_type type)
 {
        switch (type) {
index fa3123a63f65f01c9915d62ee32326ca71db5fa1..3e7a69eeb8f01adf74680c7d935b72bf34446d0c 100644 (file)
@@ -1723,6 +1723,95 @@ static int sd_scsi_to_pr_err(struct scsi_sense_hdr *sshdr, int result)
        }
 }
 
+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 };
+       const struct scsi_exec_args exec_args = {
+               .sshdr = &sshdr,
+       };
+       int result;
+
+       put_unaligned_be16(data_len, &cmd[7]);
+
+       result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, data, data_len,
+                                 SD_TIMEOUT, sdkp->max_retries, &exec_args);
+       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);
+       }
+
+       if (result <= 0)
+               return result;
+
+       return sd_scsi_to_pr_err(&sshdr, result);
+}
+
+static int sd_pr_read_keys(struct block_device *bdev, struct pr_keys *keys_info)
+{
+       int result, i, data_offset, num_copy_keys;
+       u32 num_keys = keys_info->num_keys;
+       int data_len = num_keys * 8 + 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(num_keys, 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] = { };
+       int result, len;
+
+       result = sd_pr_in_command(bdev, READ_RESERVATION, data, sizeof(data));
+       if (result)
+               return result;
+
+       len = get_unaligned_be32(&data[4]);
+       if (!len)
+               return 0;
+
+       /* 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)
 {
@@ -1804,6 +1893,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)
index e25291bbbe9b061abeb821837e9f687e948b7dab..fb58715fac869b2b4cc8c01393fef7fc0137395c 100644 (file)
@@ -20,6 +20,7 @@ enum scsi_pr_type {
 };
 
 enum scsi_pr_type block_pr_type_to_scsi(enum pr_type type);
+enum pr_type scsi_pr_type_to_block(enum scsi_pr_type type);
 
 static inline unsigned
 scsi_varlen_cdb_length(const void *hdr)
index fbe5bdfe4d6e60f51c7bf461f560742ee8112b63..07d65c1f59db33e98591071cee9d8a9c3513ffbe 100644 (file)
 #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