Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion HOWTO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,16 @@ with the caveat that when used on the command line, they must come after the
Specifies the type of uring passthrough command to be used. Supported
values are nvme and bsg. Default is nvme.

.. option:: cdb_len=int : [io_uring_cmd]

SCSI CDB length used for bsg :option:`cmd_type`. Supported values are
0, 10, 16, and 32. Default is 0 (auto): the smallest CDB whose LBA and
transfer-length fields fit the request is chosen, escalating from
READ(10)/WRITE(10) to READ(16)/WRITE(16) as needed, mirroring the sg
engine. Ignored when cmd_type=nvme. When an explicit length is set, a
request whose LBA or transfer length exceeds the field capacity of the
chosen CDB fails with -EINVAL rather than falling back to a larger CDB.

.. option:: hipri

[io_uring] [io_uring_cmd] [xnvme]
Expand Down Expand Up @@ -3068,7 +3078,11 @@ with the caveat that when used on the command line, they must come after the
Use Write Zeroes commands for write operations

**verify**
Use Verify commands for write operations
Use Verify commands for write operations. Supported for
both nvme and bsg cmd_type. For bsg, the SCSI VERIFY(10)/
(16)/(32) opcode is selected by :option:`cdb_len` and the
data comparison behavior by :option:`verify_bytchk`;
:option:`writefua` is not supported in this mode.

**zone_append**
Use zone append commands for write operations. Requires zonemode=zbd
Expand All @@ -3079,6 +3093,40 @@ with the caveat that when used on the command line, they must come after the
entries with no percentage specified.
Example: ``write/60:zeroes/30:uncor/10`` or ``write/50:zeroes/:uncor/``

.. option:: verify_bytchk=int : [io_uring_cmd]

BYTCHK field for the SCSI VERIFY command issued when
:option:`write_mode` is ``verify`` and :option:`cmd_type` is ``bsg``.
Only valid with write_mode=verify. Defaults to 0.

**0**
Medium verification only; no data is transferred to
the device.

**1**
The device compares the full transfer against the data
stored on the medium, byte by byte.

**3**
The device compares a single block against every block
in the range. Only one block is transferred.

.. option:: read_mode=str : [io_uring_cmd]

Specifies the type of read operation. Only supported with
cmd_type=bsg. Defaults to 'read'.

**read**
Use Read commands for read operations

**prefetch**
Use SCSI Pre-Fetch commands, which pull the requested
blocks from the medium into the device read cache
without transferring data to the host. Useful for
cache-warming and prefetch-overhead benchmarks. The
Pre-Fetch(10)/(16) opcode is selected by :option:`cdb_len`.
:option:`readfua` is not supported in this mode.

.. option:: verify_mode=str : [io_uring_cmd]

Specifies the type of command to be used in the verification phase. Defaults to 'read'.
Expand Down
200 changes: 177 additions & 23 deletions engines/bsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,41 +84,90 @@ int fio_bsg_uring_cmd_get_file_size(struct thread_data *td, struct fio_file *f)
}

void fio_bsg_uring_cmd_init(struct bsg_uring_cmd *cmd, struct bsg_cmd *bc,
struct io_u *io_u, int dxfer_dir)
struct io_u *io_u, int dxfer_dir,
unsigned int cdb_len)
{
memset(cmd, 0, sizeof(*cmd));
memset(bc->cdb, 0, sizeof(bc->cdb));

cmd->request = (uint64_t)(uintptr_t) bc->cdb;
cmd->request_len = sizeof(bc->cdb);
cmd->request_len = cdb_len;
cmd->response = (uint64_t)(uintptr_t) bc->sb;
cmd->max_response_len = sizeof(bc->sb);

if (dxfer_dir == SG_DXFER_TO_DEV) {
cmd->dout_xferp = (uint64_t)(uintptr_t) io_u->xfer_buf;
cmd->dout_xfer_len = io_u->xfer_buflen;
} else {
} else if (dxfer_dir == SG_DXFER_FROM_DEV) {
cmd->din_xferp = (uint64_t)(uintptr_t) io_u->xfer_buf;
cmd->din_xfer_len = io_u->xfer_buflen;
}
}

