Merge pull request #310 from mmichal10/fix-flush-interrupting

Fix flush interrupting
This commit is contained in:
Michal Rakowski 2020-01-28 21:53:15 +01:00 committed by GitHub
commit 7b0e8168d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -23,6 +23,7 @@ struct _cache_mngt_async_context {
struct completion cmpl;
spinlock_t lock;
int result;
void (*compl_func)(ocf_cache_t cache);
};
/*
@ -87,6 +88,7 @@ static inline void _cache_mngt_async_context_init(
init_completion(&context->cmpl);
spin_lock_init(&context->lock);
context->result = 0;
context->compl_func = NULL;
}
static void _cache_mngt_lock_complete(ocf_cache_t cache, void *priv, int error)
@ -196,19 +198,55 @@ static int _cache_mngt_save_sync(ocf_cache_t cache)
return result;
}
static void _cache_mngt_cache_flush_uninterruptible_complete(ocf_cache_t cache,
void *priv, int error)
{
struct _cache_mngt_sync_context *context = priv;
*context->result = error;
complete(&context->cmpl);
}
/*
* Since wait_for_completion() is used, hang tasks may occure if flush would
* take long time.
*/
static int _cache_mngt_cache_flush_uninterruptible(ocf_cache_t cache)
{
int result;
struct _cache_mngt_sync_context context;
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
init_completion(&context.cmpl);
context.result = &result;
atomic_set(&cache_priv->flush_interrupt_enabled, 0);
ocf_mngt_cache_flush(cache, _cache_mngt_cache_flush_uninterruptible_complete,
&context);
wait_for_completion(&context.cmpl);
atomic_set(&cache_priv->flush_interrupt_enabled, 1);
return result;
}
static void _cache_mngt_cache_flush_complete(ocf_cache_t cache, void *priv,
int error)
{
struct _cache_mngt_async_context *context = priv;
int result;
if (context->compl_func)
context->compl_func(cache);
result = _cache_mngt_async_callee_set_result(context, error);
if (result == -KCAS_ERR_WAITING_INTERRUPTED)
kfree(context);
}
static int _cache_mngt_cache_flush_sync(ocf_cache_t cache, bool interruption)
static int _cache_mngt_cache_flush_sync(ocf_cache_t cache, bool interruption,
void (*compl)(ocf_cache_t cache))
{
int result;
struct _cache_mngt_async_context *context;
@ -219,6 +257,7 @@ static int _cache_mngt_cache_flush_sync(ocf_cache_t cache, bool interruption)
return -ENOMEM;
_cache_mngt_async_context_init(context);
context->compl_func = compl;
atomic_set(&cache_priv->flush_interrupt_enabled, interruption);
ocf_mngt_cache_flush(cache, _cache_mngt_cache_flush_complete, context);
@ -228,6 +267,8 @@ static int _cache_mngt_cache_flush_sync(ocf_cache_t cache, bool interruption)
if (result != -KCAS_ERR_WAITING_INTERRUPTED)
kfree(context);
else if (result == -KCAS_ERR_WAITING_INTERRUPTED && interruption)
ocf_mngt_cache_flush_interrupt(cache);
atomic_set(&cache_priv->flush_interrupt_enabled, 1);
@ -239,6 +280,10 @@ static void _cache_mngt_core_flush_complete(ocf_core_t core, void *priv,
{
struct _cache_mngt_async_context *context = priv;
int result;
ocf_cache_t cache = ocf_core_get_cache(core);
if (context->compl_func)
context->compl_func(cache);
result = _cache_mngt_async_callee_set_result(context, error);
@ -246,7 +291,8 @@ static void _cache_mngt_core_flush_complete(ocf_core_t core, void *priv,
kfree(context);
}
static int _cache_mngt_core_flush_sync(ocf_core_t core, bool interruption)
static int _cache_mngt_core_flush_sync(ocf_core_t core, bool interruption,
void (*compl)(ocf_cache_t cache))
{
int result;
struct _cache_mngt_async_context *context;
@ -258,6 +304,7 @@ static int _cache_mngt_core_flush_sync(ocf_core_t core, bool interruption)
return -ENOMEM;
_cache_mngt_async_context_init(context);
context->compl_func = compl;
atomic_set(&cache_priv->flush_interrupt_enabled, interruption);
ocf_mngt_core_flush(core, _cache_mngt_core_flush_complete, context);
@ -267,6 +314,41 @@ static int _cache_mngt_core_flush_sync(ocf_core_t core, bool interruption)
if (result != -KCAS_ERR_WAITING_INTERRUPTED)
kfree(context);
else if (result == -KCAS_ERR_WAITING_INTERRUPTED && interruption)
ocf_mngt_cache_flush_interrupt(cache);
atomic_set(&cache_priv->flush_interrupt_enabled, 1);
return result;
}
static void _cache_mngt_core_flush_uninterruptible_complete(ocf_core_t core,
void *priv, int error)
{
struct _cache_mngt_sync_context *context = priv;
*context->result = error;
complete(&context->cmpl);
}
/*
* Since wait_for_completion() is used, hang tasks may occure if flush would
* take long time.
*/
static int _cache_mngt_core_flush_uninterruptible(ocf_core_t core)
{
int result;
struct _cache_mngt_sync_context context;
ocf_cache_t cache = ocf_core_get_cache(core);
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
init_completion(&context.cmpl);
context.result = &result;
atomic_set(&cache_priv->flush_interrupt_enabled, 0);
ocf_mngt_core_flush(core, _cache_mngt_core_flush_uninterruptible_complete,
&context);
wait_for_completion(&context.cmpl);
atomic_set(&cache_priv->flush_interrupt_enabled, 1);
@ -466,6 +548,12 @@ static void mark_core_id_free(uint64_t *bitmap, uint16_t core_id)
clear_bit(core_id, (unsigned long *)bitmap);
}
static void _cache_read_unlock_put_cmpl(ocf_cache_t cache)
{
ocf_mngt_cache_read_unlock(cache);
ocf_mngt_cache_put(cache);
}
int cache_mngt_flush_object(const char *cache_name, size_t cache_name_len,
const char *core_name, size_t core_name_len)
{
@ -478,21 +566,22 @@ int cache_mngt_flush_object(const char *cache_name, size_t cache_name_len,
if (result)
return result;
result = _cache_mngt_lock_sync(cache);
result = _cache_mngt_read_lock_sync(cache);
if (result) {
ocf_mngt_cache_put(cache);
return result;
}
result = ocf_core_get_by_name(cache, core_name, core_name_len, &core);
if (result)
goto out;
if (result) {
ocf_mngt_cache_read_unlock(cache);
ocf_mngt_cache_put(cache);
return result;
}
result = _cache_mngt_core_flush_sync(core, true);
result = _cache_mngt_core_flush_sync(core, true,
_cache_read_unlock_put_cmpl);
out:
ocf_mngt_cache_unlock(cache);
ocf_mngt_cache_put(cache);
return result;
}
@ -506,16 +595,15 @@ int cache_mngt_flush_device(const char *cache_name, size_t name_len)
if (result)
return result;
result = _cache_mngt_lock_sync(cache);
result = _cache_mngt_read_lock_sync(cache);
if (result) {
ocf_mngt_cache_put(cache);
return result;
}
result = _cache_mngt_cache_flush_sync(cache, true);
result = _cache_mngt_cache_flush_sync(cache, true,
_cache_read_unlock_put_cmpl);
ocf_mngt_cache_unlock(cache);
ocf_mngt_cache_put(cache);
return result;
}
@ -1071,8 +1159,11 @@ int _cache_mngt_remove_core_prepare(ocf_cache_t cache, ocf_core_t core,
if (!cmd->force_no_flush) {
if (core_active) {
/* Flush core */
flush_result = _cache_mngt_core_flush_sync(core,
flush_interruptible);
if (flush_interruptible)
flush_result = _cache_mngt_core_flush_sync(core,
flush_interruptible, _cache_read_unlock_put_cmpl);
else
flush_result = _cache_mngt_core_flush_uninterruptible(core);
} else if (!ocf_mngt_core_is_dirty(core)) {
/* Clean core is always "flushed" */
flush_result = 0;
@ -1117,20 +1208,28 @@ int cache_mngt_remove_core_from_cache(struct kcas_remove_core *cmd)
if (!cmd->force_no_flush) {
/* First check state and flush data (if requested by user)
under read lock */
result = _cache_mngt_read_lock_sync(cache);
/* Getting cache twice is workaround to make flush error handling easier
and avoid dealing with synchronizing issues */
result = ocf_mngt_cache_get(cache);
if (result)
goto put;
result = _cache_mngt_read_lock_sync(cache);
if (result) {
ocf_mngt_cache_put(cache);
goto put;
}
result = get_core_by_id(cache, cmd->core_id, &core);
if (result < 0)
goto rd_unlock;
if (result < 0) {
ocf_mngt_cache_unlock(cache);
ocf_mngt_cache_put(cache);
goto put;
}
result = _cache_mngt_remove_core_prepare(cache, core, cmd,
false);
if (result)
goto rd_unlock;
ocf_mngt_cache_read_unlock(cache);
goto put;
}
/* Acquire write lock */
@ -1186,11 +1285,6 @@ unlock:
put:
ocf_mngt_cache_put(cache);
return result;
rd_unlock:
ocf_mngt_cache_read_unlock(cache);
ocf_mngt_cache_put(cache);
return result;
}
int cache_mngt_reset_stats(const char *cache_name, size_t cache_name_len,
@ -1921,7 +2015,7 @@ int cache_mngt_set_cache_mode(const char *cache_name, size_t name_len,
goto out;
if (flush) {
result = _cache_mngt_cache_flush_sync(cache, true);
result = _cache_mngt_cache_flush_sync(cache, true, NULL);
if (result) {
ocf_mngt_cache_set_mode(cache, old_mode);
goto out;
@ -1952,7 +2046,6 @@ int cache_mngt_exit_instance(const char *cache_name, size_t name_len, int flush)
ocf_queue_t mngt_queue;
int status, flush_status = 0;
/* Get cache */
status = ocf_mngt_cache_get_by_name(cas_ctx, cache_name,
name_len, &cache);
if (status)
@ -1961,9 +2054,6 @@ int cache_mngt_exit_instance(const char *cache_name, size_t name_len, int flush)
cache_priv = ocf_cache_get_priv(cache);
mngt_queue = cache_priv->mngt_queue;
status = _cache_mngt_read_lock_sync(cache);
if (status)
goto put;
/*
* Flush cache. Flushing may take a long time, so we allow user
* to interrupt this operation. Hence we do first flush before
@ -1974,11 +2064,24 @@ int cache_mngt_exit_instance(const char *cache_name, size_t name_len, int flush)
* exported object. The second flush should be much faster.
*/
if (flush) {
status = _cache_mngt_cache_flush_sync(cache, true);
/* Getting cache twice is workaround to make flush error handling easier
and avoid dealing with synchronizing issues */
status = ocf_mngt_cache_get(cache);
if (status)
goto put;
status = _cache_mngt_read_lock_sync(cache);
if (status) {
ocf_mngt_cache_put(cache);
goto put;
}
status = _cache_mngt_cache_flush_sync(cache, true,
_cache_read_unlock_put_cmpl);
switch (status) {
case -OCF_ERR_CACHE_IN_INCOMPLETE_STATE:
case -OCF_ERR_FLUSHING_INTERRUPTED:
ocf_mngt_cache_read_unlock(cache);
case -KCAS_ERR_WAITING_INTERRUPTED:
goto put;
default:
flush_status = status;
@ -1986,8 +2089,6 @@ int cache_mngt_exit_instance(const char *cache_name, size_t name_len, int flush)
}
}
ocf_mngt_cache_read_unlock(cache);
/* get cache write lock */
status = _cache_mngt_lock_sync(cache);
if (status)
@ -2015,7 +2116,7 @@ int cache_mngt_exit_instance(const char *cache_name, size_t name_len, int flush)
/* Flush cache again. This time we don't allow interruption. */
if (flush)
flush_status = _cache_mngt_cache_flush_sync(cache, false);
flush_status = _cache_mngt_cache_flush_uninterruptible(cache);
if (flush && !flush_status)
BUG_ON(ocf_mngt_cache_is_dirty(cache));