
After marking thread as ready to stop, CAS was waiting this for thread to exit out of main execution loop (in _cas_io_queue_thread()). In case of management queue it lead to deadlock because both stoping queue and main execution loop was performed in the same execution context. Since freeing memory is the only operation after stopping thread, it can be moved just after the main thread loop. After this little reordering, synchronising between _cas_stop_thread() and _cas_io_queue_thread() in no longer needed, and no deadlock will occur. This change is needed to put management qeueue from completion context. Without this cachnge, there will be no possiblitiy to stop cache from completion context and to make rollback. Signed-off-by: Michal Mielewczyk <michal.mielewczyk@intel.com>
302 lines
6.4 KiB
C
302 lines
6.4 KiB
C
/*
|
|
* Copyright(c) 2012-2019 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
*/
|
|
|
|
#include "threads.h"
|
|
#include "cas_cache.h"
|
|
|
|
#define MAX_THREAD_NAME_SIZE 48
|
|
|
|
struct cas_thread_info {
|
|
atomic_t stop;
|
|
struct completion compl;
|
|
struct completion sync_compl;
|
|
void *sync_data;
|
|
wait_queue_head_t wq;
|
|
atomic_t kicked;
|
|
struct task_struct *thread;
|
|
char name[MAX_THREAD_NAME_SIZE];
|
|
bool running;
|
|
};
|
|
|
|
static int _cas_io_queue_thread(void *data)
|
|
{
|
|
ocf_queue_t q = data;
|
|
struct cas_thread_info *info;
|
|
|
|
BUG_ON(!q);
|
|
|
|
/* complete the creation of the thread */
|
|
info = ocf_queue_get_priv(q);
|
|
BUG_ON(!info);
|
|
|
|
CAS_DAEMONIZE(info->thread->comm);
|
|
|
|
complete(&info->compl);
|
|
|
|
/* Continue working until signaled to exit. */
|
|
do {
|
|
/* Wait until there are completed read misses from the HDDs,
|
|
* or a stop.
|
|
*/
|
|
wait_event_interruptible(info->wq, ocf_queue_pending_io(q) ||
|
|
atomic_read(&info->stop));
|
|
|
|
ocf_queue_run(q);
|
|
|
|
} while (!atomic_read(&info->stop) || ocf_queue_pending_io(q));
|
|
|
|
WARN(ocf_queue_pending_io(q), "Still pending IO requests\n");
|
|
|
|
/* If we get here, then thread was signalled to terminate.
|
|
* So, let's free memory and exit.
|
|
*/
|
|
printk(KERN_DEBUG "Thread %s stopped\n", info->name);
|
|
kfree(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _cas_cleaner_complete(ocf_cleaner_t c, uint32_t interval)
|
|
{
|
|
struct cas_thread_info *info = ocf_cleaner_get_priv(c);
|
|
uint32_t *ms = info->sync_data;
|
|
|
|
*ms = interval;
|
|
complete(&info->sync_compl);
|
|
}
|
|
|
|
static int _cas_cleaner_thread(void *data)
|
|
{
|
|
ocf_cleaner_t c = data;
|
|
ocf_cache_t cache = ocf_cleaner_get_cache(c);
|
|
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
|
|
struct cas_thread_info *info;
|
|
uint32_t ms;
|
|
|
|
BUG_ON(!c);
|
|
|
|
ENV_BUG_ON(!cache_priv);
|
|
/* complete the creation of the thread */
|
|
info = ocf_cleaner_get_priv(c);
|
|
BUG_ON(!info);
|
|
|
|
CAS_DAEMONIZE(info->thread->comm);
|
|
|
|
complete(&info->compl);
|
|
|
|
info->sync_data = &ms;
|
|
ocf_cleaner_set_cmpl(c, _cas_cleaner_complete);
|
|
|
|
do {
|
|
if (atomic_read(&info->stop))
|
|
break;
|
|
|
|
atomic_set(&info->kicked, 0);
|
|
init_completion(&info->sync_compl);
|
|
ocf_cleaner_run(c, cache_priv->io_queues[smp_processor_id()]);
|
|
wait_for_completion(&info->sync_compl);
|
|
|
|
/*
|
|
* In case of nop cleaning policy we don't want to perform cleaning
|
|
* until cleaner_kick() is called.
|
|
*/
|
|
if (ms == OCF_CLEANER_DISABLE) {
|
|
wait_event_interruptible(info->wq, atomic_read(&info->kicked) ||
|
|
atomic_read(&info->stop));
|
|
} else {
|
|
wait_event_interruptible_timeout(info->wq,
|
|
atomic_read(&info->kicked) || atomic_read(&info->stop),
|
|
msecs_to_jiffies(ms));
|
|
}
|
|
} while (true);
|
|
|
|
complete_and_exit(&info->compl, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _cas_metadata_updater_thread(void *data)
|
|
{
|
|
ocf_metadata_updater_t mu = data;
|
|
struct cas_thread_info *info;
|
|
|
|
BUG_ON(!mu);
|
|
|
|
/* complete the creation of the thread */
|
|
info = ocf_metadata_updater_get_priv(mu);
|
|
BUG_ON(!info);
|
|
|
|
CAS_DAEMONIZE(info->thread->comm);
|
|
|
|
complete(&info->compl);
|
|
|
|
do {
|
|
if (atomic_read(&info->stop))
|
|
break;
|
|
|
|
atomic_set(&info->kicked, 0);
|
|
if (ocf_metadata_updater_run(mu))
|
|
continue;
|
|
|
|
wait_event_interruptible(info->wq, atomic_read(&info->stop) ||
|
|
atomic_read(&info->kicked));
|
|
} while (true);
|
|
|
|
complete_and_exit(&info->compl, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _cas_create_thread(struct cas_thread_info **pinfo,
|
|
int (*threadfn)(void *), void *priv, int cpu,
|
|
const char *fmt, ...)
|
|
{
|
|
struct cas_thread_info *info;
|
|
struct task_struct *thread;
|
|
va_list args;
|
|
|
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
if (!info)
|
|
return -ENOMEM;
|
|
|
|
atomic_set(&info->stop, 0);
|
|
init_completion(&info->compl);
|
|
init_completion(&info->sync_compl);
|
|
init_waitqueue_head(&info->wq);
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(info->name, sizeof(info->name), fmt, args);
|
|
va_end(args);
|
|
|
|
thread = kthread_create(threadfn, priv, "%s", info->name);
|
|
if (IS_ERR(thread)) {
|
|
kfree(info);
|
|
/* Propagate error code as PTR_ERR */
|
|
return PTR_ERR(thread);
|
|
}
|
|
info->thread = thread;
|
|
|
|
/* Affinitize thread to core */
|
|
if (cpu != CAS_CPUS_ALL)
|
|
kthread_bind(thread, cpu);
|
|
|
|
if (pinfo)
|
|
*pinfo = info;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void _cas_start_thread(struct cas_thread_info *info)
|
|
{
|
|
wake_up_process(info->thread);
|
|
wait_for_completion(&info->compl);
|
|
|
|
info->running = true;
|
|
|
|
printk(KERN_DEBUG "Thread %s started\n", info->name);
|
|
}
|
|
|
|
static void _cas_stop_thread(struct cas_thread_info *info)
|
|
{
|
|
if (info->running && info->thread) {
|
|
atomic_set(&info->stop, 1);
|
|
wake_up(&info->wq);
|
|
}
|
|
}
|
|
|
|
int cas_create_queue_thread(ocf_queue_t q, int cpu)
|
|
{
|
|
struct cas_thread_info *info;
|
|
ocf_cache_t cache = ocf_queue_get_cache(q);
|
|
int result;
|
|
|
|
result = _cas_create_thread(&info, _cas_io_queue_thread, q, cpu,
|
|
"cas_io_%s_%d", ocf_cache_get_name(cache), cpu);
|
|
if (!result) {
|
|
ocf_queue_set_priv(q, info);
|
|
_cas_start_thread(info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void cas_kick_queue_thread(ocf_queue_t q)
|
|
{
|
|
struct cas_thread_info *info = ocf_queue_get_priv(q);
|
|
wake_up(&info->wq);
|
|
}
|
|
|
|
|
|
void cas_stop_queue_thread(ocf_queue_t q)
|
|
{
|
|
struct cas_thread_info *info = ocf_queue_get_priv(q);
|
|
ocf_queue_set_priv(q, NULL);
|
|
_cas_stop_thread(info);
|
|
}
|
|
|
|
int cas_create_cleaner_thread(ocf_cleaner_t c)
|
|
{
|
|
struct cas_thread_info *info;
|
|
ocf_cache_t cache = ocf_cleaner_get_cache(c);
|
|
int result;
|
|
|
|
result = _cas_create_thread(&info, _cas_cleaner_thread, c,
|
|
CAS_CPUS_ALL, "cas_clean_%s",
|
|
ocf_cache_get_name(cache));
|
|
if (!result) {
|
|
ocf_cleaner_set_priv(c, info);
|
|
_cas_start_thread(info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void cas_kick_cleaner_thread(ocf_cleaner_t c)
|
|
{
|
|
struct cas_thread_info *info = ocf_cleaner_get_priv(c);
|
|
atomic_set(&info->kicked, 1);
|
|
wake_up(&info->wq);
|
|
}
|
|
|
|
void cas_stop_cleaner_thread(ocf_cleaner_t c)
|
|
{
|
|
struct cas_thread_info *info = ocf_cleaner_get_priv(c);
|
|
ocf_cleaner_set_priv(c, NULL);
|
|
_cas_stop_thread(info);
|
|
}
|
|
|
|
int cas_create_metadata_updater_thread(ocf_metadata_updater_t mu)
|
|
{
|
|
struct cas_thread_info *info;
|
|
int result;
|
|
|
|
result = _cas_create_thread(&info, _cas_metadata_updater_thread,
|
|
mu, CAS_CPUS_ALL, "ocf_metadata_updater_%s",
|
|
ocf_cache_get_name(ocf_metadata_updater_get_cache(mu)));
|
|
if (!result) {
|
|
ocf_metadata_updater_set_priv(mu, info);
|
|
_cas_start_thread(info);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void cas_kick_metadata_updater_thread(ocf_metadata_updater_t mu)
|
|
{
|
|
struct cas_thread_info *info = ocf_metadata_updater_get_priv(mu);
|
|
atomic_set(&info->kicked, 1);
|
|
wake_up(&info->wq);
|
|
}
|
|
|
|
|
|
void cas_stop_metadata_updater_thread(ocf_metadata_updater_t mu)
|
|
{
|
|
struct cas_thread_info *info = ocf_metadata_updater_get_priv(mu);
|
|
ocf_metadata_updater_set_priv(mu, NULL);
|
|
_cas_stop_thread(info);
|
|
}
|
|
|