Assigning CPU affinity to threads and connections

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2317 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2010-09-30 18:07:27 +00:00
parent 11008f0f98
commit ff2c24c1c3
17 changed files with 681 additions and 195 deletions
+13
View File
@@ -1,3 +1,16 @@
Summary of changes between versions 2.0.0 and 2.1.0
---------------------------------------------------
- Assigning CPU affinity to threads and connections
- Splitting read/write CDBs for pass-through devices with sg_tablesize
and max_sectors limitations added to scst_disk.
- Support for thin provisioning commands added in scst_vdisk.
- Bug fixes and other minor improvements.
Summary of changes between versions 1.0.1 and 1.0.2
---------------------------------------------------
+15 -8
View File
@@ -515,6 +515,10 @@ Every target should have at least the following entries:
initiators security groups, so you can assign the addressing method
on per-initiator basis.
- cpu_mask - defines CPU affinity mask for threads serving this target.
For threads serving LUNs it is used only for devices with
threads_pool_type "per_initiator".
- io_grouping_type - defines how I/O from sessions to this target are
grouped together. This I/O grouping is very important for
performance. By setting this attribute in a right value, you can
@@ -668,7 +672,8 @@ commands by looking inside this file.
- "del GROUP_NAME" - deletes a new security group.
Each security group's subdirectory contains 2 subdirectories: initiators
and luns.
and luns as well as the following attributes: addr_method, cpu_mask and
io_grouping_type. See above description of them.
Each "initiators" subdirectory contains list of added to this groups
initiator as well as as well as file "mgmt". This file has the following
@@ -1487,13 +1492,15 @@ IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
for VDISK FILEIO devices above.
9. In some cases, for instance working with SSD devices, which consume 100%
of a single CPU load for data transfers in their internal threads, to
maximize IOPS it can be needed to assign for those threads dedicated
CPUs using Linux CPU affinity facilities. No IRQ processing should be
done on those CPUs. Check that using /proc/interrupts. See taskset
command and Documentation/IRQ-affinity.txt in your kernel's source tree
for how to assign IRQ affinity to tasks and IRQs.
9. In some cases, for instance working with SSD devices, which consume
100% of a single CPU load for data transfers in their internal threads,
to maximize IOPS it can be needed to assign for those threads dedicated
CPUs. Consider using cpu_mask attribute for devices with
threads_pool_type "per_initiator" or Linux CPU affinity facilities for
other threads_pool_types. No IRQ processing should be done on those
CPUs. Check that using /proc/interrupts. See taskset command and
Documentation/IRQ-affinity.txt in your kernel's source tree for how to
assign IRQ affinity to tasks and IRQs.
The reason for that is that processing of coming commands in SIRQ
context might be done on the same CPUs as SSD devices' threads doing data
+15 -8
View File
@@ -383,6 +383,10 @@ Every target should have at least the following entries:
initiators security groups, so you can assign the addressing method
on per-initiator basis.
- cpu_mask - defines CPU affinity mask for threads serving this target.
For threads serving LUNs it is used only for devices with
threads_pool_type "per_initiator".
- io_grouping_type - defines how I/O from sessions to this target are
grouped together. This I/O grouping is very important for
performance. By setting this attribute in a right value, you can
@@ -536,7 +540,8 @@ commands by looking inside this file.
- "del GROUP_NAME" - deletes a new security group.
Each security group's subdirectory contains 2 subdirectories: initiators
and luns.
and luns as well as the following attributes: addr_method, cpu_mask and
io_grouping_type. See above description of them.
Each "initiators" subdirectory contains list of added to this groups
initiator as well as as well as file "mgmt". This file has the following
@@ -1306,13 +1311,15 @@ IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
See also important notes about setting block sizes >512 bytes
for VDISK FILEIO devices above.
9. In some cases, for instance working with SSD devices, which consume 100%
of a single CPU load for data transfers in their internal threads, to
maximize IOPS it can be needed to assign for those threads dedicated
CPUs using Linux CPU affinity facilities. No IRQ processing should be
done on those CPUs. Check that using /proc/interrupts. See taskset
command and Documentation/IRQ-affinity.txt in your kernel's source tree
for how to assign IRQ affinity to tasks and IRQs.
9. In some cases, for instance working with SSD devices, which consume
100% of a single CPU load for data transfers in their internal threads,
to maximize IOPS it can be needed to assign for those threads dedicated
CPUs. Consider using cpu_mask attribute for devices with
threads_pool_type "per_initiator" or Linux CPU affinity facilities for
other threads_pool_types. No IRQ processing should be done on those
CPUs. Check that using /proc/interrupts. See taskset command and
Documentation/IRQ-affinity.txt in your kernel's source tree for how to
assign IRQ affinity to tasks and IRQs.
The reason for that is that processing of coming commands in SIRQ
context might be done on the same CPUs as SSD devices' threads doing data
+9
View File
@@ -335,6 +335,11 @@ enum scst_exec_context {
*/
#define SCST_AEN_SCSI 0
/*
* Notifies that CPU affinity mask on the corresponding session changed
*/
#define SCST_AEN_CPU_MASK_CHANGED 1
/*************************************************************
** Allowed return/status codes for report_aen() callback and
** scst_set_aen_delivery_status() function
@@ -2390,6 +2395,9 @@ struct scst_acg {
/* Type of I/O initiators groupping */
int acg_io_grouping_type;
/* CPU affinity for threads in this ACG */
struct cpumask acg_cpu_mask;
unsigned int tgt_acg:1;
/* sysfs release completion */
@@ -3887,6 +3895,7 @@ struct scst_sysfs_work_item {
bool is_tgt_kobj;
int io_grouping_type;
bool enable;
struct cpumask cpu_mask;
};
};
struct {
+3 -2
View File
@@ -1325,7 +1325,7 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
}
EXPORT_SYMBOL(scst_set_initial_UA);
static struct scst_aen *scst_alloc_aen(struct scst_session *sess,
struct scst_aen *scst_alloc_aen(struct scst_session *sess,
uint64_t unpacked_lun)
{
struct scst_aen *aen;
@@ -1351,7 +1351,7 @@ out:
return aen;
}
static void scst_free_aen(struct scst_aen *aen)
void scst_free_aen(struct scst_aen *aen)
{
TRACE_ENTRY();
@@ -2814,6 +2814,7 @@ struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt,
INIT_LIST_HEAD(&acg->acg_dev_list);
INIT_LIST_HEAD(&acg->acg_sess_list);
INIT_LIST_HEAD(&acg->acn_list);
cpumask_copy(&acg->acg_cpu_mask, &default_cpu_mask);
acg->acg_name = kstrdup(acg_name, GFP_KERNEL);
if (acg->acg_name == NULL) {
PRINT_ERROR("%s", "Allocation of acg_name failed");
+9
View File
@@ -158,6 +158,8 @@ static int suspend_count;
static int scst_virt_dev_last_id; /* protected by scst_mutex */
struct cpumask default_cpu_mask;
static unsigned int scst_max_cmd_mem;
unsigned int scst_max_dev_cmd_mem;
@@ -1588,9 +1590,15 @@ int scst_add_threads(struct scst_cmd_threads *cmd_threads,
cmd_threads, "%s%d", nm, n++);
} else if (tgt_dev != NULL) {
char nm[11]; /* to limit the name's len */
int rc;
strlcpy(nm, tgt_dev->dev->virt_name, ARRAY_SIZE(nm));
thr->cmd_thread = kthread_create(scst_cmd_thread,
cmd_threads, "%s%d_%d", nm, tgt_dev_num, n++);
rc = set_cpus_allowed_ptr(thr->cmd_thread,
&tgt_dev->sess->acg->acg_cpu_mask);
if (rc != 0)
PRINT_ERROR("Setting CPU affinity failed: "
"%d", rc);
} else
thr->cmd_thread = kthread_create(scst_cmd_thread,
cmd_threads, "scstd%d", n++);
@@ -2182,6 +2190,7 @@ static int __init init_scst(void)
init_waitqueue_head(&scst_dev_cmd_waitQ);
mutex_init(&scst_suspend_mutex);
INIT_LIST_HEAD(&scst_cmd_threads_list);
cpus_setall(default_cpu_mask);
scst_init_threads(&scst_main_cmd_threads);
+6
View File
@@ -193,6 +193,8 @@ extern spinlock_t scst_mgmt_lock;
extern struct list_head scst_sess_init_list;
extern struct list_head scst_sess_shut_list;
extern struct cpumask default_cpu_mask;
struct scst_cmd_thread_t {
struct task_struct *cmd_thread;
struct list_head thread_list_entry;
@@ -550,6 +552,10 @@ void scst_process_reset(struct scst_device *dev,
bool scst_is_ua_global(const uint8_t *sense, int len);
void scst_requeue_ua(struct scst_cmd *cmd);
struct scst_aen *scst_alloc_aen(struct scst_session *sess,
uint64_t unpacked_lun);
void scst_free_aen(struct scst_aen *aen);
void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
int key, int asc, int ascq);
+228 -1
View File
@@ -123,6 +123,12 @@ static ssize_t scst_tgt_io_grouping_type_show(struct kobject *kobj,
static ssize_t scst_tgt_io_grouping_type_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
static ssize_t scst_tgt_cpu_mask_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf);
static ssize_t scst_tgt_cpu_mask_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf);
@@ -156,6 +162,12 @@ static ssize_t scst_acg_io_grouping_type_show(struct kobject *kobj,
static ssize_t scst_acg_io_grouping_type_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
static ssize_t scst_acg_cpu_mask_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf);
static ssize_t scst_acg_cpu_mask_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
static ssize_t scst_acn_file_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
@@ -964,6 +976,11 @@ static struct kobj_attribute scst_tgt_io_grouping_type =
scst_tgt_io_grouping_type_show,
scst_tgt_io_grouping_type_store);
static struct kobj_attribute scst_tgt_cpu_mask =
__ATTR(cpu_mask, S_IRUGO | S_IWUSR,
scst_tgt_cpu_mask_show,
scst_tgt_cpu_mask_store);
static struct kobj_attribute scst_rel_tgt_id =
__ATTR(rel_tgt_id, S_IRUGO | S_IWUSR, scst_rel_tgt_id_show,
scst_rel_tgt_id_store);
@@ -977,6 +994,11 @@ static struct kobj_attribute scst_acg_io_grouping_type =
scst_acg_io_grouping_type_show,
scst_acg_io_grouping_type_store);
static struct kobj_attribute scst_acg_cpu_mask =
__ATTR(cpu_mask, S_IRUGO | S_IWUSR,
scst_acg_cpu_mask_show,
scst_acg_cpu_mask_store);
static ssize_t scst_tgt_enable_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -1183,6 +1205,13 @@ int scst_tgt_sysfs_create(struct scst_tgt *tgt)
goto out_err;
}
res = sysfs_create_file(&tgt->tgt_kobj, &scst_tgt_cpu_mask.attr);
if (res != 0) {
PRINT_ERROR("Can't add attribute %s for tgt %s",
scst_tgt_cpu_mask.attr.name, tgt->tgt_name);
goto out_err;
}
pattr = tgt->tgtt->tgt_attrs;
if (pattr != NULL) {
while (*pattr != NULL) {
@@ -3143,6 +3172,168 @@ out:
return res;
}
static ssize_t __scst_acg_cpu_mask_show(struct scst_acg *acg, char *buf)
{
int res;
res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE,
&acg->acg_cpu_mask);
if (!cpus_equal(acg->acg_cpu_mask, default_cpu_mask))
res += sprintf(&buf[res], "\n%s\n", SCST_SYSFS_KEY_MARK);
return res;
}
static int __scst_acg_process_cpu_mask_store(struct scst_tgt *tgt,
struct scst_acg *acg, struct cpumask *cpu_mask)
{
int res = 0;
struct scst_session *sess;
TRACE_DBG("tgt %p, acg %p", tgt, acg);
if (mutex_lock_interruptible(&scst_mutex) != 0) {
res = -EINTR;
goto out;
}
/* Check if tgt and acg not already freed while we were coming here */
if (scst_check_tgt_acg_ptrs(tgt, acg) != 0)
goto out_unlock;
cpumask_copy(&acg->acg_cpu_mask, cpu_mask);
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i;
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct scst_tgt_dev *tgt_dev;
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
sess_tgt_dev_list_entry) {
struct scst_cmd_thread_t *thr;
if (tgt_dev->active_cmd_threads != &tgt_dev->tgt_dev_cmd_threads)
continue;
list_for_each_entry(thr,
&tgt_dev->active_cmd_threads->threads_list,
thread_list_entry) {
int rc;
rc = set_cpus_allowed_ptr(thr->cmd_thread, cpu_mask);
if (rc != 0)
PRINT_ERROR("Setting CPU "
"affinity failed: %d", rc);
}
}
}
if (tgt->tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(sess, 0);
if (aen == NULL) {
PRINT_ERROR("Unable to notify target driver %s "
"about cpu_mask change", tgt->tgt_name);
continue;
}
aen->event_fn = SCST_AEN_CPU_MASK_CHANGED;
TRACE_DBG("Calling target's %s report_aen(%p)",
tgt->tgtt->name, aen);
rc = tgt->tgtt->report_aen(aen);
TRACE_DBG("Target's %s report_aen(%p) returned %d",
tgt->tgtt->name, aen, rc);
if (rc != SCST_AEN_RES_SUCCESS)
scst_free_aen(aen);
}
}
out_unlock:
mutex_unlock(&scst_mutex);
out:
return res;
}
static int __scst_acg_cpu_mask_store_work_fn(struct scst_sysfs_work_item *work)
{
return __scst_acg_process_cpu_mask_store(work->tgt, work->acg,
&work->cpu_mask);
}
static ssize_t __scst_acg_cpu_mask_store(struct scst_acg *acg,
const char *buf, size_t count)
{
int res;
struct scst_sysfs_work_item *work;
/* cpumask might be too big for stack */
res = scst_alloc_sysfs_work(__scst_acg_cpu_mask_store_work_fn,
false, &work);
if (res != 0)
goto out;
/*
* We can't use cpumask_parse_user() here, because it expects
* buffer in the user space.
*/
res = __bitmap_parse(buf, count, 0, cpumask_bits(&work->cpu_mask),
nr_cpumask_bits);
if (res != 0) {
PRINT_ERROR("__bitmap_parse() failed: %d", res);
goto out_release;
}
if (cpus_equal(acg->acg_cpu_mask, work->cpu_mask))
goto out;
work->tgt = acg->tgt;
work->acg = acg;
res = scst_sysfs_queue_wait_work(work);
out:
return res;
out_release:
scst_sysfs_work_release(&work->sysfs_work_kref);
goto out;
}
static ssize_t scst_tgt_cpu_mask_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct scst_acg *acg;
struct scst_tgt *tgt;
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
acg = tgt->default_acg;
return __scst_acg_cpu_mask_show(acg, buf);
}
static ssize_t scst_tgt_cpu_mask_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int res;
struct scst_acg *acg;
struct scst_tgt *tgt;
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
acg = tgt->default_acg;
res = __scst_acg_cpu_mask_store(acg, buf, count);
if (res != 0)
goto out;
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* Called with scst_mutex held.
*
@@ -3241,6 +3432,13 @@ int scst_acg_sysfs_create(struct scst_tgt *tgt,
goto out_del;
}
res = sysfs_create_file(&acg->acg_kobj, &scst_acg_cpu_mask.attr);
if (res != 0) {
PRINT_ERROR("Can't add tgt attr %s for tgt %s",
scst_acg_cpu_mask.attr.name, tgt->tgt_name);
goto out_del;
}
out:
TRACE_EXIT_RES(res);
return res;
@@ -3303,6 +3501,35 @@ out:
return res;
}
static ssize_t scst_acg_cpu_mask_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct scst_acg *acg;
acg = container_of(kobj, struct scst_acg, acg_kobj);
return __scst_acg_cpu_mask_show(acg, buf);
}
static ssize_t scst_acg_cpu_mask_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int res;
struct scst_acg *acg;
acg = container_of(kobj, struct scst_acg, acg_kobj);
res = __scst_acg_cpu_mask_store(acg, buf, count);
if (res != 0)
goto out;
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -5199,7 +5426,7 @@ int __init scst_sysfs_init(void)
NULL, "scst_uid");
if (IS_ERR(sysfs_work_thread)) {
res = PTR_ERR(sysfs_work_thread);
PRINT_ERROR("kthread_create() for user interface thread "
PRINT_ERROR("kthread_run() for user interface thread "
"failed: %d", res);
sysfs_work_thread = NULL;
goto out;