Introduce asynchronous cache lock
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
238
src/utils/utils_async_lock.c
Normal file
238
src/utils/utils_async_lock.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "utils_async_lock.h"
|
||||
|
||||
struct ocf_async_lock_waiter {
|
||||
struct list_head list;
|
||||
ocf_async_lock_t lock;
|
||||
bool write_lock;
|
||||
ocf_async_lock_end_t cmpl;
|
||||
};
|
||||
|
||||
void _ocf_async_lock_collect_waiters(ocf_async_lock_t lock,
|
||||
struct list_head *waiters)
|
||||
{
|
||||
ocf_async_lock_waiter_t iter, temp;
|
||||
|
||||
list_for_each_entry_safe(iter, temp, &lock->waiters, list) {
|
||||
if (!iter->write_lock) {
|
||||
list_move_tail(&iter->list, waiters);
|
||||
lock->rd++;
|
||||
} else {
|
||||
if (!lock->rd) {
|
||||
list_move_tail(&iter->list, waiters);
|
||||
lock->wr = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _ocf_async_lock_run_waiters(struct ocf_async_lock *lock,
|
||||
struct list_head *waiters, int status)
|
||||
{
|
||||
ocf_async_lock_waiter_t iter, temp;
|
||||
|
||||
/* TODO: Should we run waiters asynchronously? */
|
||||
|
||||
list_for_each_entry_safe(iter, temp, waiters, list) {
|
||||
list_del(&iter->list);
|
||||
iter->cmpl(iter, status);
|
||||
env_vfree(iter);
|
||||
}
|
||||
}
|
||||
|
||||
int ocf_async_lock_init(struct ocf_async_lock *lock, uint32_t waiter_priv_size)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = env_mutex_init(&lock->mutex);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
INIT_LIST_HEAD(&lock->waiters);
|
||||
lock->rd = 0;
|
||||
lock->wr = 0;
|
||||
lock->waiter_priv_size = waiter_priv_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ocf_async_lock_deinit(struct ocf_async_lock *lock)
|
||||
{
|
||||
struct list_head waiters;
|
||||
ocf_async_lock_waiter_t iter, temp;
|
||||
|
||||
INIT_LIST_HEAD(&waiters);
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
list_for_each_entry_safe(iter, temp, &lock->waiters, list)
|
||||
list_move_tail(&iter->list, &waiters);
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
_ocf_async_lock_run_waiters(lock, &waiters, -OCF_ERR_NO_LOCK);
|
||||
}
|
||||
|
||||
ocf_async_lock_waiter_t ocf_async_lock_new_waiter(ocf_async_lock_t lock,
|
||||
ocf_async_lock_end_t cmpl)
|
||||
{
|
||||
ocf_async_lock_waiter_t waiter;
|
||||
|
||||
waiter = env_vmalloc(sizeof(*waiter) + lock->waiter_priv_size);
|
||||
if (!waiter)
|
||||
return NULL;
|
||||
|
||||
waiter->lock = lock;
|
||||
waiter->cmpl = cmpl;
|
||||
|
||||
return waiter;
|
||||
}
|
||||
|
||||
ocf_async_lock_t ocf_async_lock_waiter_get_lock(ocf_async_lock_waiter_t waiter)
|
||||
{
|
||||
return waiter->lock;
|
||||
}
|
||||
|
||||
void *ocf_async_lock_waiter_get_priv(ocf_async_lock_waiter_t waiter)
|
||||
{
|
||||
return (void *)waiter + sizeof(*waiter);
|
||||
}
|
||||
|
||||
static int _ocf_async_trylock(struct ocf_async_lock *lock)
|
||||
{
|
||||
if (lock->wr || lock->rd)
|
||||
return -OCF_ERR_NO_LOCK;
|
||||
|
||||
lock->wr = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ocf_async_lock(ocf_async_lock_waiter_t waiter)
|
||||
{
|
||||
ocf_async_lock_t lock = waiter->lock;
|
||||
int result;
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
|
||||
result = _ocf_async_trylock(lock);
|
||||
if (!result) {
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
waiter->cmpl(waiter, 0);
|
||||
env_vfree(waiter);
|
||||
return;
|
||||
}
|
||||
|
||||
waiter->write_lock = true;
|
||||
list_add_tail(&waiter->list, &lock->waiters);
|
||||
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
}
|
||||
|
||||
int ocf_async_trylock(struct ocf_async_lock *lock)
|
||||
{
|
||||
int result;
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
result = _ocf_async_trylock(lock);
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ocf_async_unlock(struct ocf_async_lock *lock)
|
||||
{
|
||||
struct list_head waiters;
|
||||
|
||||
INIT_LIST_HEAD(&waiters);
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
|
||||
ENV_BUG_ON(lock->rd);
|
||||
ENV_BUG_ON(!lock->wr);
|
||||
|
||||
lock->wr = 0;
|
||||
|
||||
_ocf_async_lock_collect_waiters(lock, &waiters);
|
||||
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
_ocf_async_lock_run_waiters(lock, &waiters, 0);
|
||||
}
|
||||
|
||||
static int _ocf_async_read_trylock(struct ocf_async_lock *lock)
|
||||
{
|
||||
if (lock->wr || !list_empty(&lock->waiters))
|
||||
return -OCF_ERR_NO_LOCK;
|
||||
|
||||
lock->rd++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ocf_async_read_lock(ocf_async_lock_waiter_t waiter)
|
||||
{
|
||||
ocf_async_lock_t lock = waiter->lock;
|
||||
int result;
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
|
||||
result = _ocf_async_read_trylock(lock);
|
||||
if (!result) {
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
waiter->cmpl(waiter, 0);
|
||||
env_vfree(waiter);
|
||||
return;
|
||||
}
|
||||
|
||||
waiter->write_lock = false;
|
||||
list_add_tail(&waiter->list, &lock->waiters);
|
||||
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
}
|
||||
|
||||
int ocf_async_read_trylock(struct ocf_async_lock *lock)
|
||||
{
|
||||
int result;
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
result = _ocf_async_read_trylock(lock);
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ocf_async_read_unlock(struct ocf_async_lock *lock)
|
||||
{
|
||||
struct list_head waiters;
|
||||
|
||||
INIT_LIST_HEAD(&waiters);
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
|
||||
ENV_BUG_ON(!lock->rd);
|
||||
ENV_BUG_ON(lock->wr);
|
||||
|
||||
if (--lock->rd) {
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
_ocf_async_lock_collect_waiters(lock, &waiters);
|
||||
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
_ocf_async_lock_run_waiters(lock, &waiters, 0);
|
||||
}
|
||||
|
||||
bool ocf_async_is_locked(struct ocf_async_lock *lock)
|
||||
{
|
||||
bool locked;
|
||||
|
||||
env_mutex_lock(&lock->mutex);
|
||||
locked = lock->rd || lock->wr;
|
||||
env_mutex_unlock(&lock->mutex);
|
||||
|
||||
return locked;
|
||||
}
|
50
src/utils/utils_async_lock.h
Normal file
50
src/utils/utils_async_lock.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __UTILS_ASYNC_LOCK_H__
|
||||
#define __UTILS_ASYNC_LOCK_H__
|
||||
|
||||
#include "ocf_env.h"
|
||||
|
||||
struct ocf_async_lock {
|
||||
struct list_head waiters;
|
||||
env_mutex mutex;
|
||||
uint32_t rd;
|
||||
uint32_t wr;
|
||||
uint32_t waiter_priv_size;
|
||||
};
|
||||
|
||||
typedef struct ocf_async_lock *ocf_async_lock_t;
|
||||
|
||||
typedef struct ocf_async_lock_waiter *ocf_async_lock_waiter_t;
|
||||
|
||||
typedef void (*ocf_async_lock_end_t)(ocf_async_lock_waiter_t waiter, int error);
|
||||
|
||||
int ocf_async_lock_init(ocf_async_lock_t lock, uint32_t waiter_priv_size);
|
||||
|
||||
void ocf_async_lock_deinit(ocf_async_lock_t lock);
|
||||
|
||||
ocf_async_lock_waiter_t ocf_async_lock_new_waiter(ocf_async_lock_t lock,
|
||||
ocf_async_lock_end_t cmpl);
|
||||
|
||||
ocf_async_lock_t ocf_async_lock_waiter_get_lock(ocf_async_lock_waiter_t waiter);
|
||||
|
||||
void *ocf_async_lock_waiter_get_priv(ocf_async_lock_waiter_t waiter);
|
||||
|
||||
void ocf_async_lock(ocf_async_lock_waiter_t waiter);
|
||||
|
||||
int ocf_async_trylock(struct ocf_async_lock *lock);
|
||||
|
||||
void ocf_async_unlock(struct ocf_async_lock *lock);
|
||||
|
||||
void ocf_async_read_lock(ocf_async_lock_waiter_t waiter);
|
||||
|
||||
int ocf_async_read_trylock(struct ocf_async_lock *lock);
|
||||
|
||||
void ocf_async_read_unlock(struct ocf_async_lock *lock);
|
||||
|
||||
bool ocf_async_is_locked(struct ocf_async_lock *lock);
|
||||
|
||||
#endif /* __UTILS_ASYNC_LOCK_H__ */
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../engine/engine_common.h"
|
||||
|
Reference in New Issue
Block a user