@@ -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)
@@ -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
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> --- drivers/scsi/sd.c | 88 +++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_proto.h | 5 +++ 2 files changed, 93 insertions(+)