ocf/src/utils/utils_async_lock.c
Rafal Stefanowski 3cc0d07197 License change to be approved by contributors
Signed-off-by: Rafal Stefanowski <rafal.stefanowski@intel.com>
2021-10-27 12:48:20 +02:00

241 lines
4.7 KiB
C

/*
* Copyright(c) 2019-2021 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#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 err = 0;
err = env_spinlock_init(&lock->waiters_lock);
if (err)
return err;
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_spinlock_lock(&lock->waiters_lock);
list_for_each_entry_safe(iter, temp, &lock->waiters, list)
list_move_tail(&iter->list, &waiters);
env_spinlock_unlock(&lock->waiters_lock);
env_spinlock_destroy(&lock->waiters_lock);
_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_spinlock_lock(&lock->waiters_lock);
result = _ocf_async_trylock(lock);
if (!result) {
env_spinlock_unlock(&lock->waiters_lock);
waiter->cmpl(waiter, 0);
env_vfree(waiter);
return;
}
waiter->write_lock = true;
list_add_tail(&waiter->list, &lock->waiters);
env_spinlock_unlock(&lock->waiters_lock);
}
int ocf_async_trylock(struct ocf_async_lock *lock)
{
int result;
env_spinlock_lock(&lock->waiters_lock);
result = _ocf_async_trylock(lock);
env_spinlock_unlock(&lock->waiters_lock);
return result;
}
void ocf_async_unlock(struct ocf_async_lock *lock)
{
struct list_head waiters;
INIT_LIST_HEAD(&waiters);
env_spinlock_lock(&lock->waiters_lock);
ENV_BUG_ON(lock->rd);
ENV_BUG_ON(!lock->wr);
lock->wr = 0;
_ocf_async_lock_collect_waiters(lock, &waiters);
env_spinlock_unlock(&lock->waiters_lock);
_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_spinlock_lock(&lock->waiters_lock);
result = _ocf_async_read_trylock(lock);
if (!result) {
env_spinlock_unlock(&lock->waiters_lock);
waiter->cmpl(waiter, 0);
env_vfree(waiter);
return;
}
waiter->write_lock = false;
list_add_tail(&waiter->list, &lock->waiters);
env_spinlock_unlock(&lock->waiters_lock);
}
int ocf_async_read_trylock(struct ocf_async_lock *lock)
{
int result;
env_spinlock_lock(&lock->waiters_lock);
result = _ocf_async_read_trylock(lock);
env_spinlock_unlock(&lock->waiters_lock);
return result;
}
void ocf_async_read_unlock(struct ocf_async_lock *lock)
{
struct list_head waiters;
INIT_LIST_HEAD(&waiters);
env_spinlock_lock(&lock->waiters_lock);
ENV_BUG_ON(!lock->rd);
ENV_BUG_ON(lock->wr);
if (--lock->rd) {
env_spinlock_unlock(&lock->waiters_lock);
return;
}
_ocf_async_lock_collect_waiters(lock, &waiters);
env_spinlock_unlock(&lock->waiters_lock);
_ocf_async_lock_run_waiters(lock, &waiters, 0);
}
bool ocf_async_is_locked(struct ocf_async_lock *lock)
{
bool locked;
env_spinlock_lock(&lock->waiters_lock);
locked = lock->rd || lock->wr;
env_spinlock_unlock(&lock->waiters_lock);
return locked;
}