diff mbox series

[25/25] target: make completion affinity configurable

Message ID 20210212072642.17520-26-michael.christie@oracle.com (mailing list archive)
State New, archived
Headers show
Series [01/25] target: move t_task_cdb initialization | expand

Commit Message

Mike Christie Feb. 12, 2021, 7:26 a.m. UTC
It may not always be best to complete the IO on same CPU as it was
submitted on. This allows userspace to config it.

It's useful for vhost-scsi which has the single thread for submissions
and completions. Forcing the completion on the submission cpu is not
always the best thing to do since the thread could be running
on a different CPU now, and it conflicts with what the user has setup
in the lower levels with settings like the block layer rq_affinity
or for network block devices what the user has setup on their nic.

The new setting is in
/sys/kernel/config/target/$fabric/$target/param/cmd_completion_affinity

Writing 0 gives the current default behavior of completing on
the submission CPU. 1 completes the cmd on the CPU the lower layers
sent it to us from.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
---

Note: Christoph, in basically the previous version of this patch
which had vhost-scsi hard coding the behavior you asked if we could
set cmd->cpuid to the WORK UNBOUND value. I could not do this because
code uses the cmd->cpuid to access per cpu structs.


 drivers/target/target_core_fabric_configfs.c | 49 ++++++++++++++++++++
 drivers/target/target_core_internal.h        |  1 +
 drivers/target/target_core_transport.c       |  9 +++-
 include/target/target_core_base.h            |  7 +++
 4 files changed, 64 insertions(+), 2 deletions(-)

Comments

