Commit bfa8f186 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi

Pull more SCSI updates from James Bottomley:
 "The vfs has long had a write lifetime hint mechanism that gives the
  expected longevity on storage of the data being written. f2fs was the
  original consumer of this and used the hint for flash data placement
  (mostly to avoid write amplification by placing objects with similar
  lifetimes in the same erase block).

  More recently the SCSI based UFS (Universal Flash Storage) drivers
  have wanted to take advantage of this as well, for the same reasons as
  f2fs, necessitating plumbing the write hints through the block layer
  and then adding it to the SCSI core.

  The vfs write_hints already taken plumbs this as far as block and this
  completes the SCSI core enabling based on a recently agreed reuse of
  the old write command group number. The additions to the scsi_debug
  driver are for emulating this property so we can run tests on it in
  the absence of an actual UFS device"

* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi:
  scsi: scsi_debug: Maintain write statistics per group number
  scsi: scsi_debug: Implement GET STREAM STATUS
  scsi: scsi_debug: Implement the IO Advice Hints Grouping mode page
  scsi: scsi_debug: Allocate the MODE SENSE response from the heap
  scsi: scsi_debug: Rework subpage code error handling
  scsi: scsi_debug: Rework page code error handling
  scsi: scsi_debug: Support the block limits extension VPD page
  scsi: scsi_debug: Reduce code duplication
  scsi: sd: Translate data lifetime information
  scsi: scsi_proto: Add structures and constants related to I/O groups and streams
  scsi: core: Query the Block Limits Extension VPD page
