
Currently Open CAS doesn't support kernels with involuntary preemption anyways and once we add the support, we can get rid of this workaround Signed-off-by: Michal Mielewczyk <michal.mielewczyk@huawei.com>
275 lines
6.9 KiB
C
275 lines
6.9 KiB
C
/*
|
|
* Copyright(c) 2012-2022 Intel Corporation
|
|
* Copyright(c) 2024-2025 Huawei Technologies
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "ocf/ocf.h"
|
|
#include "utils_rpool.h"
|
|
#include "ocf_env.h"
|
|
#include "../cas_cache.h"
|
|
#include <linux/kmemleak.h>
|
|
|
|
#define CAS_UTILS_RPOOL_DEBUG 0
|
|
#if 1 == CAS_UTILS_RPOOL_DEBUG
|
|
#define CAS_DEBUG_TRACE() \
|
|
printk(KERN_INFO "[Utils][RPOOL] %s\n", __func__)
|
|
|
|
#define CAS_DEBUG_MSG(msg) \
|
|
printk(KERN_INFO "[Utils][RPOOL] %s - %s\n", __func__, msg)
|
|
|
|
#define CAS_DEBUG_PARAM(format, ...) \
|
|
printk(KERN_INFO "[Utils][RPOOL] %s - "format"\n", \
|
|
__func__, ##__VA_ARGS__)
|
|
#else
|
|
#define CAS_DEBUG_TRACE()
|
|
#define CAS_DEBUG_MSG(msg)
|
|
#define CAS_DEBUG_PARAM(format, ...)
|
|
#endif
|
|
|
|
/* This is currently 24B padded/force aligned to 32B.
|
|
* With a 64B cacheline this means two structs on different cores may
|
|
* invalidate each other. This shouldn't happen between different physical
|
|
* CPUs and cause false sharing though, since with an even number of cores
|
|
* per CPU same cacheline shouldn't be polluted from the other physical CPU.
|
|
* */
|
|
struct _cas_reserve_pool_per_cpu {
|
|
spinlock_t lock;
|
|
struct list_head list;
|
|
atomic_t count;
|
|
} __attribute__((__aligned__(32)));
|
|
|
|
struct cas_reserve_pool {
|
|
uint32_t limit;
|
|
uint32_t entry_size;
|
|
char *name;
|
|
struct _cas_reserve_pool_per_cpu *rpools;
|
|
};
|
|
|
|
struct _cas_rpool_pre_alloc_info {
|
|
struct work_struct ws;
|
|
struct completion cmpl;
|
|
struct cas_reserve_pool *rpool_master;
|
|
cas_rpool_new rpool_new;
|
|
void *allocator_ctx;
|
|
int error;
|
|
};
|
|
|
|
#define RPOOL_ITEM_TO_ENTRY(rpool, item) \
|
|
(void *)((unsigned long)item + sizeof(struct list_head) \
|
|
- rpool->entry_size)
|
|
|
|
#define RPOOL_ENTRY_TO_ITEM(rpool, entry) \
|
|
(struct list_head *)((unsigned long)entry + rpool->entry_size \
|
|
- sizeof(struct list_head))
|
|
|
|
static void _cas_rpool_pre_alloc_do(struct work_struct *ws)
|
|
{
|
|
struct _cas_rpool_pre_alloc_info *info =
|
|
container_of(ws, struct _cas_rpool_pre_alloc_info, ws);
|
|
struct cas_reserve_pool *rpool_master = info->rpool_master;
|
|
struct _cas_reserve_pool_per_cpu *current_rpool;
|
|
struct list_head *item;
|
|
void *entry;
|
|
int i, cpu;
|
|
|
|
CAS_DEBUG_TRACE();
|
|
|
|
cpu = smp_processor_id();
|
|
current_rpool = &rpool_master->rpools[cpu];
|
|
|
|
for (i = 0; i < rpool_master->limit; i++) {
|
|
entry = info->rpool_new(info->allocator_ctx, cpu);
|
|
if (!entry) {
|
|
info->error = -ENOMEM;
|
|
complete(&info->cmpl);
|
|
return;
|
|
}
|
|
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
|
|
list_add_tail(item, ¤t_rpool->list);
|
|
atomic_inc(¤t_rpool->count);
|
|
}
|
|
|
|
CAS_DEBUG_PARAM("Added [%d] pre allocated items to reserve poll [%s]"
|
|
" for cpu %d", atomic_read(¤t_rpool->count),
|
|
rpool_master->name, cpu);
|
|
|
|
complete(&info->cmpl);
|
|
}
|
|
|
|
void cas_rpool_destroy(struct cas_reserve_pool *rpool_master,
|
|
cas_rpool_del rpool_del, void *allocator_ctx)
|
|
{
|
|
int i, cpu_no = num_online_cpus();
|
|
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
|
struct list_head *item = NULL, *next = NULL;
|
|
void *entry;
|
|
|
|
CAS_DEBUG_TRACE();
|
|
|
|
if (!rpool_master)
|
|
return;
|
|
|
|
if (!rpool_master->rpools) {
|
|
kfree(rpool_master);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < cpu_no; i++) {
|
|
current_rpool = &rpool_master->rpools[i];
|
|
|
|
CAS_DEBUG_PARAM("Destroyed reserve poll [%s] for cpu %d",
|
|
rpool_master->name, i);
|
|
|
|
if (!atomic_read(¤t_rpool->count))
|
|
continue;
|
|
|
|
list_for_each_safe(item, next, ¤t_rpool->list) {
|
|
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
|
|
list_del(item);
|
|
rpool_del(allocator_ctx, entry);
|
|
atomic_dec(¤t_rpool->count);
|
|
}
|
|
|
|
if (atomic_read(¤t_rpool->count)) {
|
|
printk(KERN_CRIT "Not all object from reserve poll"
|
|
"[%s] deallocated\n", rpool_master->name);
|
|
WARN(true, OCF_PREFIX_SHORT" Cleanup problem\n");
|
|
}
|
|
}
|
|
|
|
kfree(rpool_master->rpools);
|
|
kfree(rpool_master);
|
|
}
|
|
|
|
struct cas_reserve_pool *cas_rpool_create(uint32_t limit, char *name,
|
|
uint32_t entry_size, cas_rpool_new rpool_new,
|
|
cas_rpool_del rpool_del, void *allocator_ctx)
|
|
{
|
|
int i, cpu_no = num_online_cpus();
|
|
struct cas_reserve_pool *rpool_master = NULL;
|
|
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
|
struct _cas_rpool_pre_alloc_info info;
|
|
|
|
CAS_DEBUG_TRACE();
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
rpool_master = kzalloc(sizeof(*rpool_master), GFP_KERNEL);
|
|
if (!rpool_master)
|
|
goto error;
|
|
|
|
rpool_master->rpools = kzalloc(sizeof(*rpool_master->rpools) * cpu_no,
|
|
GFP_KERNEL);
|
|
if (!rpool_master->rpools)
|
|
goto error;
|
|
|
|
rpool_master->limit = limit;
|
|
rpool_master->name = name;
|
|
rpool_master->entry_size = entry_size;
|
|
|
|
info.rpool_master = rpool_master;
|
|
info.rpool_new = rpool_new;
|
|
info.allocator_ctx = allocator_ctx;
|
|
|
|
for (i = 0; i < cpu_no; i++) {
|
|
current_rpool = &rpool_master->rpools[i];
|
|
spin_lock_init(¤t_rpool->lock);
|
|
INIT_LIST_HEAD(¤t_rpool->list);
|
|
|
|
init_completion(&info.cmpl);
|
|
INIT_WORK_ONSTACK(&info.ws, _cas_rpool_pre_alloc_do);
|
|
schedule_work_on(i, &info.ws);
|
|
schedule();
|
|
|
|
wait_for_completion(&info.cmpl);
|
|
if (info.error)
|
|
goto error;
|
|
|
|
CAS_DEBUG_PARAM("Created reserve poll [%s] for cpu %d",
|
|
rpool_master->name, i);
|
|
}
|
|
|
|
return rpool_master;
|
|
error:
|
|
|
|
cas_rpool_destroy(rpool_master, rpool_del, allocator_ctx);
|
|
return NULL;
|
|
}
|
|
|
|
#define LIST_FIRST_ITEM(head) head.next
|
|
|
|
void *cas_rpool_try_get(struct cas_reserve_pool *rpool_master, int *cpu)
|
|
{
|
|
unsigned long flags;
|
|
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
|
struct list_head *item = NULL;
|
|
void *entry = NULL;
|
|
|
|
CAS_DEBUG_TRACE();
|
|
|
|
get_cpu();
|
|
|
|
*cpu = smp_processor_id();
|
|
current_rpool = &rpool_master->rpools[*cpu];
|
|
|
|
put_cpu();
|
|
|
|
spin_lock_irqsave(¤t_rpool->lock, flags);
|
|
|
|
if (!list_empty(¤t_rpool->list)) {
|
|
item = LIST_FIRST_ITEM(current_rpool->list);
|
|
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
|
|
list_del(item);
|
|
atomic_dec(¤t_rpool->count);
|
|
}
|
|
|
|
spin_unlock_irqrestore(¤t_rpool->lock, flags);
|
|
|
|
if (entry) {
|
|
/* The actual allocation - kmemleak should start tracking page */
|
|
kmemleak_alloc(entry, rpool_master->entry_size, 1, GFP_NOIO);
|
|
}
|
|
|
|
CAS_DEBUG_PARAM("[%s]Removed item from reserve pool [%s] for cpu [%d], "
|
|
"items in pool %d", rpool_master->name,
|
|
item == NULL ? "SKIPPED" : "OK", *cpu,
|
|
atomic_read(¤t_rpool->count));
|
|
|
|
return entry;
|
|
}
|
|
|
|
int cas_rpool_try_put(struct cas_reserve_pool *rpool_master, void *entry, int cpu)
|
|
{
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
|
struct list_head *item;
|
|
|
|
CAS_DEBUG_TRACE();
|
|
|
|
current_rpool = &rpool_master->rpools[cpu];
|
|
|
|
spin_lock_irqsave(¤t_rpool->lock, flags);
|
|
|
|
if (atomic_read(¤t_rpool->count) >= rpool_master->limit) {
|
|
ret = 1;
|
|
goto error;
|
|
}
|
|
|
|
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
|
|
list_add_tail(item, ¤t_rpool->list);
|
|
/* Freeing memory chunk */
|
|
kmemleak_free(entry);
|
|
|
|
atomic_inc(¤t_rpool->count);
|
|
|
|
error:
|
|
CAS_DEBUG_PARAM("[%s]Added item to reserve pool [%s] for cpu [%d], "
|
|
"items in pool %d", rpool_master->name,
|
|
ret == 1 ? "SKIPPED" : "OK", cpu,
|
|
atomic_read(¤t_rpool->count));
|
|
spin_unlock_irqrestore(¤t_rpool->lock, flags);
|
|
return ret;
|
|
}
|