static int fio_bsg_uring_cmd_rw_lba(struct bsg_cmd *bc, unsigned long long lba,
unsigned long long nlb)
unsigned long long nlb,
unsigned int cdb_len)
{
if (lba > MAX_10CDB_LBA || nlb > MAX_10CDB_NLB) {
log_err("offset or nlb is larger than the "
"maximum value of a field within CDB (10)\n");
switch (cdb_len) {
case 10:
if (lba > MAX_10CDB_LBA || nlb > MAX_10CDB_NLB) {
log_err("offset or nlb is larger than the "
"maximum value of a field within CDB (10)\n");
return -EINVAL;
}
sgio_set_be32((uint32_t) lba, &bc->cdb[2]);
sgio_set_be16((uint16_t) nlb, &bc->cdb[7]);
return 0;
case 16:
if (lba > MAX_16CDB_LBA || nlb > MAX_16CDB_NLB) {
log_err("offset or nlb is larger than the "
"maximum value of a field within CDB (16)\n");
return -EINVAL;
}
sgio_set_be64((uint64_t) lba, &bc->cdb[2]);
sgio_set_be32((uint32_t) nlb, &bc->cdb[10]);
return 0;
case 32:
if (lba > MAX_32CDB_LBA || nlb > MAX_32CDB_NLB) {
log_err("offset or nlb is larger than the "
"maximum value of a field within CDB (32)\n");
return -EINVAL;
}
sgio_set_be64((uint64_t) lba, &bc->cdb[12]);
sgio_set_be32((uint32_t) nlb, &bc->cdb[28]);
return 0;
default:
log_err("unsupported CDB length: %u\n", cdb_len);
return -EINVAL;
}
sgio_set_be32((uint32_t) lba, &bc->cdb[2]);
sgio_set_be16((uint16_t) nlb, &bc->cdb[7]);
}

return 0;
static void fio_bsg_varlen_cdb_header(struct bsg_cmd *bc, uint16_t sa)
{
bc->cdb[0] = bsg_cmd_varlen;
sgio_set_be16(sa, &bc->cdb[8]);
bc->cdb[7] = BSG_VARLEN_CDB_ADDITIONAL_LEN;
}

static void fio_bsg_set_verify_bytchk(struct bsg_cmd *bc, unsigned int cdb_len,
unsigned int verify_bytchk)
{
/*
* BYTCHK occupies bits 2-1 of byte 1 for VERIFY(10)/(16) and byte 10
* for the 32-byte variable-length VERIFY. BYTCHK=0 leaves the field
* cleared (medium verification only).
*/
if (cdb_len == 32)
bc->cdb[10] |= verify_bytchk << 1;
else
bc->cdb[1] |= verify_bytchk << 1;
}

int fio_bsg_uring_cmd_prep(struct bsg_uring_cmd *cmd, struct io_u *io_u,
struct bsg_cmd *bc, bool fua)
struct bsg_cmd *bc, bool fua, unsigned int cdb_len,
enum bsg_write_mode wmode,
unsigned int verify_bytchk,
enum bsg_read_mode rmode)
{
struct bsg_data *data = FILE_ENG_DATA(io_u->file);
unsigned long long offset, nlb;
Expand All @@ -132,21 +181,115 @@ int fio_bsg_uring_cmd_prep(struct bsg_uring_cmd *cmd, struct io_u *io_u,
offset = io_u->offset / data->bs;
nlb = io_u->xfer_buflen / data->bs;

/*
* cdb_len == 0 selects auto-escalation: pick the smallest CDB whose
* LBA and transfer-length fields can hold this request, mirroring the
* sg engine. READ(10)/WRITE(10) cover a 32-bit LBA and 16-bit length;
* anything larger uses READ(16)/WRITE(16). The 32-byte CDB shares the
* same 64-bit LBA / 32-bit length field widths as the 16-byte CDB, so
* it is never reached by auto-escalation and remains opt-in only.
*/
if (cdb_len == 0) {
if (offset > MAX_10CDB_LBA || nlb > MAX_10CDB_NLB)
cdb_len = 16;
else
cdb_len = 10;
}

switch (io_u->ddir) {
case DDIR_READ:
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_FROM_DEV);
bc->cdb[0] = bsg_cmd_read_10;
if (fua)
bc->cdb[1] |= 1 << 3;
if (rmode == BSG_READ_MODE_PREFETCH) {
/*
* PREFETCH pulls the requested blocks from the medium
* into the device read cache. No host data transfer.
*/
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_NONE,
cdb_len);
switch (cdb_len) {
case 10:
bc->cdb[0] = bsg_cmd_prefetch_10;
break;
case 16:
bc->cdb[0] = bsg_cmd_prefetch_16;
break;
}
break;
}
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_FROM_DEV, cdb_len);
switch (cdb_len) {
case 10:
bc->cdb[0] = bsg_cmd_read_10;
if (fua)
bc->cdb[1] |= 1 << 3;
break;
case 16:
bc->cdb[0] = bsg_cmd_read_16;
if (fua)
bc->cdb[1] |= 1 << 3;
break;
case 32:
fio_bsg_varlen_cdb_header(bc, BSG_SA_READ_32);
if (fua)
bc->cdb[10] |= 1 << 3;
break;
}
break;
case DDIR_WRITE:
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_TO_DEV);
bc->cdb[0] = bsg_cmd_write_10;
if (fua)
bc->cdb[1] |= 1 << 3;
if (wmode == BSG_WRITE_MODE_VERIFY) {
/*
* VERIFY data direction depends on BYTCHK:
* 0 medium verification only, no host data transfer
* 1 compare the whole transfer against the medium
* 3 compare a single block against the whole range
* BYTCHK 1 and 3 send data to the device.
*/
int dxfer = verify_bytchk ? SG_DXFER_TO_DEV :
SG_DXFER_NONE;

fio_bsg_uring_cmd_init(cmd, bc, io_u, dxfer, cdb_len);
switch (cdb_len) {
case 10:
bc->cdb[0] = bsg_cmd_verify_10;
break;
case 16:
bc->cdb[0] = bsg_cmd_verify_16;
break;
case 32:
fio_bsg_varlen_cdb_header(bc, BSG_SA_VERIFY_32);
break;
}
fio_bsg_set_verify_bytchk(bc, cdb_len, verify_bytchk);
/*
* BYTCHK=3 compares one block against the entire
* range, so only a single block is transferred while
* the CDB still carries the full block count.
*/
if (verify_bytchk == 3)
cmd->dout_xfer_len = data->bs;
break;
}
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_TO_DEV, cdb_len);
switch (cdb_len) {
case 10:
bc->cdb[0] = bsg_cmd_write_10;
if (fua)
bc->cdb[1] |= 1 << 3;
break;
case 16:
bc->cdb[0] = bsg_cmd_write_16;
if (fua)
bc->cdb[1] |= 1 << 3;
break;
case 32:
fio_bsg_varlen_cdb_header(bc, BSG_SA_WRITE_32);
if (fua)
bc->cdb[10] |= 1 << 3;
break;
}
break;
case DDIR_TRIM:
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_TO_DEV);
/* UNMAP CDB has fixed 10-byte length regardless of cdb_len */
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_TO_DEV, 10);
bc->cdb[0] = bsg_cmd_unmap;
data_len = sizeof(bc->unmap_param) / sizeof(bc->unmap_param[0]);
sgio_set_be16((uint16_t) data_len, &bc->cdb[7]);
Expand All @@ -158,12 +301,23 @@ int fio_bsg_uring_cmd_prep(struct bsg_uring_cmd *cmd, struct io_u *io_u,
cmd->dout_xfer_len = data_len;
return 0;
case DDIR_SYNC:
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_NONE);
bc->cdb[0] = bsg_cmd_sync_cache_10;
/*
* SYNCHRONIZE CACHE has 10-byte (0x35) and 16-byte (0x91)
* CDB only; no 32-byte service action is defined by
* SBC. When cdb_len=32 is requested, fall back to the
* 16-byte CDB.
*/
if (cdb_len == 10) {
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_NONE, 10);
bc->cdb[0] = bsg_cmd_sync_cache_10;
} else {
fio_bsg_uring_cmd_init(cmd, bc, io_u, SG_DXFER_NONE, 16);
bc->cdb[0] = bsg_cmd_sync_cache_16;
}
return 0;
default:
return -ENOTSUP;
}

