Skip to content

Commit bf4ae8f

Browse files
johnpgarryaxboe
authored andcommitted
scsi: sd: Atomic write support
Support is divided into two main areas: - reading VPD pages and setting sdev request_queue limits - support WRITE ATOMIC (16) command and tracing The relevant block limits VPD page need to be read to allow the block layer request_queue atomic write limits to be set. These VPD page limits are described in sbc4r22 section 6.6.4 - Block limits VPD page. There are five limits of interest: - MAXIMUM ATOMIC TRANSFER LENGTH - ATOMIC ALIGNMENT - ATOMIC TRANSFER LENGTH GRANULARITY - MAXIMUM ATOMIC TRANSFER LENGTH WITH BOUNDARY - MAXIMUM ATOMIC BOUNDARY SIZE MAXIMUM ATOMIC TRANSFER LENGTH is the maximum length for a WRITE ATOMIC (16) command. It will not be greater than the device MAXIMUM TRANSFER LENGTH. ATOMIC ALIGNMENT and ATOMIC TRANSFER LENGTH GRANULARITY are the minimum alignment and length values for an atomic write in terms of logical blocks. Unlike NVMe, SCSI does not specify an LBA space boundary, but does specify a per-IO boundary granularity. The maximum boundary size is specified in MAXIMUM ATOMIC BOUNDARY SIZE. When used, this boundary value is set in the WRITE ATOMIC (16) ATOMIC BOUNDARY field - layout for the WRITE_ATOMIC_16 command can be found in sbc4r22 section 5.48. This boundary value is the granularity size at which the device may atomically write the data. A value of zero in WRITE ATOMIC (16) ATOMIC BOUNDARY field means that all data must be atomically written together. MAXIMUM ATOMIC TRANSFER LENGTH WITH BOUNDARY is the maximum atomic write length if a non-zero boundary value is set. For atomic write support, the WRITE ATOMIC (16) boundary is not of much interest, as the block layer expects each request submitted to be executed atomically. However, the SCSI spec does leave itself open to a quirky scenario where MAXIMUM ATOMIC TRANSFER LENGTH is zero, yet MAXIMUM ATOMIC TRANSFER LENGTH WITH BOUNDARY and MAXIMUM ATOMIC BOUNDARY SIZE are both non-zero. This case will be supported. To set the block layer request_queue atomic write capabilities, sanitize the VPD page limits and set limits as follows: - atomic_write_unit_min is derived from granularity and alignment values. If no granularity value is not set, use physical block size - atomic_write_unit_max is derived from MAXIMUM ATOMIC TRANSFER LENGTH. In the scenario where MAXIMUM ATOMIC TRANSFER LENGTH is zero and boundary limits are non-zero, use MAXIMUM ATOMIC BOUNDARY SIZE for atomic_write_unit_max. New flag scsi_disk.use_atomic_write_boundary is set for this scenario. - atomic_write_boundary_bytes is set to zero always SCSI also supports a WRITE ATOMIC (32) command, which is for type 2 protection enabled. This is not going to be supported now, so check for T10_PI_TYPE2_PROTECTION when setting any request_queue limits. To handle an atomic write request, add support for WRITE ATOMIC (16) command in handler sd_setup_atomic_cmnd(). Flag use_atomic_write_boundary is checked here for encoding ATOMIC BOUNDARY field. Trace info is also added for WRITE_ATOMIC_16 command. Reviewed-by: Martin K. Petersen <[email protected]> Signed-off-by: John Garry <[email protected]> Acked-by: Darrick J. Wong <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent caf336f commit bf4ae8f

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed

drivers/scsi/scsi_trace.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,26 @@ scsi_trace_zbc_out(struct trace_seq *p, unsigned char *cdb, int len)
325325
return ret;
326326
}
327327

