From 3434be392051a2fdb295df3cfe07bf75235250a0 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:14 +0000 Subject: scsi: target: Rename target_configure_unmap_from_queue() Rename target_configure_unmap_from_queue() to target_configure_unmap_from_bdev() since it now takes a bdev. Signed-off-by: Mike Christie Signed-off-by: John Garry Reviewed-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-2-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_device.c | 6 +++--- drivers/target/target_core_file.c | 4 ++-- drivers/target/target_core_iblock.c | 4 ++-- include/target/target_core_backend.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 7bb711b24c0d..83fe3d9a9681 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -844,8 +844,8 @@ free_device: * Check if the underlying struct block_device supports discard and if yes * configure the UNMAP parameters. */ -bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, - struct block_device *bdev) +bool target_configure_unmap_from_bdev(struct se_dev_attrib *attrib, + struct block_device *bdev) { int block_size = bdev_logical_block_size(bdev); @@ -863,7 +863,7 @@ bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, bdev_discard_alignment(bdev) / block_size; return true; } -EXPORT_SYMBOL(target_configure_unmap_from_queue); +EXPORT_SYMBOL(target_configure_unmap_from_bdev); /* * Convert from blocksize advertised to the initiator to the 512 byte diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 2d78ef74633c..b2610073e8cc 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -92,8 +92,8 @@ static bool fd_configure_unmap(struct se_device *dev) struct inode *inode = file->f_mapping->host; if (S_ISBLK(inode->i_mode)) - return target_configure_unmap_from_queue(&dev->dev_attrib, - I_BDEV(inode)); + return target_configure_unmap_from_bdev(&dev->dev_attrib, + I_BDEV(inode)); /* Limit UNMAP emulation to 8k Number of LBAs (NoLB) */ dev->dev_attrib.max_unmap_lba_count = 0x2000; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 66c292b7d74b..281612b9830f 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -84,8 +84,8 @@ static bool iblock_configure_unmap(struct se_device *dev) { struct iblock_dev *ib_dev = IBLOCK_DEV(dev); - return target_configure_unmap_from_queue(&dev->dev_attrib, - ib_dev->ibd_bd); + return target_configure_unmap_from_bdev(&dev->dev_attrib, + ib_dev->ibd_bd); } static int iblock_configure_device(struct se_device *dev) diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 4063a701081b..d394306f8f49 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -121,8 +121,8 @@ sense_reason_t passthrough_parse_cdb(struct se_cmd *cmd, bool target_sense_desc_format(struct se_device *dev); sector_t target_to_linux_sector(struct se_device *dev, sector_t lb); -bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, - struct block_device *bdev); +bool target_configure_unmap_from_bdev(struct se_dev_attrib *attrib, + struct block_device *bdev); static inline bool target_dev_configured(struct se_device *se_dev) { -- cgit v1.2.3 From d505447b8d78f4d81a67d492ac72b8d3a1805e72 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:15 +0000 Subject: scsi: target: Add atomic se_device fields Add atomic fields to the se_device and export them in configfs. Initially only target_core_iblock will be supported and we will inherit all the settings from the block layer. Signed-off-by: Mike Christie jpg: Stop being allowed to configure atomic write alignment, Signed-off-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-3-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_configfs.c | 15 +++++++++++++++ include/target/target_core_base.h | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index b19acd662726..5470c1258445 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -578,6 +578,11 @@ DEF_CONFIGFS_ATTRIB_SHOW(unmap_zeroes_data); DEF_CONFIGFS_ATTRIB_SHOW(max_write_same_len); DEF_CONFIGFS_ATTRIB_SHOW(emulate_rsoc); DEF_CONFIGFS_ATTRIB_SHOW(submit_type); +DEF_CONFIGFS_ATTRIB_SHOW(atomic_max_len); +DEF_CONFIGFS_ATTRIB_SHOW(atomic_alignment); +DEF_CONFIGFS_ATTRIB_SHOW(atomic_granularity); +DEF_CONFIGFS_ATTRIB_SHOW(atomic_max_with_boundary); +DEF_CONFIGFS_ATTRIB_SHOW(atomic_max_boundary); #define DEF_CONFIGFS_ATTRIB_STORE_U32(_name) \ static ssize_t _name##_store(struct config_item *item, const char *page,\ @@ -1300,6 +1305,11 @@ CONFIGFS_ATTR(, max_write_same_len); CONFIGFS_ATTR(, alua_support); CONFIGFS_ATTR(, pgr_support); CONFIGFS_ATTR(, submit_type); +CONFIGFS_ATTR_RO(, atomic_max_len); +CONFIGFS_ATTR_RO(, atomic_alignment); +CONFIGFS_ATTR_RO(, atomic_granularity); +CONFIGFS_ATTR_RO(, atomic_max_with_boundary); +CONFIGFS_ATTR_RO(, atomic_max_boundary); /* * dev_attrib attributes for devices using the target core SBC/SPC @@ -1343,6 +1353,11 @@ struct configfs_attribute *sbc_attrib_attrs[] = { &attr_pgr_support, &attr_emulate_rsoc, &attr_submit_type, + &attr_atomic_alignment, + &attr_atomic_max_len, + &attr_atomic_granularity, + &attr_atomic_max_with_boundary, + &attr_atomic_max_boundary, NULL, }; EXPORT_SYMBOL(sbc_attrib_attrs); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index c4d9116904aa..70ece58d3078 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -731,6 +731,11 @@ struct se_dev_attrib { u32 unmap_granularity; u32 unmap_granularity_alignment; u32 max_write_same_len; + u32 atomic_max_len; + u32 atomic_alignment; + u32 atomic_granularity; + u32 atomic_max_with_boundary; + u32 atomic_max_boundary; u8 submit_type; struct se_device *da_dev; struct config_group da_group; -- cgit v1.2.3 From c486634fe2b10301bd8f0319c70a919433bfdf17 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:16 +0000 Subject: scsi: target: Add helper to set up atomic values from block_device Add a helper function that sets up the atomic value based on a block_device similar to what we do for unmap. Signed-off-by: Mike Christie jpg: Set atomic alignment, drop atomic_supported reference Signed-off-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-4-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_device.c | 17 +++++++++++++++++ include/target/target_core_backend.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 83fe3d9a9681..39a2d9c3eb9e 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -840,6 +840,23 @@ free_device: return NULL; } +void target_configure_write_atomic_from_bdev(struct se_dev_attrib *attrib, + struct block_device *bdev) +{ + struct request_queue *q = bdev_get_queue(bdev); + int block_size = bdev_logical_block_size(bdev); + + if (!bdev_can_atomic_write(bdev)) + return; + + attrib->atomic_max_len = queue_atomic_write_max_bytes(q) / block_size; + attrib->atomic_granularity = attrib->atomic_alignment = + queue_atomic_write_unit_min_bytes(q) / block_size; + attrib->atomic_max_with_boundary = 0; + attrib->atomic_max_boundary = 0; +} +EXPORT_SYMBOL_GPL(target_configure_write_atomic_from_bdev); + /* * Check if the underlying struct block_device supports discard and if yes * configure the UNMAP parameters. diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index d394306f8f49..e32de80854b6 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -123,6 +123,8 @@ bool target_sense_desc_format(struct se_device *dev); sector_t target_to_linux_sector(struct se_device *dev, sector_t lb); bool target_configure_unmap_from_bdev(struct se_dev_attrib *attrib, struct block_device *bdev); +void target_configure_write_atomic_from_bdev(struct se_dev_attrib *attrib, + struct block_device *bdev); static inline bool target_dev_configured(struct se_device *se_dev) { -- cgit v1.2.3 From 526145725106b490b0c2d9f200b705b17a3da6b6 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:17 +0000 Subject: scsi: target: Add WRITE_ATOMIC_16 handler Add the core LIO code to process the WRITE_ATOMIC_16 command. Signed-off-by: Mike Christie jpg: fix return code from sbc_check_atomic, reformat Signed-off-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-5-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_sbc.c | 51 +++++++++++++++++++++++++++++++++++++++ include/target/target_core_base.h | 1 + 2 files changed, 52 insertions(+) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index fe8beb7dbab1..abe91dc8722e 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -764,6 +764,49 @@ sbc_check_dpofua(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb) return 0; } +static sense_reason_t +sbc_check_atomic(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb) +{ + struct se_dev_attrib *attrib = &dev->dev_attrib; + u16 boundary, transfer_len; + u64 lba; + + lba = transport_lba_64(cdb); + boundary = get_unaligned_be16(&cdb[10]); + transfer_len = get_unaligned_be16(&cdb[12]); + + if (!attrib->atomic_max_len) + return TCM_UNSUPPORTED_SCSI_OPCODE; + + if (boundary) { + if (transfer_len > attrib->atomic_max_with_boundary) + return TCM_INVALID_CDB_FIELD; + + if (boundary > attrib->atomic_max_boundary) + return TCM_INVALID_CDB_FIELD; + } else { + if (transfer_len > attrib->atomic_max_len) + return TCM_INVALID_CDB_FIELD; + } + + if (attrib->atomic_granularity) { + if (transfer_len % attrib->atomic_granularity) + return TCM_INVALID_CDB_FIELD; + + if (boundary && boundary % attrib->atomic_granularity) + return TCM_INVALID_CDB_FIELD; + } + + if (dev->dev_attrib.atomic_alignment) { + u64 _lba = lba; + + if (do_div(_lba, dev->dev_attrib.atomic_alignment)) + return TCM_INVALID_CDB_FIELD; + } + + return 0; +} + sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct exec_cmd_ops *ops) { @@ -861,6 +904,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct exec_cmd_ops *ops) break; case WRITE_16: case WRITE_VERIFY_16: + case WRITE_ATOMIC_16: sectors = transport_get_sectors_16(cdb); cmd->t_task_lba = transport_lba_64(cdb); @@ -872,6 +916,13 @@ sbc_parse_cdb(struct se_cmd *cmd, struct exec_cmd_ops *ops) return ret; cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; + if (cdb[0] == WRITE_ATOMIC_16) { + cmd->se_cmd_flags |= SCF_ATOMIC; + + ret = sbc_check_atomic(dev, cmd, cdb); + if (ret) + return ret; + } cmd->execute_cmd = sbc_execute_rw; break; case VARIABLE_LENGTH_CMD: diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 70ece58d3078..56333b5726c8 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -158,6 +158,7 @@ enum se_cmd_flags_table { SCF_TASK_ATTR_SET = (1 << 17), SCF_TREAT_READ_AS_NORMAL = (1 << 18), SCF_TASK_ORDERED_SYNC = (1 << 19), + SCF_ATOMIC = (1 << 20), }; /* -- cgit v1.2.3 From 710ad826e300ef6d13aa6fddd613a04285cc451d Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:18 +0000 Subject: scsi: target: Report atomic values in INQUIRY Report the atomic values in the Block Limits VPD page. Signed-off-by: Mike Christie jpg: handle not having atomic_supported attribute Signed-off-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-6-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_spc.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index aad0096afa21..ddf8104645d1 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -521,7 +521,6 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) have_tp = 1; buf[0] = dev->transport->get_device_type(dev); - buf[3] = have_tp ? 0x3c : 0x10; /* Set WSNZ to 1 */ buf[4] = 0x01; @@ -562,11 +561,10 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) else put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]); - /* - * Exit now if we don't support TP. - */ + put_unaligned_be16(12, &buf[2]); + if (!have_tp) - goto max_write_same; + goto try_atomic; /* * Set MAXIMUM UNMAP LBA COUNT @@ -595,9 +593,29 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) /* * MAXIMUM WRITE SAME LENGTH */ -max_write_same: put_unaligned_be64(dev->dev_attrib.max_write_same_len, &buf[36]); + put_unaligned_be16(40, &buf[2]); + +try_atomic: + /* + * ATOMIC + */ + if (!dev->dev_attrib.atomic_max_len) + goto done; + + if (dev->dev_attrib.atomic_max_len < io_max_blocks) + put_unaligned_be32(dev->dev_attrib.atomic_max_len, &buf[44]); + else + put_unaligned_be32(io_max_blocks, &buf[44]); + + put_unaligned_be32(dev->dev_attrib.atomic_alignment, &buf[48]); + put_unaligned_be32(dev->dev_attrib.atomic_granularity, &buf[52]); + put_unaligned_be32(dev->dev_attrib.atomic_max_with_boundary, &buf[56]); + put_unaligned_be32(dev->dev_attrib.atomic_max_boundary, &buf[60]); + + put_unaligned_be16(60, &buf[2]); +done: return 0; } -- cgit v1.2.3 From 2d9668883bba31b206f9b02bfc8db1fcb4ca6079 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:19 +0000 Subject: scsi: target: Add WRITE_ATOMIC_16 support to RSOC Report if the device supports WRITE_ATOMIC_16 in the REPORT_SUPPORTED_OPERATION_CODES command. Signed-off-by: Mike Christie Signed-off-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-7-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_spc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index ddf8104645d1..fe2b888bcb43 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -1470,6 +1470,24 @@ static const struct target_opcode_descriptor tcm_opcode_write_same32 = { .update_usage_bits = set_dpofua_usage_bits32, }; +static bool tcm_is_atomic_enabled(const struct target_opcode_descriptor *descr, + struct se_cmd *cmd) +{ + return cmd->se_dev->dev_attrib.atomic_max_len; +} + +static struct target_opcode_descriptor tcm_opcode_write_atomic16 = { + .support = SCSI_SUPPORT_FULL, + .opcode = WRITE_ATOMIC_16, + .cdb_size = 16, + .usage_bits = {WRITE_ATOMIC_16, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, SCSI_GROUP_NUMBER_MASK, SCSI_CONTROL_MASK}, + .enabled = tcm_is_atomic_enabled, + .update_usage_bits = set_dpofua_usage_bits, +}; + static bool tcm_is_caw_enabled(const struct target_opcode_descriptor *descr, struct se_cmd *cmd) { @@ -2026,6 +2044,7 @@ static const struct target_opcode_descriptor *tcm_supported_opcodes[] = { &tcm_opcode_write16, &tcm_opcode_write_verify16, &tcm_opcode_write_same32, + &tcm_opcode_write_atomic16, &tcm_opcode_compare_write, &tcm_opcode_read_capacity, &tcm_opcode_read_capacity16, -- cgit v1.2.3 From 8e62d8f4b159ae80b4db5b5e46db0c5fec58c4a2 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 20 Oct 2025 10:38:20 +0000 Subject: scsi: target: Add atomic support to target_core_iblock Make target_core_iblock use the LIO helper function to translate its block_device atomic settings to LIO settings. If we then get a write that LIO has indicated is atomic via the SCF_ATOMIC flag, we use the REQ_ATOMIC flag to tell the block layer to perform an atomic write. Signed-off-by: Mike Christie Signed-off-by: John Garry Reviewed-by: John Garry Link: https://patch.msgid.link/20251020103820.2917593-8-john.g.garry@oracle.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_iblock.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 281612b9830f..8ec7b534ad76 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -152,6 +152,8 @@ static int iblock_configure_device(struct se_device *dev) if (bdev_nonrot(bd)) dev->dev_attrib.is_nonrot = 1; + target_configure_write_atomic_from_bdev(&dev->dev_attrib, bd); + bi = bdev_get_integrity(bd); if (!bi) return 0; @@ -773,6 +775,9 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, else if (!bdev_write_cache(ib_dev->ibd_bd)) opf |= REQ_FUA; } + + if (cmd->se_cmd_flags & SCF_ATOMIC) + opf |= REQ_ATOMIC; } else { opf = REQ_OP_READ; miter_dir = SG_MITER_FROM_SG; -- cgit v1.2.3