Himanshu Madhani Feb. 12, 2021, 7:35 p.m. UTC | #1
On 2/12/21 1:26 AM, Mike Christie wrote:
> It may not always be best to complete the IO on same CPU as it was
> submitted on. This allows userspace to config it.
> 
> It's useful for vhost-scsi which has the single thread for submissions
> and completions. Forcing the completion on the submission cpu is not
> always the best thing to do since the thread could be running
> on a different CPU now, and it conflicts with what the user has setup
> in the lower levels with settings like the block layer rq_affinity
> or for network block devices what the user has setup on their nic.
> 
> The new setting is in
> /sys/kernel/config/target/$fabric/$target/param/cmd_completion_affinity
> 
> Writing 0 gives the current default behavior of completing on
> the submission CPU. 1 completes the cmd on the CPU the lower layers
> sent it to us from.
> 
> Signed-off-by: Mike Christie <michael.christie@oracle.com>
> ---
> 
> Note: Christoph, in basically the previous version of this patch
> which had vhost-scsi hard coding the behavior you asked if we could
> set cmd->cpuid to the WORK UNBOUND value. I could not do this because
> code uses the cmd->cpuid to access per cpu structs.
> 
> 
>   drivers/target/target_core_fabric_configfs.c | 49 ++++++++++++++++++++
>   drivers/target/target_core_internal.h        |  1 +
>   drivers/target/target_core_transport.c       |  9 +++-
>   include/target/target_core_base.h            |  7 +++
>   4 files changed, 64 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
> index ee85602213f7..7ab27580758a 100644
> --- a/drivers/target/target_core_fabric_configfs.c
> +++ b/drivers/target/target_core_fabric_configfs.c
> @@ -892,6 +892,7 @@ static void target_fabric_release_wwn(struct config_item *item)
>   	struct target_fabric_configfs *tf = wwn->wwn_tf;
>   
>   	configfs_remove_default_groups(&wwn->fabric_stat_group);
> +	configfs_remove_default_groups(&wwn->param_group);
>   	tf->tf_ops->fabric_drop_wwn(wwn);
>   }
>   
> @@ -918,6 +919,49 @@ TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL);
>   
>   /* End of tfc_wwn_fabric_stats_cit */
>   
> +static ssize_t
> +target_fabric_wwn_cmd_completion_affinity_show(struct config_item *item,
> +					       char *page)
> +{
> +	struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
> +					  param_group);
> +	return sprintf(page, "%u\n", wwn->cmd_compl_affinity);
> +}
> +
> +static ssize_t
> +target_fabric_wwn_cmd_completion_affinity_store(struct config_item *item,
> +						const char *page, size_t count)
> +{
> +	struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
> +					  param_group);
> +	u8 compl_val;
> +
> +	if (kstrtou8(page, 0, &compl_val))
> +		return -EINVAL;
> +
> +	switch (compl_val) {
> +	case SE_COMPL_AFFINITY_CPUID:
> +	case SE_COMPL_AFFINITY_CURR_CPU:
> +		wwn->cmd_compl_affinity = compl_val;
> +		break;
> +	default:
> +		pr_err("Command completion value must be between %d and %d\n",
> +		       SE_COMPL_AFFINITY_CPUID, SE_COMPL_AFFINITY_CURR_CPU);
> +		return -EINVAL;
> +	}
> +
> +	wwn->cmd_compl_affinity = compl_val;
> +	return count;
> +}
> +CONFIGFS_ATTR(target_fabric_wwn_, cmd_completion_affinity);
> +
> +static struct configfs_attribute *target_fabric_wwn_param_attrs[] = {
> +	&target_fabric_wwn_attr_cmd_completion_affinity,
> +	NULL,
> +};
> +
> +TF_CIT_SETUP(wwn_param, NULL, NULL, target_fabric_wwn_param_attrs);
> +
>   /* Start of tfc_wwn_cit */
>   
>   static struct config_group *target_fabric_make_wwn(
> @@ -945,6 +989,10 @@ static struct config_group *target_fabric_make_wwn(
>   			&tf->tf_wwn_fabric_stats_cit);
>   	configfs_add_default_group(&wwn->fabric_stat_group, &wwn->wwn_group);
>   
> +	config_group_init_type_name(&wwn->param_group, "param",
> +			&tf->tf_wwn_param_cit);
> +	configfs_add_default_group(&wwn->param_group, &wwn->wwn_group);
> +
>   	if (tf->tf_ops->add_wwn_groups)
>   		tf->tf_ops->add_wwn_groups(wwn);
>   	return &wwn->wwn_group;
> @@ -974,6 +1022,7 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf)
>   	target_fabric_setup_discovery_cit(tf);
>   	target_fabric_setup_wwn_cit(tf);
>   	target_fabric_setup_wwn_fabric_stats_cit(tf);
> +	target_fabric_setup_wwn_param_cit(tf);
>   	target_fabric_setup_tpg_cit(tf);
>   	target_fabric_setup_tpg_base_cit(tf);
>   	target_fabric_setup_tpg_port_cit(tf);
> diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
> index 56f841fd7f04..a343bcfa2180 100644
> --- a/drivers/target/target_core_internal.h
> +++ b/drivers/target/target_core_internal.h
> @@ -34,6 +34,7 @@ struct target_fabric_configfs {
>   	struct config_item_type tf_discovery_cit;
>   	struct config_item_type	tf_wwn_cit;
>   	struct config_item_type tf_wwn_fabric_stats_cit;
> +	struct config_item_type tf_wwn_param_cit;
>   	struct config_item_type tf_tpg_cit;
>   	struct config_item_type tf_tpg_base_cit;
>   	struct config_item_type tf_tpg_lun_cit;
> diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
> index 45bb5253461a..cf3d916dd612 100644
> --- a/drivers/target/target_core_transport.c
> +++ b/drivers/target/target_core_transport.c
> @@ -857,7 +857,8 @@ static bool target_cmd_interrupted(struct se_cmd *cmd)
>   /* May be called from interrupt context so must not sleep. */
>   void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
>   {
> -	int success;
> +	struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn;
> +	int success, cpu = WORK_CPU_UNBOUND;
>   	unsigned long flags;
>   
>   	if (target_cmd_interrupted(cmd))
> @@ -884,7 +885,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
>   
>   	INIT_WORK(&cmd->work, success ? target_complete_ok_work :
>   		  target_complete_failure_work);
> -	queue_work_on(cmd->cpuid, target_completion_wq, &cmd->work);
> +
> +	if (wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID)
> +		cpu = cmd->cpuid;
> +
> +	queue_work_on(cpu, target_completion_wq, &cmd->work);
>   }
>   EXPORT_SYMBOL(target_complete_cmd);
>   
> diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
> index b8e0a3250bd0..4ed385537301 100644
> --- a/include/target/target_core_base.h
> +++ b/include/target/target_core_base.h
> @@ -943,11 +943,18 @@ static inline struct se_portal_group *param_to_tpg(struct config_item *item)
>   			tpg_param_group);
>   }
>   
> +enum {
> +	SE_COMPL_AFFINITY_CPUID,	/* Use se_cmd's cpuid for completion */
> +	SE_COMPL_AFFINITY_CURR_CPU,	/* Complete on current CPU */
> +};
> +
>   struct se_wwn {
>   	struct target_fabric_configfs *wwn_tf;
>   	void			*priv;
>   	struct config_group	wwn_group;
>   	struct config_group	fabric_stat_group;
> +	struct config_group	param_group;
> +	u8			cmd_compl_affinity;
>   };
>   
>   static inline void atomic_inc_mb(atomic_t *v)
> 

Looks Good.

Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
diff mbox series

Patch

diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index ee85602213f7..7ab27580758a 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -892,6 +892,7 @@  static void target_fabric_release_wwn(struct config_item *item)
 	struct target_fabric_configfs *tf = wwn->wwn_tf;
 
 	configfs_remove_default_groups(&wwn->fabric_stat_group);
+	configfs_remove_default_groups(&wwn->param_group);
 	tf->tf_ops->fabric_drop_wwn(wwn);
 }
 
@@ -918,6 +919,49 @@  TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL);
 
 /* End of tfc_wwn_fabric_stats_cit */
 