328+
static const char *
329+
scsi_trace_atomic_write16_out(struct trace_seq *p, unsigned char *cdb, int len)
330+
{
331+
const char *ret = trace_seq_buffer_ptr(p);
332+
unsigned int boundary_size;
333+
unsigned int nr_blocks;
334+
sector_t lba;
335+
336+
lba = get_unaligned_be64(&cdb[2]);
337+
boundary_size = get_unaligned_be16(&cdb[10]);
338+
nr_blocks = get_unaligned_be16(&cdb[12]);
339+
340+
trace_seq_printf(p, "lba=%llu txlen=%u boundary_size=%u",
341+
lba, nr_blocks, boundary_size);
342+
343+
trace_seq_putc(p, 0);
344+
345+
return ret;
346+
}
347+
328348
static const char *
329349
scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len)
330350
{
@@ -385,6 +405,8 @@ scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len)
385405
return scsi_trace_zbc_in(p, cdb, len);
386406
case ZBC_OUT:
387407
return scsi_trace_zbc_out(p, cdb, len);
408+
case WRITE_ATOMIC_16:
409+
return scsi_trace_atomic_write16_out(p, cdb, len);
388410
default:
389411
return scsi_trace_misc(p, cdb, len);
390412
}

drivers/scsi/sd.c

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,64 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
939939
return scsi_alloc_sgtables(cmd);
940940
}
941941

942+
static void sd_config_atomic(struct scsi_disk *sdkp, struct queue_limits *lim)
943+
{
944+
unsigned int logical_block_size = sdkp->device->sector_size,
945+
physical_block_size_sectors, max_atomic, unit_min, unit_max;
946+
947+
if ((!sdkp->max_atomic && !sdkp->max_atomic_with_boundary) ||
948+
sdkp->protection_type == T10_PI_TYPE2_PROTECTION)
949+
return;
950+
951+
physical_block_size_sectors = sdkp->physical_block_size /
952+
sdkp->device->sector_size;
953+
954+
unit_min = rounddown_pow_of_two(sdkp->atomic_granularity ?
955+
sdkp->atomic_granularity :
956+
physical_block_size_sectors);
957+
958+
/*
959+
* Only use atomic boundary when we have the odd scenario of
960+
* sdkp->max_atomic == 0, which the spec does permit.
961+
*/
962+
if (sdkp->max_atomic) {
963+
max_atomic = sdkp->max_atomic;
964+
unit_max = rounddown_pow_of_two(sdkp->max_atomic);
965+
sdkp->use_atomic_write_boundary = 0;
966+
} else {
967+
max_atomic = sdkp->max_atomic_with_boundary;
968+
unit_max = rounddown_pow_of_two(sdkp->max_atomic_boundary);
969+
sdkp->use_atomic_write_boundary = 1;
970+
}
971+
972+
/*
973+
* Ensure compliance with granularity and alignment. For now, keep it
974+
* simple and just don't support atomic writes for values mismatched
975+
* with max_{boundary}atomic, physical block size, and
976+
* atomic_granularity itself.
977+
*
978+
* We're really being distrustful by checking unit_max also...
979+
*/
980+
if (sdkp->atomic_granularity > 1) {
981+
if (unit_min > 1 && unit_min % sdkp->atomic_granularity)
982+
return;
983+
if (unit_max > 1 && unit_max % sdkp->atomic_granularity)
984+
return;
985+
}
986+
987+
if (sdkp->atomic_alignment > 1) {
988+
if (unit_min > 1 && unit_min % sdkp->atomic_alignment)
989+
return;
990+
if (unit_max > 1 && unit_max % sdkp->atomic_alignment)
991+
return;
992+
}
993+
994+
lim->atomic_write_hw_max = max_atomic * logical_block_size;
995+
lim->atomic_write_hw_boundary = 0;
996+
lim->atomic_write_hw_unit_min = unit_min * logical_block_size;
997+
lim->atomic_write_hw_unit_max = unit_max * logical_block_size;
998+
}
999+
9421000
static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
9431001
bool unmap)
9441002
{
@@ -1237,6 +1295,26 @@ static int sd_cdl_dld(struct scsi_disk *sdkp, struct scsi_cmnd *scmd)
12371295
return (hint - IOPRIO_HINT_DEV_DURATION_LIMIT_1) + 1;
12381296
}
12391297