parents e3111d9c af180c08
......@@ -241,6 +241,11 @@ config SCSI_SCAN_ASYNC
Note that this setting also affects whether resuming from
system suspend will be performed asynchronously.
config SCSI_PROTO_TEST
tristate "scsi_proto.h unit tests" if !KUNIT_ALL_TESTS
depends on SCSI && KUNIT
default KUNIT_ALL_TESTS
menu "SCSI Transports"
depends on SCSI
......
......@@ -24,6 +24,8 @@ obj-$(CONFIG_SCSI_COMMON) += scsi_common.o
obj-$(CONFIG_RAID_ATTRS) += raid_class.o
obj-$(CONFIG_SCSI_PROTO_TEST) += scsi_proto_test.o
# --- NOTE ORDERING HERE ---
# For kernel non-modular link, transport attributes need to
# be initialised before drivers
......
......@@ -517,6 +517,8 @@ void scsi_attach_vpd(struct scsi_device *sdev)
scsi_update_vpd_page(sdev, 0xb1, &sdev->vpd_pgb1);
if (vpd_buf->data[i] == 0xb2)
scsi_update_vpd_page(sdev, 0xb2, &sdev->vpd_pgb2);
if (vpd_buf->data[i] == 0xb7)
scsi_update_vpd_page(sdev, 0xb7, &sdev->vpd_pgb7);
}
kfree(vpd_buf);
}
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Google LLC
*/
#include <kunit/test.h>
#include <asm-generic/unaligned.h>
#include <scsi/scsi_proto.h>
static void test_scsi_proto(struct kunit *test)
{
static const union {
struct scsi_io_group_descriptor desc;
u8 arr[sizeof(struct scsi_io_group_descriptor)];
} d = { .arr = { 0x45, 0, 0, 0, 0xb0, 0xe4, 0xe3 } };
KUNIT_EXPECT_EQ(test, d.desc.io_advice_hints_mode + 0, 1);
KUNIT_EXPECT_EQ(test, d.desc.st_enble + 0, 1);
KUNIT_EXPECT_EQ(test, d.desc.cs_enble + 0, 0);
KUNIT_EXPECT_EQ(test, d.desc.ic_enable + 0, 1);
KUNIT_EXPECT_EQ(test, d.desc.acdlu + 0, 1);
KUNIT_EXPECT_EQ(test, d.desc.rlbsr + 0, 3);
KUNIT_EXPECT_EQ(test, d.desc.lbm_descriptor_type + 0, 0);
KUNIT_EXPECT_EQ(test, d.desc.params[0] + 0, 0xe4);
KUNIT_EXPECT_EQ(test, d.desc.params[1] + 0, 0xe3);
static const union {
struct scsi_stream_status s;
u8 arr[sizeof(struct scsi_stream_status)];
} ss = { .arr = { 0x80, 0, 0x12, 0x34, 0x3f } };
KUNIT_EXPECT_EQ(test, ss.s.perm + 0, 1);
KUNIT_EXPECT_EQ(test, get_unaligned_be16(&ss.s.stream_identifier),
0x1234);
KUNIT_EXPECT_EQ(test, ss.s.rel_lifetime + 0, 0x3f);
static const union {
struct scsi_stream_status_header h;
u8 arr[sizeof(struct scsi_stream_status_header)];
} sh = { .arr = { 1, 2, 3, 4, 0, 0, 5, 6 } };
KUNIT_EXPECT_EQ(test, get_unaligned_be32(&sh.h.len), 0x1020304);
KUNIT_EXPECT_EQ(test, get_unaligned_be16(&sh.h.number_of_open_streams),
0x506);
}
static struct kunit_case scsi_proto_test_cases[] = {
KUNIT_CASE(test_scsi_proto),
{}
};
static struct kunit_suite scsi_proto_test_suite = {
.name = "scsi_proto",
.test_cases = scsi_proto_test_cases,
};
kunit_test_suite(scsi_proto_test_suite);
MODULE_DESCRIPTION("<scsi/scsi_proto.h> unit tests");
MODULE_AUTHOR("Bart Van Assche");
MODULE_LICENSE("GPL");
......@@ -449,6 +449,7 @@ static void scsi_device_dev_release(struct device *dev)
struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL;
struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL;
struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL;
struct scsi_vpd *vpd_pgb7 = NULL;
unsigned long flags;
might_sleep();
......@@ -494,6 +495,8 @@ static void scsi_device_dev_release(struct device *dev)
lockdep_is_held(&sdev->inquiry_mutex));
vpd_pgb2 = rcu_replace_pointer(sdev->vpd_pgb2, vpd_pgb2,
lockdep_is_held(&sdev->inquiry_mutex));
vpd_pgb7 = rcu_replace_pointer(sdev->vpd_pgb7, vpd_pgb7,
lockdep_is_held(&sdev->inquiry_mutex));
mutex_unlock(&sdev->inquiry_mutex);
if (vpd_pg0)
......@@ -510,6 +513,8 @@ static void scsi_device_dev_release(struct device *dev)
kfree_rcu(vpd_pgb1, rcu);
if (vpd_pgb2)
kfree_rcu(vpd_pgb2, rcu);
if (vpd_pgb7)
kfree_rcu(vpd_pgb7, rcu);
kfree(sdev->inquiry);
kfree(sdev);
......@@ -921,6 +926,7 @@ sdev_vpd_pg_attr(pg89);
sdev_vpd_pg_attr(pgb0);
sdev_vpd_pg_attr(pgb1);
sdev_vpd_pg_attr(pgb2);
sdev_vpd_pg_attr(pgb7);
sdev_vpd_pg_attr(pg0);
static ssize_t show_inquiry(struct file *filep, struct kobject *kobj,
......@@ -1295,6 +1301,9 @@ static umode_t scsi_sdev_bin_attr_is_visible(struct kobject *kobj,
if (attr == &dev_attr_vpd_pgb2 && !sdev->vpd_pgb2)
return 0;
if (attr == &dev_attr_vpd_pgb7 && !sdev->vpd_pgb7)
return 0;
return S_IRUGO;
}
......@@ -1347,6 +1356,7 @@ static struct bin_attribute *scsi_sdev_bin_attrs[] = {
&dev_attr_vpd_pgb0,
&dev_attr_vpd_pgb1,
&dev_attr_vpd_pgb2,
&dev_attr_vpd_pgb7,
&dev_attr_inquiry,
NULL
};
......
......@@ -47,6 +47,7 @@
#include <linux/blkpg.h>
#include <linux/blk-pm.h>
#include <linux/delay.h>
#include <linux/rw_hint.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/string_helpers.h>
......@@ -1080,12 +1081,38 @@ static blk_status_t sd_setup_flush_cmnd(struct scsi_cmnd *cmd)
return BLK_STS_OK;
}
/**
* sd_group_number() - Compute the GROUP NUMBER field
* @cmd: SCSI command for which to compute the value of the six-bit GROUP NUMBER
* field.
*
* From SBC-5 r05 (https://www.t10.org/cgi-bin/ac.pl?t=f&f=sbc5r05.pdf):
* 0: no relative lifetime.
* 1: shortest relative lifetime.
* 2: second shortest relative lifetime.
* 3 - 0x3d: intermediate relative lifetimes.
* 0x3e: second longest relative lifetime.
* 0x3f: longest relative lifetime.
*/
static u8 sd_group_number(struct scsi_cmnd *cmd)
{
const struct request *rq = scsi_cmd_to_rq(cmd);
struct scsi_disk *sdkp = scsi_disk(rq->q->disk);
if (!sdkp->rscs)
return 0;
return min3((u32)rq->write_hint, (u32)sdkp->permanent_stream_count,
0x3fu);
}
static blk_status_t sd_setup_rw32_cmnd(struct scsi_cmnd *cmd, bool write,
sector_t lba, unsigned int nr_blocks,
unsigned char flags, unsigned int dld)
{
cmd->cmd_len = SD_EXT_CDB_SIZE;
cmd->cmnd[0] = VARIABLE_LENGTH_CMD;
cmd->cmnd[6] = sd_group_number(cmd);
cmd->cmnd[7] = 0x18; /* Additional CDB len */
cmd->cmnd[9] = write ? WRITE_32 : READ_32;
cmd->cmnd[10] = flags;
......@@ -1104,7 +1131,7 @@ static blk_status_t sd_setup_rw16_cmnd(struct scsi_cmnd *cmd, bool write,
cmd->cmd_len = 16;
cmd->cmnd[0] = write ? WRITE_16 : READ_16;
cmd->cmnd[1] = flags | ((dld >> 2) & 0x01);
cmd->cmnd[14] = (dld & 0x03) << 6;
cmd->cmnd[14] = ((dld & 0x03) << 6) | sd_group_number(cmd);
cmd->cmnd[15] = 0;
put_unaligned_be64(lba, &cmd->cmnd[2]);
put_unaligned_be32(nr_blocks, &cmd->cmnd[10]);
......@@ -1119,7 +1146,7 @@ static blk_status_t sd_setup_rw10_cmnd(struct scsi_cmnd *cmd, bool write,
cmd->cmd_len = 10;
cmd->cmnd[0] = write ? WRITE_10 : READ_10;
cmd->cmnd[1] = flags;
cmd->cmnd[6] = 0;
cmd->cmnd[6] = sd_group_number(cmd);
cmd->cmnd[9] = 0;
put_unaligned_be32(lba, &cmd->cmnd[2]);
put_unaligned_be16(nr_blocks, &cmd->cmnd[7]);
......@@ -1256,7 +1283,7 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
ret = sd_setup_rw16_cmnd(cmd, write, lba, nr_blocks,
protect | fua, dld);
} else if ((nr_blocks > 0xff) || (lba > 0x1fffff) ||
sdp->use_10_for_rw || protect) {
sdp->use_10_for_rw || protect || rq->write_hint) {
ret = sd_setup_rw10_cmnd(cmd, write, lba, nr_blocks,
protect | fua);
} else {
......@@ -3059,6 +3086,70 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
sdkp->DPOFUA = 0;
}
static bool sd_is_perm_stream(struct scsi_disk *sdkp, unsigned int stream_id)
{
u8 cdb[16] = { SERVICE_ACTION_IN_16, SAI_GET_STREAM_STATUS };
struct {
struct scsi_stream_status_header h;
struct scsi_stream_status s;
} buf;
struct scsi_device *sdev = sdkp->device;
struct scsi_sense_hdr sshdr;
const struct scsi_exec_args exec_args = {
.sshdr = &sshdr,
};
int res;
put_unaligned_be16(stream_id, &cdb[4]);
put_unaligned_be32(sizeof(buf), &cdb[10]);
res = scsi_execute_cmd(sdev, cdb, REQ_OP_DRV_IN, &buf, sizeof(buf),
SD_TIMEOUT, sdkp->max_retries, &exec_args);
if (res < 0)
return false;
if (scsi_status_is_check_condition(res) && scsi_sense_valid(&sshdr))
sd_print_sense_hdr(sdkp, &sshdr);
if (res)
return false;
if (get_unaligned_be32(&buf.h.len) < sizeof(struct scsi_stream_status))
return false;
return buf.h.stream_status[0].perm;
}
static void sd_read_io_hints(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdp = sdkp->device;
const struct scsi_io_group_descriptor *desc, *start, *end;
struct scsi_sense_hdr sshdr;
struct scsi_mode_data data;
int res;
res = scsi_mode_sense(sdp, /*dbd=*/0x8, /*modepage=*/0x0a,
/*subpage=*/0x05, buffer, SD_BUF_SIZE, SD_TIMEOUT,
sdkp->max_retries, &data, &sshdr);
if (res < 0)
return;
start = (void *)buffer + data.header_length + 16;
end = (void *)buffer + ALIGN_DOWN(data.header_length + data.length,
sizeof(*end));
/*
* From "SBC-5 Constrained Streams with Data Lifetimes": Device severs
* should assign the lowest numbered stream identifiers to permanent
* streams.
*/
for (desc = start; desc < end; desc++)
if (!desc->st_enble || !sd_is_perm_stream(sdkp, desc - start))
break;
sdkp->permanent_stream_count = desc - start;
if (sdkp->rscs && sdkp->permanent_stream_count < 2)
sd_printk(KERN_INFO, sdkp,
"Unexpected: RSCS has been set and the permanent stream count is %u\n",
sdkp->permanent_stream_count);
else if (sdkp->permanent_stream_count)
sd_printk(KERN_INFO, sdkp, "permanent stream count = %d\n",
sdkp->permanent_stream_count);
}
/*
* The ATO bit indicates whether the DIF application tag is available
* for use by the operating system.
......@@ -3166,6 +3257,18 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
rcu_read_unlock();
}
/* Parse the Block Limits Extension VPD page (0xb7) */
static void sd_read_block_limits_ext(struct scsi_disk *sdkp)
{
struct scsi_vpd *vpd;
rcu_read_lock();
vpd = rcu_dereference(sdkp->device->vpd_pgb7);
if (vpd && vpd->len >= 2)
sdkp->rscs = vpd->data[5] & 1;
rcu_read_unlock();
}
/**
* sd_read_block_characteristics - Query block dev. characteristics
* @sdkp: disk to query
......@@ -3541,6 +3644,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
if (scsi_device_supports_vpd(sdp)) {
sd_read_block_provisioning(sdkp);
sd_read_block_limits(sdkp);
sd_read_block_limits_ext(sdkp);
sd_read_block_characteristics(sdkp);
sd_zbc_read_zones(sdkp, buffer);
sd_read_cpr(sdkp);
......@@ -3550,6 +3654,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
sd_read_write_protect_flag(sdkp, buffer);
sd_read_cache_type(sdkp, buffer);
sd_read_io_hints(sdkp, buffer);
sd_read_app_tag_own(sdkp, buffer);
sd_read_write_same(sdkp, buffer);
sd_read_security(sdkp, buffer);
......
......@@ -125,6 +125,8 @@ struct scsi_disk {
unsigned int physical_block_size;
unsigned int max_medium_access_timeouts;
unsigned int medium_access_timed_out;
/* number of permanent streams */
u16 permanent_stream_count;
u8 media_present;
u8 write_prot;
u8 protection_type;/* Data Integrity Field */
......@@ -151,6 +153,7 @@ struct scsi_disk {
unsigned urswrz : 1;
unsigned security : 1;
unsigned ignore_medium_access_errors : 1;
unsigned rscs : 1; /* reduced stream control support */
};
#define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev)
......
......@@ -149,6 +149,7 @@ struct scsi_device {
struct scsi_vpd __rcu *vpd_pgb0;
struct scsi_vpd __rcu *vpd_pgb1;
struct scsi_vpd __rcu *vpd_pgb2;
struct scsi_vpd __rcu *vpd_pgb7;
struct scsi_target *sdev_target;
......
......@@ -10,6 +10,7 @@
#ifndef _SCSI_PROTO_H_
#define _SCSI_PROTO_H_
#include <linux/build_bug.h>
#include <linux/types.h>
/*
......@@ -126,6 +127,7 @@
#define SAI_READ_CAPACITY_16 0x10
#define SAI_GET_LBA_STATUS 0x12
#define SAI_REPORT_REFERRALS 0x13
#define SAI_GET_STREAM_STATUS 0x16
/* values for maintenance in */
#define MI_REPORT_IDENTIFYING_INFORMATION 0x05
#define MI_REPORT_TARGET_PGS 0x0a
......@@ -275,6 +277,82 @@ struct scsi_lun {
__u8 scsi_lun[8];
};
/* SBC-5 IO advice hints group descriptor */
struct scsi_io_group_descriptor {
#if defined(__BIG_ENDIAN)
u8 io_advice_hints_mode: 2;
u8 reserved1: 3;
u8 st_enble: 1;
u8 cs_enble: 1;
u8 ic_enable: 1;
#elif defined(__LITTLE_ENDIAN)
u8 ic_enable: 1;
u8 cs_enble: 1;
u8 st_enble: 1;
u8 reserved1: 3;
u8 io_advice_hints_mode: 2;
#else
#error
#endif
u8 reserved2[3];
/* Logical block markup descriptor */
#if defined(__BIG_ENDIAN)
u8 acdlu: 1;
u8 reserved3: 1;
u8 rlbsr: 2;
u8 lbm_descriptor_type: 4;
#elif defined(__LITTLE_ENDIAN)
u8 lbm_descriptor_type: 4;
u8 rlbsr: 2;
u8 reserved3: 1;
u8 acdlu: 1;
#else
#error
#endif
u8 params[2];
u8 reserved4;
u8 reserved5[8];
};
static_assert(sizeof(struct scsi_io_group_descriptor) == 16);
/* SCSI stream status descriptor */
struct scsi_stream_status {
#if defined(__BIG_ENDIAN)
u8 perm: 1;
u8 reserved1: 7;
#elif defined(__LITTLE_ENDIAN)
u8 reserved1: 7;
u8 perm: 1;
#else
#error
#endif
u8 reserved2;
__be16 stream_identifier;
#if defined(__BIG_ENDIAN)
u8 reserved3: 2;
u8 rel_lifetime: 6;
#elif defined(__LITTLE_ENDIAN)
u8 rel_lifetime: 6;
u8 reserved3: 2;
#else
#error
#endif
u8 reserved4[3];
};
static_assert(sizeof(struct scsi_stream_status) == 8);
/* GET STREAM STATUS parameter data */
struct scsi_stream_status_header {
__be32 len; /* length in bytes of stream_status[] array. */
u16 reserved;
__be16 number_of_open_streams;
DECLARE_FLEX_ARRAY(struct scsi_stream_status, stream_status);
};
static_assert(sizeof(struct scsi_stream_status_header) == 8);
/* SPC asymmetric access states */
#define SCSI_ACCESS_STATE_OPTIMAL 0x00
#define SCSI_ACCESS_STATE_ACTIVE 0x01
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment