Merge branch 'svn-trunk'

This commit is contained in:
Bart Van Assche
2016-05-05 21:08:08 -07:00
5 changed files with 175 additions and 60 deletions
-31
View File
@@ -631,37 +631,6 @@ static inline void *vzalloc(unsigned long size)
}
#endif
/* <linux/workqueue.h> */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
static inline int cancel_delayed_work_sync(struct delayed_work *work)
{
int res;
res = cancel_delayed_work(work);
flush_scheduled_work();
return res;
}
#else
/*
* While cancel_delayed_work_sync() has not been defined in the vanilla kernel
* 2.6.18 nor in 2.6.19 nor in RHEL/CentOS 5.0..5.5, a definition is available
* in RHEL/CentOS 5.6. Unfortunately that definition is incompatible with what
* we need. So define cancel_delayed_work() as a macro such that it overrides
* the RHEL/CentOS 5.6 inline function definition in <linux/workqueue.h>.
*/
#define cancel_delayed_work_sync(work) \
({ \
int res; \
\
res = cancel_delayed_work((work)); \
flush_scheduled_work(); \
res; \
})
#endif
#endif
/* <scsi/scsi_cmnd.h> */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+3
View File
@@ -2127,6 +2127,9 @@ struct scst_cmd {
/* Pointer to lists of commands with the lock */
struct scst_cmd_threads *cmd_threads;
/* Assigned processing thread (for better CPU affinity) */
struct scst_cmd_thread_t *cmd_thr;
atomic_t cmd_ref;
struct scst_session *sess; /* corresponding session */
+20 -11
View File
@@ -64,11 +64,6 @@ Pass-through dev handlers will not work.
/*
* Main SCST mutex. All targets, devices and dev_types management is done
* under this mutex.
*
* It must NOT be used in any works (schedule_work(), etc.), because
* otherwise a deadlock (double lock, actually) is possible, e.g., with
* scst_user detach_tgt(), which is called under scst_mutex and calls
* flush_scheduled_work().
*/
struct mutex scst_mutex;
EXPORT_SYMBOL_GPL(scst_mutex);
@@ -104,6 +99,7 @@ struct kmem_cache *scst_dev_cachep;
struct kmem_cache *scst_tgtd_cachep;
struct kmem_cache *scst_sess_cachep;
struct kmem_cache *scst_acgd_cachep;
static struct kmem_cache *scst_thr_cachep;
#ifdef CONFIG_SCST_PROC
struct list_head scst_acg_list;
@@ -1920,23 +1916,26 @@ int scst_add_threads(struct scst_cmd_threads *cmd_threads,
}
for (i = 0; i < num; i++) {
thr = kzalloc(sizeof(*thr), GFP_KERNEL);
thr = kmem_cache_zalloc(scst_thr_cachep, GFP_KERNEL);
if (!thr) {
res = -ENOMEM;
PRINT_ERROR("Fail to allocate thr %d", res);
goto out_wait;
}
INIT_LIST_HEAD(&thr->thr_active_cmd_list);
spin_lock_init(&thr->thr_cmd_list_lock);
thr->thr_cmd_threads = cmd_threads;
if (dev != NULL) {
thr->cmd_thread = kthread_create(scst_cmd_thread,
cmd_threads, "%.13s%d", dev->virt_name, n++);
thr, "%.13s%d", dev->virt_name, n++);
} else if (tgt_dev != NULL) {
thr->cmd_thread = kthread_create(scst_cmd_thread,
cmd_threads, "%.10s%d_%d",
thr, "%.10s%d_%d",
tgt_dev->dev->virt_name, tgt_dev_num, n++);
} else
thr->cmd_thread = kthread_create(scst_cmd_thread,
cmd_threads, "scstd%d", n++);
thr, "scstd%d", n++);
if (IS_ERR(thr->cmd_thread)) {
res = PTR_ERR(thr->cmd_thread);
@@ -1988,6 +1987,10 @@ out:
return res;
}
/*
* The being stopped threads must not have assigned commands, which usually
* means suspended activities.
*/
void scst_del_threads(struct scst_cmd_threads *cmd_threads, int num)
{
TRACE_ENTRY();
@@ -2016,7 +2019,7 @@ void scst_del_threads(struct scst_cmd_threads *cmd_threads, int num)
if (rc != 0 && rc != -EINTR)
TRACE_MGMT_DBG("kthread_stop() failed: %d", rc);
kfree(ct);
kmem_cache_free(scst_thr_cachep, ct);
}
EXTRACHECKS_BUG_ON((cmd_threads->nr_threads == 0) &&
@@ -2588,12 +2591,14 @@ static int __init init_scst(void)
#endif
if (!INIT_CACHEP(scst_acgd_cachep, scst_acg_dev)) /* read-mostly */
goto out_destroy_tgtd_cache;
if (!INIT_CACHEP_ALIGN(scst_thr_cachep, scst_cmd_thread_t))
goto out_destroy_acg_cache;
scst_mgmt_mempool = mempool_create(64, mempool_alloc_slab,
mempool_free_slab, scst_mgmt_cachep);
if (scst_mgmt_mempool == NULL) {
res = -ENOMEM;
goto out_destroy_acg_cache;
goto out_destroy_thr_cache;
}
/*
@@ -2763,6 +2768,9 @@ out_destroy_mgmt_stub_mempool:
out_destroy_mgmt_mempool:
mempool_destroy(scst_mgmt_mempool);
out_destroy_thr_cache:
kmem_cache_destroy(scst_thr_cachep);
out_destroy_acg_cache:
kmem_cache_destroy(scst_acgd_cachep);
@@ -2855,6 +2863,7 @@ static void __exit exit_scst(void)
DEINIT_CACHEP(scst_dev_cachep);
DEINIT_CACHEP(scst_tgt_cachep);
DEINIT_CACHEP(scst_acgd_cachep);
DEINIT_CACHEP(scst_thr_cachep);
scst_lib_exit();
+4 -1
View File
@@ -213,9 +213,12 @@ extern struct list_head scst_sess_shut_list;
extern cpumask_t default_cpu_mask;
struct scst_cmd_thread_t {
struct list_head thr_active_cmd_list;
spinlock_t thr_cmd_list_lock;
struct task_struct *cmd_thread;
struct scst_cmd_threads *thr_cmd_threads;
struct list_head thread_list_entry;
bool being_stopped;
bool being_stopped;
};
static inline bool scst_set_io_context(struct scst_cmd *cmd,
+148 -17
View File
@@ -1917,18 +1917,32 @@ static void scst_process_redirect_cmd(struct scst_cmd *cmd,
context);
/* go through */
case SCST_CONTEXT_THREAD:
spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags);
{
struct list_head *active_cmd_list;
if (cmd->cmd_thr != NULL) {
TRACE_DBG("Using assigned thread %p for cmd %p",
cmd->cmd_thr, cmd);
active_cmd_list = &cmd->cmd_thr->thr_active_cmd_list;
spin_lock_irqsave(&cmd->cmd_thr->thr_cmd_list_lock, flags);
} else {
active_cmd_list = &cmd->cmd_threads->active_cmd_list;
spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags);
}
TRACE_DBG("Adding cmd %p to active cmd list", cmd);
if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
list_add(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
list_add(&cmd->cmd_list_entry, active_cmd_list);
else
list_add_tail(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
wake_up(&cmd->cmd_threads->cmd_list_waitQ);
spin_unlock_irqrestore(&cmd->cmd_threads->cmd_list_lock, flags);
list_add_tail(&cmd->cmd_list_entry, active_cmd_list);
if (cmd->cmd_thr != NULL) {
wake_up_process(cmd->cmd_thr->cmd_thread);
spin_unlock_irqrestore(&cmd->cmd_thr->thr_cmd_list_lock, flags);
} else {
wake_up(&cmd->cmd_threads->cmd_list_waitQ);
spin_unlock_irqrestore(&cmd->cmd_threads->cmd_list_lock, flags);
}
break;
}
}
TRACE_EXIT();
return;
@@ -5456,17 +5470,20 @@ static void scst_do_job_active(struct list_head *cmd_list,
return;
}
static inline int test_cmd_threads(struct scst_cmd_threads *p_cmd_threads)
static inline int test_cmd_threads(struct scst_cmd_thread_t *thr)
{
int res = !list_empty(&p_cmd_threads->active_cmd_list) ||
unlikely(kthread_should_stop()) ||
tm_dbg_is_release();
int res = !list_empty(&thr->thr_active_cmd_list) ||
!list_empty(&thr->thr_cmd_threads->active_cmd_list) ||
unlikely(kthread_should_stop()) ||
tm_dbg_is_release();
return res;
}
int scst_cmd_thread(void *arg)
{
struct scst_cmd_threads *p_cmd_threads = arg;
struct scst_cmd_thread_t *thr = arg;
struct scst_cmd_threads *p_cmd_threads = thr->thr_cmd_threads;
bool someth_done, p_locked, thr_locked;
TRACE_ENTRY();
@@ -5482,10 +5499,24 @@ int scst_cmd_thread(void *arg)
wake_up_all(&p_cmd_threads->ioctx_wq);
spin_lock_irq(&p_cmd_threads->cmd_list_lock);
spin_lock(&thr->thr_cmd_list_lock);
while (!kthread_should_stop()) {
wait_event_locked(p_cmd_threads->cmd_list_waitQ,
test_cmd_threads(p_cmd_threads), lock_irq,
p_cmd_threads->cmd_list_lock);
if (!test_cmd_threads(thr)) {
DEFINE_WAIT(wait);
do {
prepare_to_wait_exclusive_head(
&p_cmd_threads->cmd_list_waitQ,
&wait, TASK_INTERRUPTIBLE);
if (test_cmd_threads(thr))
break;
spin_unlock(&thr->thr_cmd_list_lock);
spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
schedule();
spin_lock_irq(&p_cmd_threads->cmd_list_lock);
spin_lock(&thr->thr_cmd_list_lock);
} while (!test_cmd_threads(thr));
finish_wait(&p_cmd_threads->cmd_list_waitQ, &wait);
}
if (tm_dbg_is_release()) {
spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
@@ -5493,9 +5524,109 @@ int scst_cmd_thread(void *arg)
spin_lock_irq(&p_cmd_threads->cmd_list_lock);
}
scst_do_job_active(&p_cmd_threads->active_cmd_list,
&p_cmd_threads->cmd_list_lock, false);
/*
* Idea of this code is to have local queue be more prioritized
* comparing to the more global queue as 2:1, as well as the
* local processing not touching the more global data for writes
* during its iterations when the more global queue is empty.
* Why 2:1? 2 is average number of intermediate commands states
* reaching this point here.
*/
p_locked = true;
thr_locked = true;
do {
int thr_cnt;
someth_done = false;
again:
if (!list_empty(&p_cmd_threads->active_cmd_list)) {
struct scst_cmd *cmd;
if (!p_locked) {
if (thr_locked) {
spin_unlock_irq(&thr->thr_cmd_list_lock);
thr_locked = false;
}
spin_lock_irq(&p_cmd_threads->cmd_list_lock);
p_locked = true;
goto again;
}
cmd = list_first_entry(&p_cmd_threads->active_cmd_list,
typeof(*cmd), cmd_list_entry);
TRACE_DBG("Deleting cmd %p from active cmd list", cmd);
list_del(&cmd->cmd_list_entry);
if (thr_locked) {
spin_unlock(&thr->thr_cmd_list_lock);
thr_locked = false;
}
spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
p_locked = false;
if (cmd->cmd_thr == NULL) {
TRACE_DBG("Assigning thread %p on cmd %p",
thr, cmd);
cmd->cmd_thr = thr;
}
scst_process_active_cmd(cmd, false);
someth_done = true;
}
if (thr_locked && p_locked) {
/* We need to maintain order of locks and unlocks */
spin_unlock(&thr->thr_cmd_list_lock);
spin_unlock(&p_cmd_threads->cmd_list_lock);
spin_lock(&thr->thr_cmd_list_lock);
p_locked = false;
} else if (!thr_locked) {
if (p_locked) {
spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
p_locked = false;
}
spin_lock_irq(&thr->thr_cmd_list_lock);
thr_locked = true;
}
thr_cnt = 0;
while (!list_empty(&thr->thr_active_cmd_list)) {
struct scst_cmd *cmd = list_first_entry(
&thr->thr_active_cmd_list,
typeof(*cmd), cmd_list_entry);
TRACE_DBG("Deleting cmd %p from thr active cmd list", cmd);
list_del(&cmd->cmd_list_entry);
spin_unlock_irq(&thr->thr_cmd_list_lock);
thr_locked = false;
scst_process_active_cmd(cmd, false);
someth_done = true;
if (++thr_cnt == 2)
break;
else {
spin_lock_irq(&thr->thr_cmd_list_lock);
thr_locked = true;
}
}
} while (someth_done);
EXTRACHECKS_BUG_ON(p_locked);
if (thr_locked) {
spin_unlock_irq(&thr->thr_cmd_list_lock);
thr_locked = false;
}
spin_lock_irq(&p_cmd_threads->cmd_list_lock);
spin_lock(&thr->thr_cmd_list_lock);
}
spin_unlock(&thr->thr_cmd_list_lock);
spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
scst_ioctx_put(p_cmd_threads);