1298+
static blk_status_t sd_setup_atomic_cmnd(struct scsi_cmnd *cmd,
1299+
sector_t lba, unsigned int nr_blocks,
1300+
bool boundary, unsigned char flags)
1301+
{
1302+
cmd->cmd_len = 16;
1303+
cmd->cmnd[0] = WRITE_ATOMIC_16;
1304+
cmd->cmnd[1] = flags;
1305+
put_unaligned_be64(lba, &cmd->cmnd[2]);
1306+
put_unaligned_be16(nr_blocks, &cmd->cmnd[12]);
1307+
if (boundary)
1308+
put_unaligned_be16(nr_blocks, &cmd->cmnd[10]);
1309+
else
1310+
put_unaligned_be16(0, &cmd->cmnd[10]);
1311+
put_unaligned_be16(nr_blocks, &cmd->cmnd[12]);
1312+
cmd->cmnd[14] = 0;
1313+
cmd->cmnd[15] = 0;
1314+
1315+
return BLK_STS_OK;
1316+
}
1317+
12401318
static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
12411319
{
12421320
struct request *rq = scsi_cmd_to_rq(cmd);
@@ -1302,6 +1380,10 @@ static blk_status_t sd_setup_read_write_cmnd(struct scsi_cmnd *cmd)
13021380
if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) {
13031381
ret = sd_setup_rw32_cmnd(cmd, write, lba, nr_blocks,
13041382
protect | fua, dld);
1383+
} else if (rq->cmd_flags & REQ_ATOMIC && write) {
1384+
ret = sd_setup_atomic_cmnd(cmd, lba, nr_blocks,
1385+
sdkp->use_atomic_write_boundary,
1386+
protect | fua);
13051387
} else if (sdp->use_16_for_rw || (nr_blocks > 0xffff)) {
13061388
ret = sd_setup_rw16_cmnd(cmd, write, lba, nr_blocks,
13071389
protect | fua, dld);
@@ -3264,7 +3346,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp,
32643346
sdkp->max_ws_blocks = (u32)get_unaligned_be64(&vpd->data[36]);
32653347

32663348
if (!sdkp->lbpme)
3267-
goto out;
3349+
goto config_atomic;
32683350

32693351
lba_count = get_unaligned_be32(&vpd->data[20]);
32703352
desc_count = get_unaligned_be32(&vpd->data[24]);
@@ -3279,6 +3361,15 @@ static void sd_read_block_limits(struct scsi_disk *sdkp,
32793361
get_unaligned_be32(&vpd->data[32]) & ~(1 << 31);
32803362

32813363
sd_config_discard(sdkp, lim, sd_discard_mode(sdkp));
3364+
3365+
config_atomic:
3366+
sdkp->max_atomic = get_unaligned_be32(&vpd->data[44]);
3367+
sdkp->atomic_alignment = get_unaligned_be32(&vpd->data[48]);
3368+
sdkp->atomic_granularity = get_unaligned_be32(&vpd->data[52]);
3369+
sdkp->max_atomic_with_boundary = get_unaligned_be32(&vpd->data[56]);
3370+
sdkp->max_atomic_boundary = get_unaligned_be32(&vpd->data[60]);
3371+
3372+
sd_config_atomic(sdkp, lim);
32823373
}
32833374

32843375
out:

drivers/scsi/sd.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ struct scsi_disk {
115115
u32 max_unmap_blocks;
116116
u32 unmap_granularity;
117117
u32 unmap_alignment;
118+
119+
u32 max_atomic;
120+
u32 atomic_alignment;
121+
u32 atomic_granularity;
122+
u32 max_atomic_with_boundary;
123+
u32 max_atomic_boundary;
124+
118125
u32 index;
119126
unsigned int physical_block_size;
120127
unsigned int max_medium_access_timeouts;
@@ -148,6 +155,7 @@ struct scsi_disk {
148155
unsigned security : 1;
149156
unsigned ignore_medium_access_errors : 1;
150157
unsigned rscs : 1; /* reduced stream control support */
158+
unsigned use_atomic_write_boundary : 1;
151159
};
152160
#define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev)
153161

include/scsi/scsi_proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
#define WRITE_SAME_16 0x93
121121
#define ZBC_OUT 0x94
122122
#define ZBC_IN 0x95
123+
#define WRITE_ATOMIC_16 0x9c
123124
#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
124125
#define SERVICE_ACTION_IN_16 0x9e
125126
#define SERVICE_ACTION_OUT_16 0x9f

include/trace/events/scsi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
scsi_opcode_name(WRITE_32), \
103103
scsi_opcode_name(WRITE_SAME_32), \
104104
scsi_opcode_name(ATA_16), \
105+
scsi_opcode_name(WRITE_ATOMIC_16), \
105106
scsi_opcode_name(ATA_12))
106107

107108
#define scsi_hostbyte_name(result) { result, #result }

0 commit comments

Comments
 (0)