+static ssize_t
+target_fabric_wwn_cmd_completion_affinity_show(struct config_item *item,
+					       char *page)
+{
+	struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
+					  param_group);
+	return sprintf(page, "%u\n", wwn->cmd_compl_affinity);
+}
+
+static ssize_t
+target_fabric_wwn_cmd_completion_affinity_store(struct config_item *item,
+						const char *page, size_t count)
+{
+	struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
+					  param_group);
+	u8 compl_val;
+
+	if (kstrtou8(page, 0, &compl_val))
+		return -EINVAL;
+
+	switch (compl_val) {
+	case SE_COMPL_AFFINITY_CPUID:
+	case SE_COMPL_AFFINITY_CURR_CPU:
+		wwn->cmd_compl_affinity = compl_val;
+		break;
+	default:
+		pr_err("Command completion value must be between %d and %d\n",
+		       SE_COMPL_AFFINITY_CPUID, SE_COMPL_AFFINITY_CURR_CPU);
+		return -EINVAL;
+	}
+
+	wwn->cmd_compl_affinity = compl_val;
+	return count;
+}
+CONFIGFS_ATTR(target_fabric_wwn_, cmd_completion_affinity);
+
+static struct configfs_attribute *target_fabric_wwn_param_attrs[] = {
+	&target_fabric_wwn_attr_cmd_completion_affinity,
+	NULL,
+};
+
+TF_CIT_SETUP(wwn_param, NULL, NULL, target_fabric_wwn_param_attrs);
+
 /* Start of tfc_wwn_cit */
 
 static struct config_group *target_fabric_make_wwn(
@@ -945,6 +989,10 @@  static struct config_group *target_fabric_make_wwn(
 			&tf->tf_wwn_fabric_stats_cit);
 	configfs_add_default_group(&wwn->fabric_stat_group, &wwn->wwn_group);
 
+	config_group_init_type_name(&wwn->param_group, "param",
+			&tf->tf_wwn_param_cit);
+	configfs_add_default_group(&wwn->param_group, &wwn->wwn_group);
+
 	if (tf->tf_ops->add_wwn_groups)
 		tf->tf_ops->add_wwn_groups(wwn);
 	return &wwn->wwn_group;
@@ -974,6 +1022,7 @@  int target_fabric_setup_cits(struct target_fabric_configfs *tf)
 	target_fabric_setup_discovery_cit(tf);
 	target_fabric_setup_wwn_cit(tf);
 	target_fabric_setup_wwn_fabric_stats_cit(tf);
+	target_fabric_setup_wwn_param_cit(tf);
 	target_fabric_setup_tpg_cit(tf);
 	target_fabric_setup_tpg_base_cit(tf);
 	target_fabric_setup_tpg_port_cit(tf);
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 56f841fd7f04..a343bcfa2180 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -34,6 +34,7 @@  struct target_fabric_configfs {
 	struct config_item_type tf_discovery_cit;
 	struct config_item_type	tf_wwn_cit;
 	struct config_item_type tf_wwn_fabric_stats_cit;
+	struct config_item_type tf_wwn_param_cit;
 	struct config_item_type tf_tpg_cit;
 	struct config_item_type tf_tpg_base_cit;
 	struct config_item_type tf_tpg_lun_cit;
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 45bb5253461a..cf3d916dd612 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -857,7 +857,8 @@  static bool target_cmd_interrupted(struct se_cmd *cmd)
 /* May be called from interrupt context so must not sleep. */
 void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 {
-	int success;
+	struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn;
+	int success, cpu = WORK_CPU_UNBOUND;
 	unsigned long flags;
 
 	if (target_cmd_interrupted(cmd))
@@ -884,7 +885,11 @@  void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 
 	INIT_WORK(&cmd->work, success ? target_complete_ok_work :
 		  target_complete_failure_work);
-	queue_work_on(cmd->cpuid, target_completion_wq, &cmd->work);
+
+	if (wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID)
+		cpu = cmd->cpuid;
+
+	queue_work_on(cpu, target_completion_wq, &cmd->work);
 }
 EXPORT_SYMBOL(target_complete_cmd);
 
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index b8e0a3250bd0..4ed385537301 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -943,11 +943,18 @@  static inline struct se_portal_group *param_to_tpg(struct config_item *item)
 			tpg_param_group);
 }
 
+enum {
+	SE_COMPL_AFFINITY_CPUID,	/* Use se_cmd's cpuid for completion */
+	SE_COMPL_AFFINITY_CURR_CPU,	/* Complete on current CPU */
+};
+
 struct se_wwn {
 	struct target_fabric_configfs *wwn_tf;
 	void			*priv;
 	struct config_group	wwn_group;
 	struct config_group	fabric_stat_group;
+	struct config_group	param_group;
+	u8			cmd_compl_affinity;
 };
 
 static inline void atomic_inc_mb(atomic_t *v)