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

From: Nicholas A. Bellinger
Date: Mon Sep 13 2010 - 03:20:16 EST


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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/