[PATCH 2/2] lio-target: Add support for per iSCSI connection CPU scheduling affinity

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx>

This patch adds support for a per connection context struct iscsi_conn->conn_cpumask
which by default will set the CPU scheduling affinity for a RX/TX thread_set
pair to execute on the same CPU.  It adds a struct iscsi_conn->conn_cpumask, as well
as conn_rx_reset_cpumask and conn_tx_reset_cpumask used by iscsi_thread_check_cpumask()
discussed below.

The first item is iscsi_thread_get_cpumask() that is used to determine which CPU of the
active online CPUs the struct iscsi_conn threads will be running on.  The logic to
determine which CPU the two threads of the set will be running on was originally
inspried by drivers/block/drbd/drbd_main.c:drbd_calc_cpu_mask(), and looks like
the following in iscsi_thread_get_cpumask():

	ord = ts->thread_id % cpumask_weight(cpu_online_mask);
	for_each_online_cpu(cpu) {
		if (ord-- == 0) {
			cpumask_set_cpu(cpu, conn->conn_cpumask);
			return;
		}
	}

The code that will notify the scheduler to update the struct iscsi_conn->conn_cpumask
is located in iscsi_thread_check_cpumask(), which is run once per iteration within the
primary while loops of iscsi_target_tx_thread() and iscsi_target_rx_thread() to determine
if set_cpus_allowed_ptr() should be called for a newly updated struct conn->conn_cpumask.
This patch currently sets conn_rx_reset_cpumask=1 and conn_tx_reset_cpumask=1 within
iscsi_target_login.c: iscsi_post_login_handler() during the iSCSI loginc process in
order to signal the initial conn_cpumask generated by iscsi_thread_get_cpumask().

So far this code has been tested on a bare-metal Nehalem 8 CPUID system running
v2.6.36-rc3, and checking with 'taskset -c -p' to show that each individual
iscsi_t[r,t]x/$THREAD_ID pair have their CPU affinity set to the same CPU ID based
on the thread_id generated using bitmap_find_free_region(iscsi_global->ts_bitmap, ...)
in iscsi_thread_queue.c:iscsi_allocate_thread_sets().

Signed-off-by: Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx>
---
 drivers/target/lio-target/iscsi_target.c       |   85 ++++++++++++++++++++++++
 drivers/target/lio-target/iscsi_target.h       |    1 +
 drivers/target/lio-target/iscsi_target_core.h  |    4 +
 drivers/target/lio-target/iscsi_target_login.c |   23 +++++++
 4 files changed, 113 insertions(+), 0 deletions(-)

diff --git a/drivers/target/lio-target/iscsi_target.c b/drivers/target/lio-target/iscsi_target.c
index 9b7c86c..3c47b55 100644
--- a/drivers/target/lio-target/iscsi_target.c
+++ b/drivers/target/lio-target/iscsi_target.c
@@ -4441,6 +4441,76 @@ static void iscsi_tx_thread_wait_for_TCP(struct iscsi_conn *conn)
 	}
 }
 