return fio_bsg_uring_cmd_rw_lba(bc, offset, nlb);
return fio_bsg_uring_cmd_rw_lba(bc, offset, nlb, cdb_len);
}
40 changes: 38 additions & 2 deletions engines/bsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,52 @@ struct bsg_uring_cmd {
#endif /* CONFIG_BSG_URING_CMD */

#define MAX_SB 64
#define MAX_CDB_LEN 32

#define MAX_10CDB_LBA 0xFFFFFFFFULL
#define MAX_10CDB_NLB 0xFFFFU
#define MAX_16CDB_LBA 0xFFFFFFFFFFFFFFFFULL
#define MAX_16CDB_NLB 0xFFFFFFFFU
#define MAX_32CDB_LBA MAX_16CDB_LBA
#define MAX_32CDB_NLB MAX_16CDB_NLB

/* Variable-length CDB (opcode 0x7F) service actions for 32-byte CDBs */
#define BSG_VARLEN_CDB_OPCODE 0x7F
#define BSG_VARLEN_CDB_ADDITIONAL_LEN 0x18 /* 24 -> total 32 bytes */
#define BSG_SA_READ_32 0x0009
#define BSG_SA_WRITE_32 0x000B
#define BSG_SA_VERIFY_32 0x000A

enum bsg_io_opcode {
bsg_cmd_read_10 = 0x28,
bsg_cmd_read_16 = 0x88,
bsg_cmd_read_capacity_10 = 0x25,
bsg_cmd_sync_cache_10 = 0x35,
bsg_cmd_sync_cache_16 = 0x91,
bsg_cmd_unmap = 0x42,
bsg_cmd_write_10 = 0x2A,
bsg_cmd_write_16 = 0x8A,
bsg_cmd_verify_10 = 0x2F,
bsg_cmd_verify_16 = 0x8F,
bsg_cmd_prefetch_10 = 0x34,
bsg_cmd_prefetch_16 = 0x90,
bsg_cmd_varlen = BSG_VARLEN_CDB_OPCODE,
};

/* Which command mode to issue for DDIR_WRITE ddir. */
enum bsg_write_mode {
BSG_WRITE_MODE_WRITE = 0,
BSG_WRITE_MODE_VERIFY,
};

/* Which command mode to issue for DDIR_READ ddir. */
enum bsg_read_mode {
BSG_READ_MODE_READ = 0,
BSG_READ_MODE_PREFETCH,
};

struct bsg_cmd {
unsigned char cdb[16];
unsigned char cdb[MAX_CDB_LEN];
unsigned char sb[MAX_SB];
uint8_t unmap_param[24];
};
Expand All @@ -62,6 +95,9 @@ int fio_bsg_uring_cmd_read_capacity(struct thread_data *td, unsigned int *bs,
int fio_bsg_uring_cmd_get_file_size(struct thread_data *td, struct fio_file *f);

int fio_bsg_uring_cmd_prep(struct bsg_uring_cmd *cmd, struct io_u *io_u,
struct bsg_cmd *bc, bool fua);
struct bsg_cmd *bc, bool fua, unsigned int cdb_len,
enum bsg_write_mode wmode,
unsigned int verify_bytchk,
enum bsg_read_mode rmode);

#endif /* FIO_BSG_H */
Loading
Loading