Merge patch series "scsi: target: Add support for completing commands from backend context"

The following patches made over Linus's current tree allow users to
tell the target layer to perform direct completions instead of always
deferring to LIO's completion workqueue. When the frontend driver
already has multiple worker threads (or you are doing a LUN per target
with a single thread per target) then bypassing the LIO workqueue can
increase performance 20-30%.

Link: https://patch.msgid.link/20260222232946.7637-1-michael.christie@oracle.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Martin K. Petersen 2026-02-28 21:05:49 -05:00
commit 1bf5c303eb
17 changed files with 129 additions and 14 deletions

View File

@ -3925,6 +3925,7 @@ static const struct target_core_fabric_ops srpt_template = {
.tfc_wwn_attrs = srpt_wwn_attrs,
.tfc_tpg_attrib_attrs = srpt_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -1612,6 +1612,7 @@ static const struct target_core_fabric_ops efct_lio_ops = {
.sess_get_initiator_sid = NULL,
.tfc_tpg_base_attrs = efct_lio_tpg_attrs,
.tfc_tpg_attrib_attrs = efct_lio_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};
@ -1650,6 +1651,7 @@ static const struct target_core_fabric_ops efct_lio_npiv_ops = {
.tfc_tpg_base_attrs = efct_lio_npiv_tpg_attrs,
.tfc_tpg_attrib_attrs = efct_lio_npiv_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -3968,6 +3968,7 @@ static const struct target_core_fabric_ops ibmvscsis_ops = {
.tfc_wwn_attrs = ibmvscsis_wwn_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -1841,6 +1841,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = {
.tfc_tpg_base_attrs = tcm_qla2xxx_tpg_attrs,
.tfc_tpg_attrib_attrs = tcm_qla2xxx_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};
@ -1881,6 +1882,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = {
.tfc_wwn_attrs = tcm_qla2xxx_wwn_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -1591,6 +1591,7 @@ const struct target_core_fabric_ops iscsi_ops = {
.write_pending_must_be_called = 1,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -1107,6 +1107,7 @@ static const struct target_core_fabric_ops loop_ops = {
.tfc_wwn_attrs = tcm_loop_wwn_attrs,
.tfc_tpg_base_attrs = tcm_loop_tpg_attrs,
.tfc_tpg_attrib_attrs = tcm_loop_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_QUEUE_SUBMIT,
.direct_submit_supp = 0,
};

View File

@ -2278,6 +2278,7 @@ static const struct target_core_fabric_ops sbp_ops = {
.tfc_tpg_base_attrs = sbp_tpg_base_attrs,
.tfc_tpg_attrib_attrs = sbp_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -578,6 +578,7 @@ 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(complete_type);
DEF_CONFIGFS_ATTRIB_SHOW(atomic_max_len);
DEF_CONFIGFS_ATTRIB_SHOW(atomic_alignment);
DEF_CONFIGFS_ATTRIB_SHOW(atomic_granularity);
@ -1269,6 +1270,24 @@ static ssize_t submit_type_store(struct config_item *item, const char *page,
return count;
}
static ssize_t complete_type_store(struct config_item *item, const char *page,
size_t count)
{
struct se_dev_attrib *da = to_attrib(item);
int ret;
u8 val;
ret = kstrtou8(page, 0, &val);
if (ret < 0)
return ret;
if (val > TARGET_QUEUE_COMPL)
return -EINVAL;
da->complete_type = val;
return count;
}
CONFIGFS_ATTR(, emulate_model_alias);
CONFIGFS_ATTR(, emulate_dpo);
CONFIGFS_ATTR(, emulate_fua_write);
@ -1305,6 +1324,7 @@ CONFIGFS_ATTR(, max_write_same_len);
CONFIGFS_ATTR(, alua_support);
CONFIGFS_ATTR(, pgr_support);
CONFIGFS_ATTR(, submit_type);
CONFIGFS_ATTR(, complete_type);
CONFIGFS_ATTR_RO(, atomic_max_len);
CONFIGFS_ATTR_RO(, atomic_alignment);
CONFIGFS_ATTR_RO(, atomic_granularity);
@ -1353,6 +1373,7 @@ struct configfs_attribute *sbc_attrib_attrs[] = {
&attr_pgr_support,
&attr_emulate_rsoc,
&attr_submit_type,
&attr_complete_type,
&attr_atomic_alignment,
&attr_atomic_max_len,
&attr_atomic_granularity,
@ -1376,6 +1397,7 @@ struct configfs_attribute *passthrough_attrib_attrs[] = {
&attr_alua_support,
&attr_pgr_support,
&attr_submit_type,
&attr_complete_type,
NULL,
};
EXPORT_SYMBOL(passthrough_attrib_attrs);

View File

@ -813,6 +813,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
DA_UNMAP_ZEROES_DATA_DEFAULT;
dev->dev_attrib.max_write_same_len = DA_MAX_WRITE_SAME_LEN;
dev->dev_attrib.submit_type = TARGET_FABRIC_DEFAULT_SUBMIT;
dev->dev_attrib.submit_type = TARGET_FABRIC_DEFAULT_COMPL;
/* Skip allocating lun_stats since we can't export them. */
xcopy_lun = &dev->xcopy_lun;

View File

@ -1065,6 +1065,28 @@ target_fabric_wwn_cmd_completion_affinity_store(struct config_item *item,
}
CONFIGFS_ATTR(target_fabric_wwn_, cmd_completion_affinity);
static ssize_t
target_fabric_wwn_default_complete_type_show(struct config_item *item,
char *page)
{
struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
param_group);
return sysfs_emit(page, "%u\n",
wwn->wwn_tf->tf_ops->default_compl_type);
}
CONFIGFS_ATTR_RO(target_fabric_wwn_, default_complete_type);
static ssize_t
target_fabric_wwn_direct_complete_supported_show(struct config_item *item,
char *page)
{
struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
param_group);
return sysfs_emit(page, "%u\n",
wwn->wwn_tf->tf_ops->direct_compl_supp);
}
CONFIGFS_ATTR_RO(target_fabric_wwn_, direct_complete_supported);
static ssize_t
target_fabric_wwn_default_submit_type_show(struct config_item *item,
char *page)
@ -1089,6 +1111,8 @@ CONFIGFS_ATTR_RO(target_fabric_wwn_, direct_submit_supported);
static struct configfs_attribute *target_fabric_wwn_param_attrs[] = {
&target_fabric_wwn_attr_cmd_completion_affinity,
&target_fabric_wwn_attr_default_complete_type,
&target_fabric_wwn_attr_direct_complete_supported,
&target_fabric_wwn_attr_default_submit_type,
&target_fabric_wwn_attr_direct_submit_supported,
NULL,

View File

@ -902,13 +902,59 @@ static bool target_cmd_interrupted(struct se_cmd *cmd)
return false;
}
static void target_complete(struct se_cmd *cmd, int success)
{
struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn;
struct se_dev_attrib *da;
u8 compl_type;
int cpu;
if (!wwn) {
cpu = cmd->cpuid;
goto queue_work;
}
da = &cmd->se_dev->dev_attrib;
if (da->complete_type == TARGET_FABRIC_DEFAULT_COMPL)
compl_type = wwn->wwn_tf->tf_ops->default_compl_type;
else if (da->complete_type == TARGET_DIRECT_SUBMIT &&
wwn->wwn_tf->tf_ops->direct_compl_supp)
compl_type = TARGET_DIRECT_COMPL;
else
compl_type = TARGET_QUEUE_COMPL;
if (compl_type == TARGET_DIRECT_COMPL) {
/*
* Failure handling and processing secondary stages of
* complex commands can be too heavy to handle from the
* fabric driver so always defer.
*/
if (success && !cmd->transport_complete_callback) {
target_complete_ok_work(&cmd->work);
return;
}
compl_type = TARGET_QUEUE_COMPL;
}
queue_work:
INIT_WORK(&cmd->work, success ? target_complete_ok_work :
target_complete_failure_work);
if (!wwn || wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID)
cpu = cmd->cpuid;
else
cpu = wwn->cmd_compl_affinity;
queue_work_on(cpu, target_completion_wq, &cmd->work);
}
/* May be called from interrupt context so must not sleep. */
void target_complete_cmd_with_sense(struct se_cmd *cmd, u8 scsi_status,
sense_reason_t sense_reason)
{
struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn;
int success, cpu;
unsigned long flags;
int success;
if (target_cmd_interrupted(cmd))
return;
@ -933,15 +979,7 @@ void target_complete_cmd_with_sense(struct se_cmd *cmd, u8 scsi_status,
cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
INIT_WORK(&cmd->work, success ? target_complete_ok_work :
target_complete_failure_work);
if (!wwn || wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID)
cpu = cmd->cpuid;
else
cpu = wwn->cmd_compl_affinity;
queue_work_on(cpu, target_completion_wq, &cmd->work);
target_complete(cmd, success);
}
EXPORT_SYMBOL(target_complete_cmd_with_sense);

View File

@ -434,6 +434,7 @@ static const struct target_core_fabric_ops ft_fabric_ops = {
.tfc_wwn_attrs = ft_wwn_attrs,
.tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -2016,6 +2016,7 @@ static const struct target_core_fabric_ops usbg_ops = {
.tfc_wwn_attrs = usbg_wwn_attrs,
.tfc_tpg_base_attrs = usbg_base_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -2950,6 +2950,8 @@ static const struct target_core_fabric_ops vhost_scsi_ops = {
.tfc_tpg_base_attrs = vhost_scsi_tpg_attrs,
.tfc_tpg_attrib_attrs = vhost_scsi_tpg_attrib_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.direct_compl_supp = 1,
.default_submit_type = TARGET_QUEUE_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -1832,6 +1832,7 @@ static const struct target_core_fabric_ops scsiback_ops = {
.tfc_tpg_base_attrs = scsiback_tpg_attrs,
.tfc_tpg_param_attrs = scsiback_param_attrs,
.default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};

View File

@ -111,6 +111,15 @@
/* Peripheral Device Text Identification Information */
#define PD_TEXT_ID_INFO_LEN 256
enum target_compl_type {
/* Use the fabric driver's default completion type */
TARGET_FABRIC_DEFAULT_COMPL,
/* Complete from the backend calling context */
TARGET_DIRECT_COMPL,
/* Defer completion to the LIO workqueue */
TARGET_QUEUE_COMPL,
};
enum target_submit_type {
/* Use the fabric driver's default submission type */
TARGET_FABRIC_DEFAULT_SUBMIT,
@ -741,6 +750,7 @@ struct se_dev_attrib {
u32 atomic_granularity;
u32 atomic_max_with_boundary;
u32 atomic_max_boundary;
u8 complete_type;
u8 submit_type;
struct se_device *da_dev;
struct config_group da_group;

View File

@ -118,15 +118,21 @@ struct target_core_fabric_ops {
* its entirety before a command is aborted.
*/
unsigned int write_pending_must_be_called:1;
/*
* Set this if the driver does not require calling queue_data_in
* queue_status and check_stop_free from a worker thread when
* completing successful commands.
*/
unsigned int direct_compl_supp:1;
/*
* Set this if the driver supports submitting commands to the backend
* from target_submit/target_submit_cmd.
*/
unsigned int direct_submit_supp:1;
/*
* Set this to a target_submit_type value.
*/
/* Set this to a target_submit_type value. */
u8 default_submit_type;
/* Set this to the target_compl_type value. */
u8 default_compl_type;
};
int target_register_template(const struct target_core_fabric_ops *fo);