+#ifdef CONFIG_SMP
+
+void iscsi_thread_get_cpumask(struct iscsi_conn *conn)
+{
+	struct se_thread_set *ts = conn->thread_set;
+	int ord, cpu;
+	/*
+	 * thread_id is assigned from iscsi_global->ts_bitmap from
+	 * within iscsi_thread_set.c:iscsi_allocate_thread_sets()
+	 *
+	 * Here we use thread_id to determine which CPU that this
+	 * iSCSI connection's se_thread_set will be scheduled to
+	 * execute upon.
+	 */
+	ord = ts->thread_id % cpumask_weight(cpu_online_mask);
+#if 0
+	printk(">>>>>>>>>>>>>>>>>>>> Generated ord: %d from thread_id: %d\n",
+			ord, ts->thread_id);
+#endif
+	for_each_online_cpu(cpu) {
+		if (ord-- == 0) {
+			cpumask_set_cpu(cpu, conn->conn_cpumask);
+			return;
+		}
+	}
+	/*
+	 * This should never be reached..
+	 */
+	dump_stack();
+	cpumask_setall(conn->conn_cpumask);
+}
+
+static inline void iscsi_thread_check_cpumask(
+	struct iscsi_conn *conn,
+	struct task_struct *p,
+	int mode)
+{
+	char buf[128];
+	/*
+	 * mode == 1 signals iscsi_target_tx_thread() usage.
+	 * mode == 0 signals iscsi_target_rx_thread() usage.
+	 */
+	if (mode == 1) {
+		if (!(conn->conn_tx_reset_cpumask))
+			return;
+		conn->conn_tx_reset_cpumask = 0;
+	} else {
+		if (!(conn->conn_rx_reset_cpumask))
+			return;
+		conn->conn_rx_reset_cpumask = 0;
+	}
+	/*
+	 * Update the CPU mask for this single kthread so that
+	 * both TX and RX kthreads are scheduled to run on the
+	 * same CPU.
+	 */
+	memset(buf, 0, 128);
+	cpumask_scnprintf(buf, 128, conn->conn_cpumask);
+#if 0
+	printk(">>>>>>>>>>>>>> Calling set_cpus_allowed_ptr(): %s for %s\n",
+			buf, p->comm);
+#endif
+	set_cpus_allowed_ptr(p, conn->conn_cpumask);
+}
+
+#else
+#define iscsi_thread_get_cpumask(X) ({})
+#define iscsi_thread_check_cpumask(X, Y, Z) ({})
+#endif /* CONFIG_SMP */
+
 /*	iscsi_target_tx_thread():
  *
  *
@@ -4472,6 +4542,12 @@ restart:
 	eodr = map_sg = ret = sent_status = use_misc = 0;
 
 	while (1) {
+		/*
+		 * Ensure that both TX and RX per connection kthreads
+		 * are scheduled to run on the same CPU.
+		 */
+		iscsi_thread_check_cpumask(conn, current, 1);
+
 		ret = down_interruptible(&conn->tx_sem);
 
 		if ((ts->status == ISCSI_THREAD_SET_RESET) ||
@@ -4817,6 +4893,12 @@ restart:
 		goto out;
 
 	while (1) {
+		/*
+		 * Ensure that both TX and RX per connection kthreads
+		 * are scheduled to run on the same CPU.
+		 */
+		iscsi_thread_check_cpumask(conn, current, 0);
+
 		memset((void *)buffer, 0, ISCSI_HDR_LEN);
 		memset((void *)&iov, 0, sizeof(struct iovec));
 
@@ -5141,6 +5223,9 @@ int iscsi_close_connection(
 	if (conn->conn_tx_hash.tfm)
 		crypto_free_hash(conn->conn_tx_hash.tfm);
 
+	if (conn->conn_cpumask)
+		free_cpumask_var(conn->conn_cpumask);
+
 	kfree(conn->conn_ops);
 	conn->conn_ops = NULL;
 
diff --git a/drivers/target/lio-target/iscsi_target.h b/drivers/target/lio-target/iscsi_target.h
index 688b22e..c725797 100644
--- a/drivers/target/lio-target/iscsi_target.h
+++ b/drivers/target/lio-target/iscsi_target.h
@@ -35,6 +35,7 @@ extern int lio_queue_status(struct se_cmd *);
 extern u16 lio_set_fabric_sense_len(struct se_cmd *, u32);
 extern u16 lio_get_fabric_sense_len(void);
 extern int lio_queue_tm_rsp(struct se_cmd *);
+extern void iscsi_thread_get_cpumask(struct iscsi_conn *);
 extern int iscsi_target_tx_thread(void *);
 extern int iscsi_target_rx_thread(void *);
 extern int iscsi_close_connection(struct iscsi_conn *);
diff --git a/drivers/target/lio-target/iscsi_target_core.h b/drivers/target/lio-target/iscsi_target_core.h
index 733f09f..017558a 100644
--- a/drivers/target/lio-target/iscsi_target_core.h
+++ b/drivers/target/lio-target/iscsi_target_core.h
@@ -588,6 +588,10 @@ struct iscsi_conn {
 	/* libcrypto RX and TX contexts for crc32c */
 	struct hash_desc	conn_rx_hash;
 	struct hash_desc	conn_tx_hash;
+	/* Used for scheduling TX and RX connection kthreads */
+	cpumask_var_t		conn_cpumask;
+	int			conn_rx_reset_cpumask:1;
+	int			conn_tx_reset_cpumask:1;
 	/* list_head of struct iscsi_cmd for this connection */
 	struct list_head	conn_cmd_list;
 	struct list_head	immed_queue_list;
diff --git a/drivers/target/lio-target/iscsi_target_login.c b/drivers/target/lio-target/iscsi_target_login.c
index 7c8500d..6fda6fe 100644
--- a/drivers/target/lio-target/iscsi_target_login.c
+++ b/drivers/target/lio-target/iscsi_target_login.c
@@ -80,6 +80,11 @@ static int iscsi_login_init_conn(struct iscsi_conn *conn)
 	spin_lock_init(&conn->response_queue_lock);
 	spin_lock_init(&conn->state_lock);
 
+	if (!(zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL))) {
+		printk(KERN_ERR "Unable to allocate conn->conn_cpumask\n");
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
@@ -703,6 +708,14 @@ static int iscsi_post_login_handler(
 
 		iscsi_post_login_start_timers(conn);
 		iscsi_activate_thread_set(conn, ts);
+		/*
+		 * Determine CPU mask to ensure connection's RX and TX kthreads
+		 * are scheduled on the same CPU.
+		 */
+		iscsi_thread_get_cpumask(conn);
+		conn->conn_rx_reset_cpumask = 1;
+		conn->conn_tx_reset_cpumask = 1;
+
 		iscsi_dec_conn_usage_count(conn);
 		if (stop_timer) {
 			spin_lock_bh(&se_tpg->session_lock);
@@ -752,6 +765,13 @@ static int iscsi_post_login_handler(
 
 	iscsi_post_login_start_timers(conn);
 	iscsi_activate_thread_set(conn, ts);
+	/*
+	 * Determine CPU mask to ensure connection's RX and TX kthreads
+	 * are scheduled on the same CPU.
+	 */
+	iscsi_thread_get_cpumask(conn);
+	conn->conn_rx_reset_cpumask = 1;
+	conn->conn_tx_reset_cpumask = 1;
 
 	iscsi_dec_conn_usage_count(conn);
 
@@ -1354,6 +1374,9 @@ old_sess_out:
 	if (conn->conn_tx_hash.tfm)
 		crypto_free_hash(conn->conn_tx_hash.tfm);
 
+	if (conn->conn_cpumask)
+		free_cpumask_var(conn->conn_cpumask);
+
 	kfree(conn->conn_ops);
 
 	if (conn->param_list) {
-- 
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux