lio-target: Add support for per iSCSI connection CPU scheduling affinity
Subsystems (2)
PATCH 2/2 lio-target: Add support for per iSCSI connection CPU scheduling affinity
From: Nicholas Bellinger <[email protected]>
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().
--- 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(-)
drivers/target/lio-target/iscsi_target.c +85 -0
@@ -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 ;
drivers/target/lio-target/iscsi_target.h +1 -0
@@ -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 *);
drivers/target/lio-target/iscsi_target_core.h +4 -0
@@ -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;
drivers/target/lio-target/iscsi_target_login.c +23 -0
@@ -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) {
Signature · 1 line
1.5.6.5