Initial commit
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
13
modules/cas_cache/utils/cas_cache_utils.h
Normal file
13
modules/cas_cache/utils/cas_cache_utils.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CAS_UTILS_H__
|
||||
#define __CAS_UTILS_H__
|
||||
|
||||
#include "utils_nvme.h"
|
||||
#include "utils_properties.h"
|
||||
|
||||
#endif /* __CAS_UTILS_H__ */
|
22
modules/cas_cache/utils/utils_blk.c
Normal file
22
modules/cas_cache/utils/utils_blk.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "utils_blk.h"
|
||||
|
||||
int cas_blk_get_part_count(struct block_device *bdev)
|
||||
{
|
||||
struct disk_part_tbl *ptbl;
|
||||
int i, count = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
ptbl = rcu_dereference(bdev->bd_disk->part_tbl);
|
||||
for (i = 0; i < ptbl->len; ++i) {
|
||||
if (rcu_access_pointer(ptbl->part[i]))
|
||||
count++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return count;
|
||||
}
|
14
modules/cas_cache/utils/utils_blk.h
Normal file
14
modules/cas_cache/utils/utils_blk.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_BLK_H_
|
||||
#define UTILS_BLK_H_
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/genhd.h>
|
||||
|
||||
int cas_blk_get_part_count(struct block_device *bdev);
|
||||
|
||||
#endif /* UTILS_BLK_H_ */
|
130
modules/cas_cache/utils/utils_data.c
Normal file
130
modules/cas_cache/utils/utils_data.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
/**
|
||||
* This function locates index of IO vec from given vecs array where byte at
|
||||
* offset is located. When found it returns its index and byte offset within
|
||||
* this vec.
|
||||
* @param vecs IO vector array to be searched
|
||||
* @param vec_num number of items in IO vector array
|
||||
* @param offset byte offset to be found
|
||||
* @param offset_in_vec byte offset within found IO vec
|
||||
* @return vec index if it lies within specified buffer, otherwise -1
|
||||
*/
|
||||
static int get_starting_vec(struct bio_vec *vecs, uint64_t vecs_num,
|
||||
uint64_t offset, uint64_t *offset_in_vec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vecs_num; i++) {
|
||||
if (vecs[i].bv_len > offset) {
|
||||
if (offset_in_vec != NULL)
|
||||
*offset_in_vec = offset;
|
||||
return i;
|
||||
}
|
||||
offset -= vecs[i].bv_len;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t cas_data_cpy(struct bio_vec *dst, uint64_t dst_num,
|
||||
struct bio_vec *src, uint64_t src_num,
|
||||
uint64_t to, uint64_t from, uint64_t bytes)
|
||||
{
|
||||
uint64_t i, j, dst_len, src_len, to_copy;
|
||||
uint64_t dst_off, src_off;
|
||||
uint64_t written = 0;
|
||||
int ret;
|
||||
void *dst_p, *src_p;
|
||||
struct bio_vec *curr_dst, *curr_src;
|
||||
|
||||
/* Locate vec idx and offset in dst vec array */
|
||||
ret = get_starting_vec(dst, dst_num, to, &to);
|
||||
if (ret < 0) {
|
||||
CAS_PRINT_RL(KERN_INFO "llu dst buffer too small "
|
||||
"to_offset=%llu bytes=%llu", to, bytes);
|
||||
return 0;
|
||||
}
|
||||
j = ret;
|
||||
|
||||
/* Locate vec idx and offset in src vec array */
|
||||
ret = get_starting_vec(src, src_num, from, &from);
|
||||
if (ret < 0) {
|
||||
CAS_PRINT_RL(KERN_INFO "llu src buffer too small "
|
||||
"from_offset=%llu bytes=%llu", from, bytes);
|
||||
return 0;
|
||||
}
|
||||
i = ret;
|
||||
|
||||
curr_dst = &dst[j];
|
||||
curr_src = &src[i];
|
||||
|
||||
dst_off = curr_dst->bv_offset + to;
|
||||
dst_len = curr_dst->bv_len - to;
|
||||
|
||||
src_off = curr_src->bv_offset + from;
|
||||
src_len = curr_src->bv_len - from;
|
||||
|
||||
while (written < bytes) {
|
||||
dst_p = page_address(curr_dst->bv_page) + dst_off;
|
||||
src_p = page_address(curr_src->bv_page) + src_off;
|
||||
|
||||
to_copy = src_len > dst_len ? dst_len : src_len;
|
||||
|
||||
/* Prevent from copying too much*/
|
||||
if ((written + to_copy) > bytes)
|
||||
to_copy = bytes - written;
|
||||
|
||||
memcpy(dst_p, src_p, to_copy);
|
||||
written += to_copy;
|
||||
|
||||
if (written == bytes)
|
||||
break;
|
||||
|
||||
/* Setup new len and offset. */
|
||||
dst_off += to_copy;
|
||||
dst_len -= to_copy;
|
||||
|
||||
src_off += to_copy;
|
||||
src_len -= to_copy;
|
||||
|
||||
/* Go to next src buffer */
|
||||
if (src_len == 0) {
|
||||
i++;
|
||||
|
||||
/* Setup new len and offset. */
|
||||
if (i < src_num) {
|
||||
curr_src = &src[i];
|
||||
src_off = curr_src->bv_offset;
|
||||
src_len = curr_src->bv_len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Go to next dst buffer */
|
||||
if (dst_len == 0) {
|
||||
j++;
|
||||
|
||||
if (j < dst_num) {
|
||||
curr_dst = &dst[j];
|
||||
dst_off = curr_dst->bv_offset;
|
||||
dst_len = curr_dst->bv_len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (written != bytes) {
|
||||
CAS_PRINT_RL(KERN_INFO "Written bytes not equal requested bytes "
|
||||
"(written=%llu; requested=%llu)", written, bytes);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
31
modules/cas_cache/utils/utils_data.h
Normal file
31
modules/cas_cache/utils/utils_data.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_DATA_H_
|
||||
#define UTILS_DATA_H_
|
||||
|
||||
/**
|
||||
* @brief Copy data from a data vector to another one
|
||||
*
|
||||
* This function copies number of bytes from source IO vector to destination
|
||||
* IO vector. It starts coping to specified offset in destination IO vector. If
|
||||
* there is not enough space it will return number of bytes that was
|
||||
* successfully copied.
|
||||
*
|
||||
* @param dst destination IO vector
|
||||
* @param dst_num size of destination IO vector
|
||||
* @param src source IO vector
|
||||
* @param src_num size of source IO vector
|
||||
* @param to dst offset where write to will start
|
||||
* @param from src offset where write from will start
|
||||
* @param bytes number of bytes to be copied
|
||||
*
|
||||
* @return number of bytes written from src to dst
|
||||
*/
|
||||
uint64_t cas_data_cpy(struct bio_vec *dst, uint64_t dst_num,
|
||||
struct bio_vec *src, uint64_t src_num,
|
||||
uint64_t to, uint64_t from, uint64_t bytes);
|
||||
|
||||
#endif /* UTILS_DATA_H_ */
|
78
modules/cas_cache/utils/utils_gc.c
Normal file
78
modules/cas_cache/utils/utils_gc.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "utils_gc.h"
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#if defined (CAS_GARBAGE_COLLECTOR)
|
||||
struct cas_vfree_item {
|
||||
struct llist_head list;
|
||||
struct work_struct ws;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cas_vfree_item, cas_vfree_item);
|
||||
|
||||
static atomic_t freed = ATOMIC_INIT(0);
|
||||
|
||||
static void cas_garbage_collector(struct work_struct *w)
|
||||
{
|
||||
struct cas_vfree_item *item = container_of(w, struct cas_vfree_item,
|
||||
ws);
|
||||
struct llist_node *llnode = llist_del_all(&item->list);
|
||||
|
||||
while (llnode) {
|
||||
void *item = llnode;
|
||||
|
||||
llnode = llnode->next;
|
||||
atomic_dec(&freed);
|
||||
vfree(item);
|
||||
}
|
||||
}
|
||||
|
||||
void cas_vfree(const void *addr)
|
||||
{
|
||||
struct cas_vfree_item *item = this_cpu_ptr(&cas_vfree_item);
|
||||
|
||||
atomic_inc(&freed);
|
||||
|
||||
if (llist_add((struct llist_node *)addr, &item->list))
|
||||
schedule_work(&item->ws);
|
||||
}
|
||||
|
||||
void cas_garbage_collector_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
struct cas_vfree_item *item;
|
||||
|
||||
item = &per_cpu(cas_vfree_item, i);
|
||||
init_llist_head(&item->list);
|
||||
INIT_WORK(&item->ws, cas_garbage_collector);
|
||||
}
|
||||
}
|
||||
|
||||
void cas_garbage_collector_deinit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
struct cas_vfree_item *item;
|
||||
|
||||
item = &per_cpu(cas_vfree_item, i);
|
||||
while (work_pending(&item->ws))
|
||||
schedule();
|
||||
}
|
||||
|
||||
WARN(atomic_read(&freed) != 0,
|
||||
OCF_PREFIX_SHORT" Not all memory deallocated\n");
|
||||
}
|
||||
#else
|
||||
void cas_garbage_collector_init(void) {};
|
||||
|
||||
void cas_garbage_collector_deinit(void) {};
|
||||
|
||||
void cas_vfree(const void *addr) { vfree(addr); };
|
||||
#endif
|
16
modules/cas_cache/utils/utils_gc.h
Normal file
16
modules/cas_cache/utils/utils_gc.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_GC_H_
|
||||
#define UTILS_GC_H_
|
||||
|
||||
|
||||
void cas_garbage_collector_init(void);
|
||||
|
||||
void cas_garbage_collector_deinit(void);
|
||||
|
||||
void cas_vfree(const void *addr);
|
||||
|
||||
#endif /* UTILS_GC_H_ */
|
583
modules/cas_cache/utils/utils_nvme.c
Normal file
583
modules/cas_cache/utils/utils_nvme.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#if defined(CAS_NVME_PARTIAL)
|
||||
|
||||
#include "cas_cache.h"
|
||||
#include "utils_nvme.h"
|
||||
#include "utils_blk.h"
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
|
||||
int cas_nvme_get_nsid(struct block_device *bdev, unsigned int *nsid)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Maximum NSID is 0xFFFFFFFF, so theoretically there is no free
|
||||
* room for error code. However it's unlikely that there will ever
|
||||
* be device with such number of namespaces, so we treat this value
|
||||
* as it was signed. Then in case of negative value we interpret it
|
||||
* as an error code. Moreover in case of error we can be sure, that
|
||||
* we deal with non-NVMe device, because this ioctl should never
|
||||
* fail with NVMe driver.
|
||||
*/
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ID, (unsigned long)NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*nsid = (unsigned int)ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NVME_ID_CNS_NS 0x00
|
||||
#define NVME_ID_CNS_CTRL 0x01
|
||||
|
||||
int cas_nvme_identify_ns(struct block_device *bdev, unsigned int nsid,
|
||||
struct nvme_id_ns *ns)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ns));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = cpu_to_le32(nsid);
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*ns);
|
||||
cmd.cdw10 = NVME_ID_CNS_NS;
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(ns, (void *)buffer, sizeof(*ns));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*ns));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cas_nvme_identify_ns_contorller(struct file *file, struct nvme_id_ns *ns)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
mm_segment_t old_fs;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ns));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = 1;
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*ns);
|
||||
cmd.cdw10 = NVME_ID_CNS_NS;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = file->f_op->unlocked_ioctl(file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(ns, (void *)buffer, sizeof(*ns));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*ns));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CAS_NVME_FULL)
|
||||
|
||||
#define FORMAT_WORKAROUND_NOT_NEED 0
|
||||
#define FORMAT_WORKAROUND_NEED 1
|
||||
|
||||
static int __cas_nvme_check_fw(struct nvme_id_ctrl *id_ctrl)
|
||||
{
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice. We need to compare
|
||||
* only 5 last characters.
|
||||
*/
|
||||
|
||||
return (strncmp(&id_ctrl->fr[3], "101H0", 5) < 0) ?
|
||||
FORMAT_WORKAROUND_NEED :
|
||||
FORMAT_WORKAROUND_NOT_NEED;
|
||||
}
|
||||
|
||||
int cas_nvme_identify_ctrl(struct block_device *bdev,
|
||||
struct nvme_id_ctrl *id_ctrl)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*id_ctrl));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*id_ctrl);
|
||||
cmd.cdw10 = NVME_ID_CNS_CTRL;
|
||||
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(id_ctrl, (void *)buffer, sizeof(*id_ctrl));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*id_ctrl));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_bdev(struct block_device *bdev, unsigned int nsid,
|
||||
int lbaf, int ms)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
|
||||
cmd.opcode = nvme_admin_format_nvm;
|
||||
cmd.nsid = nsid;
|
||||
cmd.cdw10 = lbaf | ms<<4;
|
||||
cmd.timeout_ms = 1200000;
|
||||
return ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
}
|
||||
|
||||
static int _cas_nvme_controller_identify(struct file *character_device_file,
|
||||
unsigned long __user buffer)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = 0;
|
||||
cmd.addr = (__u64)buffer;
|
||||
/* 1 - identify contorller, 0 - identify namespace */
|
||||
cmd.cdw10 = 1;
|
||||
cmd.data_len = 0x1000;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = character_device_file->f_op->unlocked_ioctl(character_device_file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_controller(struct file *character_device_file,
|
||||
int lbaf, bool sbnsupp)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
/* Send format command to device */
|
||||
cmd.opcode = nvme_admin_format_nvm;
|
||||
cmd.nsid = 0xFFFFFFFF;
|
||||
cmd.cdw10 = lbaf | sbnsupp << 4;
|
||||
cmd.timeout_ms = 120000;
|
||||
cmd.addr = 0;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = character_device_file->f_op->unlocked_ioctl(character_device_file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int find_lbaf(struct nvme_lbaf *lbaf, int cnt, int atomic)
|
||||
{
|
||||
int ms = atomic ? 8 : 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= cnt; ++i)
|
||||
if (lbaf[i].ms == ms && lbaf[i].ds == 9)
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* context for async probe */
|
||||
struct _probe_context
|
||||
{
|
||||
struct completion cmpl;
|
||||
struct ocf_metadata_probe_status status;
|
||||
int error;
|
||||
};
|
||||
|
||||
static void _cas_nvme_probe_cmpl(void *priv, int error,
|
||||
struct ocf_metadata_probe_status *status)
|
||||
{
|
||||
struct _probe_context *ctx = (struct _probe_context*)priv;
|
||||
|
||||
ctx->error = error;
|
||||
if (!error) {
|
||||
ctx->status = *status;
|
||||
}
|
||||
|
||||
complete(&ctx->cmpl);
|
||||
}
|
||||
|
||||
static int _cas_nvme_preformat_check(struct block_device *bdev, int force)
|
||||
{
|
||||
ocf_volume_t volume;
|
||||
struct _probe_context probe_ctx;
|
||||
int ret = 0;
|
||||
|
||||
if (bdev != bdev->bd_contains)
|
||||
return -KCAS_ERR_A_PART;
|
||||
|
||||
if (cas_blk_get_part_count(bdev) > 1 && !force)
|
||||
return -KCAS_ERR_CONTAINS_PART;
|
||||
|
||||
ret = cas_blk_open_volume_by_bdev(&volume, bdev);
|
||||
if (ret == -KCAS_ERR_NVME_BAD_FORMAT) {
|
||||
/* Current format is not supported by CAS, so we can be sure
|
||||
* that there is no dirty data. Do format
|
||||
*/
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
/* An error occurred, stop processing */
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&probe_ctx.cmpl);
|
||||
ocf_metadata_probe(cas_ctx, volume, _cas_nvme_probe_cmpl, &probe_ctx);
|
||||
if (wait_for_completion_interruptible(&probe_ctx.cmpl)) {
|
||||
ocf_volume_close(volume);
|
||||
return -OCF_ERR_FLUSHING_INTERRUPTED;
|
||||
}
|
||||
|
||||
if (probe_ctx.error == -ENODATA) {
|
||||
/* Cache was not detected on this device
|
||||
* NVMe can be formated
|
||||
*/
|
||||
ret = 0;
|
||||
} else if (probe_ctx.error == -EBUSY) {
|
||||
ret = -OCF_ERR_NOT_OPEN_EXC;
|
||||
} else if (probe_ctx.error) {
|
||||
/* Some error occurred, we do not have sure about clean cache */
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
} else {
|
||||
/* Check if cache was closed in proper way */
|
||||
if (!probe_ctx.status.clean_shutdown ||
|
||||
probe_ctx.status.cache_dirty) {
|
||||
/* Dirty shutdown */
|
||||
ret = -KCAS_ERR_DIRTY_EXISTS_NVME;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
/* Force overwrites dirty shutdown */
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ocf_volume_close(volume);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_namespace_by_path(const char *device_path,
|
||||
int metadata_mode, int force)
|
||||
{
|
||||
struct nvme_id_ns *ns;
|
||||
struct nvme_id_ctrl *id;
|
||||
|
||||
unsigned int nsid, sbnsupp = 0;
|
||||
int best_lbaf = 0;
|
||||
int ret = 0;
|
||||
struct block_device *bdev;
|
||||
char holder[] = "CAS FORMAT\n";
|
||||
|
||||
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
|
||||
if (!ns)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
id = kmalloc(sizeof(*id), GFP_KERNEL);
|
||||
if (!id) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
bdev = OPEN_BDEV_EXCLUSIVE(device_path,
|
||||
FMODE_READ | FMODE_WRITE | FMODE_EXCL, holder);
|
||||
if (IS_ERR(bdev)) {
|
||||
if (PTR_ERR(bdev) == -EBUSY)
|
||||
ret = -OCF_ERR_NOT_OPEN_EXC;
|
||||
else
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = cas_nvme_get_nsid(bdev, &nsid);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_NOT_NVME;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_preformat_check(bdev, force);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = cas_nvme_identify_ns(bdev, nsid, ns);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (metadata_mode == CAS_METADATA_MODE_NORMAL) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 0);
|
||||
sbnsupp = 0;
|
||||
} else if (metadata_mode == CAS_METADATA_MODE_ATOMIC) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 1);
|
||||
sbnsupp = !(ns->mc & (1<<1));
|
||||
}
|
||||
|
||||
if (best_lbaf < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = cas_nvme_identify_ctrl(bdev, id);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (__cas_nvme_check_fw(id) == FORMAT_WORKAROUND_NEED) {
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice.
|
||||
*/
|
||||
ret = _cas_nvme_format_bdev(bdev, nsid, best_lbaf, sbnsupp);
|
||||
if (ret)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_format_bdev(bdev, nsid, best_lbaf, sbnsupp);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = ioctl_by_bdev(bdev, BLKRRPART, (unsigned long)NULL);
|
||||
out2:
|
||||
CLOSE_BDEV_EXCLUSIVE(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
|
||||
out1:
|
||||
kfree(id);
|
||||
kfree(ns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_get_bdev_from_controller(struct block_device **bdev,
|
||||
int major, int minor, int namespace_number)
|
||||
{
|
||||
mm_segment_t old_fs;
|
||||
char *sys_path;
|
||||
struct file *file;
|
||||
char readbuffer[12] = {0};
|
||||
char holder[] = "CAS FORMAT\n";
|
||||
int ret = 0;
|
||||
|
||||
sys_path = kzalloc(sizeof(char)*MAX_STR_LEN, GFP_KERNEL);
|
||||
if (!sys_path)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
sprintf(sys_path, "/sys/dev/char/%d:%d/nvme%dn%d/dev",
|
||||
major, minor, minor, namespace_number);
|
||||
|
||||
file = filp_open(sys_path, O_RDONLY, 0);
|
||||
kfree(sys_path);
|
||||
if (IS_ERR(file))
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = file->f_op->read(file, readbuffer, sizeof(readbuffer),
|
||||
&file->f_pos);
|
||||
set_fs(old_fs);
|
||||
filp_close(file, 0);
|
||||
if (ret < 0)
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
ret = sscanf(readbuffer, "%d:%d", &major, &minor);
|
||||
if (ret < 0)
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
*bdev = blkdev_get_by_dev(MKDEV(major, minor),
|
||||
FMODE_READ | FMODE_WRITE | FMODE_EXCL, holder);
|
||||
if (IS_ERR(*bdev))
|
||||
return -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_character_device(const char *device_path,
|
||||
int metadata_mode, int force)
|
||||
{
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
struct file *character_device_file = NULL;
|
||||
struct nvme_id_ctrl *ctrl;
|
||||
unsigned long __user buffer;
|
||||
struct kstat *stat;
|
||||
struct block_device **ndev = NULL;
|
||||
int i;
|
||||
struct nvme_id_ns *ns;
|
||||
int best_lbaf = 0;
|
||||
int sbnsupp = 0;
|
||||
|
||||
ctrl = kzalloc(sizeof(struct nvme_id_ctrl), GFP_KERNEL);
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ctrl));
|
||||
stat = kmalloc(sizeof(struct kstat), GFP_KERNEL);
|
||||
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
if (!ctrl || !buffer || !stat || !ns) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
character_device_file = filp_open(device_path, O_RDWR | O_EXCL, 0);
|
||||
if (IS_ERR(character_device_file)) {
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_controller_identify(character_device_file, buffer);
|
||||
if (ret < 0) {
|
||||
ret = KCAS_ERR_FORMAT_FAILED;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = copy_from_user(ctrl, (void *)buffer, sizeof(*ctrl));
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
ndev = kmalloc_array(ctrl->nn, sizeof(struct block_device), GFP_KERNEL);
|
||||
if (!ndev) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = vfs_stat(device_path, stat);
|
||||
set_fs(old_fs);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
for (i = 1; i <= ctrl->nn; i++) {
|
||||
ret = _cas_nvme_get_bdev_from_controller(&ndev[i-1],
|
||||
MAJOR(stat->rdev), MINOR(stat->rdev), i);
|
||||
if (ret) {
|
||||
i--;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_preformat_check(ndev[i-1], force);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = cas_nvme_identify_ns_contorller(character_device_file, ns);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
if (metadata_mode == CAS_METADATA_MODE_NORMAL) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 0);
|
||||
sbnsupp = 0;
|
||||
} else if (metadata_mode == CAS_METADATA_MODE_ATOMIC) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 1);
|
||||
sbnsupp = !(ns->mc & (1<<1));
|
||||
}
|
||||
|
||||
if (best_lbaf < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (__cas_nvme_check_fw(ctrl) == FORMAT_WORKAROUND_NEED) {
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice.
|
||||
*/
|
||||
ret = _cas_nvme_format_controller(character_device_file,
|
||||
best_lbaf, sbnsupp);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _cas_nvme_format_controller(character_device_file,
|
||||
best_lbaf, sbnsupp);
|
||||
if (ret < 0)
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
cleanup:
|
||||
for (i = i-1; i >= 1; i--) {
|
||||
ret |= ioctl_by_bdev(ndev[i-1], BLKRRPART, (unsigned long)NULL);
|
||||
blkdev_put(ndev[i-1], FMODE_READ | FMODE_WRITE | FMODE_EXCL);
|
||||
}
|
||||
|
||||
out1:
|
||||
kfree(ndev);
|
||||
kfree(ctrl);
|
||||
kfree(stat);
|
||||
|
||||
kfree(ns);
|
||||
cas_vm_munmap(buffer, sizeof(buffer));
|
||||
filp_close(character_device_file, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cas_nvme_format_optimal(const char *device_path, int metadata_mode,
|
||||
int force)
|
||||
{
|
||||
int ret;
|
||||
uint8_t type;
|
||||
|
||||
ret = cas_blk_identify_type(device_path, &type);
|
||||
if (ret == -OCF_ERR_INVAL_VOLUME_TYPE) {
|
||||
/* An error occurred, stop processing */
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type == BLOCK_DEVICE_VOLUME || type == ATOMIC_DEVICE_VOLUME) {
|
||||
ret = _cas_nvme_format_namespace_by_path(device_path,
|
||||
metadata_mode, force);
|
||||
} else if (type == NVME_CONTROLLER && false) {
|
||||
/*
|
||||
* TODO(rbaldyga): Make it safe with NVMe drives that do not
|
||||
* handle format change properly.
|
||||
*/
|
||||
ret = _cas_nvme_format_character_device(device_path,
|
||||
metadata_mode, force);
|
||||
} else {
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
38
modules/cas_cache/utils/utils_nvme.h
Normal file
38
modules/cas_cache/utils/utils_nvme.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_NVME_H_
|
||||
#define UTILS_NVME_H_
|
||||
|
||||
#if defined(CAS_UAPI_NVME)
|
||||
#include <uapi/nvme.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_UAPI_LINUX_NVME)
|
||||
#include <uapi/linux/nvme.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_UAPI_LINUX_NVME_IOCTL)
|
||||
#include <uapi/linux/nvme_ioctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_NVME_PARTIAL)
|
||||
|
||||
#include <linux/nvme.h>
|
||||
|
||||
int cas_nvme_get_nsid(struct block_device *bdev, unsigned int *nsid);
|
||||
int cas_nvme_identify_ns(struct block_device *bdev, unsigned int nsid,
|
||||
struct nvme_id_ns *ns);
|
||||
|
||||
#if defined(CAS_NVME_FULL)
|
||||
|
||||
int cas_nvme_format_optimal(const char *device_path, int metadata_mode,
|
||||
int force);
|
||||
|
||||
#endif /* CAS_NVME_FULL */
|
||||
|
||||
#endif /* CAS_NVME_PARTIAL */
|
||||
|
||||
#endif /* UTILS_NVME_H_ */
|
769
modules/cas_cache/utils/utils_properties.c
Normal file
769
modules/cas_cache/utils/utils_properties.c
Normal file
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
#define INTERNAL_CALL 0
|
||||
#define EXTERNAL_CALL 1
|
||||
|
||||
#define CAS_PROPERTIES_VERSION 101
|
||||
|
||||
#define VERSION_STR ".version"
|
||||
|
||||
/*
|
||||
* Difference between constant and non constant entry is store in LSB
|
||||
* e.g.:
|
||||
* cas_property_string in binary 0000 1010
|
||||
* cas_property_string_const in binary 0000 1011
|
||||
*/
|
||||
|
||||
#define CAS_PROP_UNCONST(type) (type & ~CAS_PROPERTIES_CONST)
|
||||
#define CAS_PROP_CHECK_CONST(type) (type & CAS_PROPERTIES_CONST)
|
||||
|
||||
enum cas_property_type {
|
||||
cas_property_string = 10,
|
||||
cas_property_string_const =
|
||||
(cas_property_string | CAS_PROPERTIES_CONST),
|
||||
cas_property_sint = 16,
|
||||
cas_property_sint_const = (cas_property_sint | CAS_PROPERTIES_CONST),
|
||||
cas_property_uint = 74,
|
||||
cas_property_uint_const = (cas_property_uint | CAS_PROPERTIES_CONST),
|
||||
};
|
||||
|
||||
struct cas_properties {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct _cas_property {
|
||||
uint8_t type;
|
||||
char *key;
|
||||
struct list_head item;
|
||||
union {
|
||||
void *value;
|
||||
uint64_t value_uint;
|
||||
int64_t value_sint;
|
||||
};
|
||||
};
|
||||
|
||||
struct cas_properties *cas_properties_create(void)
|
||||
{
|
||||
struct cas_properties *props;
|
||||
int result;
|
||||
|
||||
props = kzalloc(sizeof(*props), GFP_KERNEL);
|
||||
if (!props)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&props->list);
|
||||
|
||||
result = cas_properties_add_uint(props, VERSION_STR,
|
||||
CAS_PROPERTIES_VERSION, CAS_PROPERTIES_CONST);
|
||||
if (result) {
|
||||
kfree(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
result = cas_properties_add_uint(props, ".size", 0,
|
||||
CAS_PROPERTIES_NON_CONST);
|
||||
if (result) {
|
||||
kfree(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void cas_properties_destroy(struct cas_properties *props)
|
||||
{
|
||||
struct list_head *curr, *tmp;
|
||||
struct _cas_property *entry;
|
||||
|
||||
list_for_each_safe(curr, tmp, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
list_del(curr);
|
||||
if (cas_property_string == CAS_PROP_UNCONST(entry->type))
|
||||
kfree(entry->value);
|
||||
kfree(entry->key);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
kfree(props);
|
||||
}
|
||||
|
||||
static uint64_t _cas_prop_get_size(struct cas_properties *props)
|
||||
{
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
uint64_t size_all = 0;
|
||||
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
|
||||
size_all += cas_prop_strnlen(entry->key, MAX_STRING_SIZE) + 1;
|
||||
size_all += sizeof(entry->type);
|
||||
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
size_all += cas_prop_strnlen(entry->value,
|
||||
MAX_STRING_SIZE) + 1;
|
||||
break;
|
||||
case cas_property_sint:
|
||||
size_all += sizeof(entry->value_sint);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
size_all += sizeof(entry->value_uint);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return size_all;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_string(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, char *value)
|
||||
{
|
||||
uint64_t str_size = 0;
|
||||
|
||||
str_size = cas_prop_strnlen(value, MAX_STRING_SIZE) + 1;
|
||||
|
||||
if ((*offset + str_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, value, str_size);
|
||||
*offset += str_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_string(const char *buffer, const uint64_t size,
|
||||
uint64_t *offset, char **str)
|
||||
{
|
||||
char *tmp_str = NULL;
|
||||
uint64_t str_size = 0;
|
||||
|
||||
if (*offset >= size)
|
||||
return -ENOMEM;
|
||||
|
||||
str_size = cas_prop_strnlen(&buffer[*offset], size - *offset ) + 1;
|
||||
|
||||
if (str_size > size - *offset) {
|
||||
/* no null terminator at the end of buffer */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tmp_str = kstrdup(&buffer[*offset], GFP_KERNEL);
|
||||
if (!tmp_str)
|
||||
return -ENOMEM;
|
||||
|
||||
*offset += str_size;
|
||||
*str = tmp_str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_int(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, uint64_t number)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
/*
|
||||
* To prevent issue connected with byte order we
|
||||
* serialize integer byte by byte.
|
||||
*/
|
||||
for (i = 0; i < sizeof(number); i++) {
|
||||
char byte = number & 0xFF;
|
||||
|
||||
if (*offset < size)
|
||||
buffer[*offset] = byte;
|
||||
else
|
||||
return -ENOMEM;
|
||||
|
||||
(*offset)++;
|
||||
number = number >> 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_uint(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, uint64_t number)
|
||||
{
|
||||
return _cas_prop_serialize_int(buffer, size, offset, number);
|
||||
}
|
||||
|
||||
|
||||
static int _cas_prop_serialize_sint(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, int64_t number)
|
||||
{
|
||||
return _cas_prop_serialize_int(buffer, size, offset, (uint64_t) number);
|
||||
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_int(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, uint64_t *number)
|
||||
{
|
||||
int32_t i;
|
||||
uint64_t byte;
|
||||
|
||||
*number = 0;
|
||||
|
||||
/*
|
||||
* To prevent issue connected with byte order we
|
||||
* parse integer byte by byte.
|
||||
*/
|
||||
for (i = 0; i < sizeof(*number); i++) {
|
||||
if (*offset >= size)
|
||||
return -ENOMEM;
|
||||
|
||||
byte = buffer[*offset] & 0xFF;
|
||||
byte = byte << (i * 8);
|
||||
|
||||
*number |= byte;
|
||||
|
||||
(*offset)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_uint(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, uint64_t *number)
|
||||
{
|
||||
return _cas_prop_parse_int(buffer, size, offset, number);
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_sint(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, int64_t *number)
|
||||
{
|
||||
return _cas_prop_parse_int(buffer, size, offset, (uint64_t *) number);
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize(struct _cas_property *entry, void *buffer,
|
||||
const uint64_t size, uint64_t *offset)
|
||||
{
|
||||
uint64_t item_size = 0;
|
||||
void *item;
|
||||
int result = 0;
|
||||
|
||||
if (*offset > size)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Each entry is represented in buffer in order as below
|
||||
* (e.g. in case we have entry with integer) :
|
||||
* <----- entry ----->
|
||||
* <- key -><-type-><- integer ->
|
||||
* <- X bytes -><1 byte><- 8 byte ->
|
||||
* | | | |
|
||||
*/
|
||||
|
||||
/*
|
||||
* First step - serialize key
|
||||
*/
|
||||
|
||||
item_size = cas_prop_strnlen(entry->key, MAX_STRING_SIZE) + 1;
|
||||
item = entry->key;
|
||||
|
||||
if ((*offset + item_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, item, item_size);
|
||||
*offset += item_size;
|
||||
|
||||
/*
|
||||
* Second step - serialize type
|
||||
*/
|
||||
|
||||
item_size = sizeof(entry->type);
|
||||
item = &entry->type;
|
||||
|
||||
if ((*offset + item_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, item, item_size);
|
||||
*offset += item_size;
|
||||
|
||||
/*
|
||||
* Third step - serialize value
|
||||
*/
|
||||
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
/* Serialize string */
|
||||
result = _cas_prop_serialize_string(buffer, size, offset,
|
||||
entry->value);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
/* Serialize signed integer */
|
||||
result = _cas_prop_serialize_sint(buffer, size, offset,
|
||||
entry->value_uint);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
/* Serialize unsigned integer */
|
||||
result = _cas_prop_serialize_uint(buffer, size, offset,
|
||||
entry->value_uint);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int cas_properties_serialize(struct cas_properties *props,
|
||||
struct casdsk_props_conf *caches_serialized_conf)
|
||||
{
|
||||
int result = 0;
|
||||
uint64_t offset = 0, size;
|
||||
uint16_t crc = 0;
|
||||
void *buffer;
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
|
||||
size = _cas_prop_get_size(props);
|
||||
if (size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
buffer = vzalloc(size);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Update first entry on list - size of buffer
|
||||
*/
|
||||
result = cas_properties_add_uint(props, ".size", size,
|
||||
CAS_PROPERTIES_CONST);
|
||||
if (result)
|
||||
goto error_after_buffer_allocation;
|
||||
|
||||
/*
|
||||
* Serialize each entry, one by one
|
||||
*/
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
result = _cas_prop_serialize(entry, buffer, size, &offset);
|
||||
if (result)
|
||||
goto error_after_buffer_allocation;
|
||||
}
|
||||
|
||||
crc = crc16(0, buffer, size);
|
||||
|
||||
caches_serialized_conf->buffer = buffer;
|
||||
caches_serialized_conf->size = size;
|
||||
caches_serialized_conf->crc = crc;
|
||||
return result;
|
||||
|
||||
error_after_buffer_allocation:
|
||||
vfree(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
void cas_properties_print(struct cas_properties *props)
|
||||
{
|
||||
int result = 0;
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
char *abc;
|
||||
|
||||
/*
|
||||
* Serialize each entry, one by one
|
||||
*/
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
printk(KERN_DEBUG "[Upgrade] Key: %s", entry->key);
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
printk(", string, ");
|
||||
abc = (char *)entry->value;
|
||||
printk("Value: %s ", abc);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
break;
|
||||
case cas_property_uint:
|
||||
printk(", uint, ");
|
||||
printk("Value: %llu ", entry->value_uint);
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_version(const char *buffer, uint64_t *offset,
|
||||
uint64_t *version, int trigger)
|
||||
{
|
||||
int result = 0;
|
||||
char *key = NULL;
|
||||
uint8_t type;
|
||||
|
||||
result = _cas_prop_parse_string(buffer, strlen(VERSION_STR) + 1,
|
||||
offset, &key);
|
||||
if (result)
|
||||
goto error_during_parse_key;
|
||||
|
||||
if (strcmp(VERSION_STR, key)) {
|
||||
result = -EINVAL;
|
||||
goto error_after_parse_key;
|
||||
}
|
||||
|
||||
type = buffer[*offset];
|
||||
if (cas_property_uint_const != type) {
|
||||
result = -EINVAL;
|
||||
goto error_after_parse_key;
|
||||
}
|
||||
*offset += sizeof(type);
|
||||
|
||||
result = _cas_prop_parse_uint(buffer,
|
||||
strlen(VERSION_STR) + 1 + sizeof(type) +
|
||||
sizeof(*version), offset, version);
|
||||
if (result)
|
||||
goto error_after_parse_key;
|
||||
|
||||
/*
|
||||
* In case that is external call
|
||||
* we don't need check version.
|
||||
*/
|
||||
if (trigger == INTERNAL_CALL && *version != CAS_PROPERTIES_VERSION) {
|
||||
printk(KERN_ERR "Version of interface using to parse is "
|
||||
"different than version used to serialize\n");
|
||||
result = -EPERM;
|
||||
}
|
||||
|
||||
error_after_parse_key:
|
||||
kfree(key);
|
||||
error_during_parse_key:
|
||||
return result;
|
||||
}
|
||||
|
||||
int cas_properites_parse_version(struct casdsk_props_conf *caches_serialized_conf,
|
||||
uint64_t *version)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
buffer = (char *) caches_serialized_conf->buffer;
|
||||
if (!buffer)
|
||||
return -EINVAL;
|
||||
|
||||
return _cas_prop_parse_version(buffer, &offset, version, EXTERNAL_CALL);
|
||||
}
|
||||
|
||||
struct cas_properties *
|
||||
cas_properites_parse(struct casdsk_props_conf *caches_serialized_conf)
|
||||
{
|
||||
struct cas_properties *props;
|
||||
char *key = NULL, *value = NULL, *buffer = NULL;
|
||||
int result;
|
||||
uint8_t type;
|
||||
uint64_t uint_value, size = 0, offset = 0, version = 0;
|
||||
uint16_t crc;
|
||||
int64_t sint_value;
|
||||
bool constant = false;
|
||||
|
||||
props = cas_properties_create();
|
||||
if (IS_ERR(props))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!caches_serialized_conf) {
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
buffer = (char *) caches_serialized_conf->buffer;
|
||||
if (!buffer) {
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
size = caches_serialized_conf->size;
|
||||
crc = crc16(0, buffer, size);
|
||||
if (crc != caches_serialized_conf->crc) {
|
||||
printk(KERN_ERR "Cache configuration corrupted");
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse first entry on list - version of interface used to
|
||||
* serialization
|
||||
*/
|
||||
result = _cas_prop_parse_version(buffer, &offset, &version,
|
||||
INTERNAL_CALL);
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
|
||||
while (offset < size) {
|
||||
/*
|
||||
* Parse key of entry
|
||||
*/
|
||||
result = _cas_prop_parse_string(buffer, size, &offset, &key);
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
|
||||
/*
|
||||
* Parse type of entry
|
||||
*/
|
||||
if (offset + sizeof(type) > size) {
|
||||
kfree(key);
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
memcpy(&type, buffer + offset, sizeof(type));
|
||||
offset += sizeof(type);
|
||||
|
||||
constant = CAS_PROP_CHECK_CONST(type);
|
||||
type = CAS_PROP_UNCONST(type);
|
||||
|
||||
switch (type) {
|
||||
case cas_property_string:
|
||||
/* Parse string */
|
||||
result = _cas_prop_parse_string(buffer, size, &offset,
|
||||
&value);
|
||||
if (result)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Add new entry with string to CAS properties instance
|
||||
*/
|
||||
result |= cas_properties_add_string(props, key, value,
|
||||
constant);
|
||||
kfree(value);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
/* Parse signed integer */
|
||||
result = _cas_prop_parse_sint(buffer, size, &offset,
|
||||
&sint_value);
|
||||
/* Add new entry with signed integer to CAS properties
|
||||
* instance
|
||||
*/
|
||||
result |= cas_properties_add_sint(props, key,
|
||||
sint_value, constant);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
/* Parse unsigned integer */
|
||||
result = _cas_prop_parse_uint(buffer, size, &offset,
|
||||
&uint_value);
|
||||
/* Add new entry with unsigned integer to CAS properties
|
||||
* instance
|
||||
*/
|
||||
result |= cas_properties_add_uint(props, key,
|
||||
uint_value, constant);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case when we added new entry,
|
||||
* we not need hold key value longer.
|
||||
*/
|
||||
kfree(key);
|
||||
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
return props;
|
||||
|
||||
error_after_props_allocation:
|
||||
cas_properties_destroy(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
static struct _cas_property *_cas_prop_find(const struct cas_properties *props,
|
||||
const char *key)
|
||||
{
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
if (strncmp(key, entry->key, MAX_STRING_SIZE) == 0)
|
||||
return entry;
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
static struct _cas_property *_cas_prop_alloc_entry_key(const char *key)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
entry->key = kstrdup(key, GFP_KERNEL);
|
||||
if (!entry->key) {
|
||||
kfree(entry);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&entry->item);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADD
|
||||
*/
|
||||
|
||||
int cas_properties_add_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else if (cas_property_uint != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_uint_const : cas_property_uint;
|
||||
entry->value_uint = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_properties_add_sint(struct cas_properties *props, const char *key,
|
||||
int64_t value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else if (cas_property_sint != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_sint_const : cas_property_sint;
|
||||
entry->value_sint = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_properties_add_string(struct cas_properties *props, const char *key,
|
||||
const char *value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
char *tmp_value = NULL;
|
||||
|
||||
tmp_value = kstrdup(value, GFP_KERNEL);
|
||||
if (!tmp_value)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry)) {
|
||||
kfree(tmp_value);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else {
|
||||
if (cas_property_string != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
kfree(tmp_value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kfree(entry->value);
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_string_const :
|
||||
cas_property_string;
|
||||
entry->value = tmp_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GET
|
||||
*/
|
||||
|
||||
int cas_properties_get_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t *value)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_uint ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
*value = entry->value_uint;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
||||
|
||||
int cas_properties_get_sint(struct cas_properties *props, const char *key,
|
||||
int64_t *value)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_sint ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
*value = entry->value_sint;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
||||
|
||||
int cas_properties_get_string(struct cas_properties *props, const char *key,
|
||||
char *value, uint32_t size)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_string ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
/* Check if size of destination memory is enough */
|
||||
if (size < cas_prop_strnlen(entry->value, MAX_STRING_SIZE) + 1)
|
||||
return -ENOMEM;
|
||||
|
||||
cas_prop_strncpy(value, size, entry->value,
|
||||
cas_prop_strnlen(entry->value, MAX_STRING_SIZE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
153
modules/cas_cache/utils/utils_properties.h
Normal file
153
modules/cas_cache/utils/utils_properties.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_PROPERTIES_H_
|
||||
#define UTILS_PROPERTIES_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define cas_prop_strncpy(dest, dest_size, src, src_size) \
|
||||
strlcpy(dest, src, dest_size)
|
||||
#define cas_prop_strnlen(string, size) strnlen(string, size)
|
||||
#else
|
||||
#define cas_prop_strncpy(dest, dest_size, src, src_size) \
|
||||
strncpy(dest, src, src_size)
|
||||
#define cas_prop_strnlen(string, size) strlen(string)
|
||||
#endif
|
||||
|
||||
#include "../../cas_disk/cas_disk.h"
|
||||
|
||||
#define MAX_STRING_SIZE 4095
|
||||
|
||||
#define CAS_PROPERTIES_NON_CONST false
|
||||
#define CAS_PROPERTIES_CONST true
|
||||
|
||||
/**
|
||||
* @file utils_properties.h
|
||||
* @brief CAS cache interface for collect and serialization CAS properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Handler for instance of CAS properties
|
||||
*/
|
||||
struct cas_properties;
|
||||
|
||||
/**
|
||||
* @brief Initialize instance of CAS properties
|
||||
*
|
||||
* @return Handler to instance of interface
|
||||
*/
|
||||
struct cas_properties *cas_properties_create(void);
|
||||
|
||||
/**
|
||||
* @brief De-initialize instance of CAS properties
|
||||
*
|
||||
* @param props Handler to instance to de-initialize
|
||||
*/
|
||||
void cas_properties_destroy(struct cas_properties *props);
|
||||
|
||||
/**
|
||||
* @brief Serialize given CAS properties instance to continuous buffer
|
||||
*
|
||||
* @param props instance of CAS properties
|
||||
* @param idisk conf instance of CAS properties
|
||||
* @return result of serialize CAS properties
|
||||
*/
|
||||
int cas_properties_serialize(struct cas_properties *props,
|
||||
struct casdsk_props_conf *caches_serialized_conf);
|
||||
|
||||
/**
|
||||
* @brief Parse of first entry given continuous buffer to get version of
|
||||
* interface which been used to serialize
|
||||
*
|
||||
* @param buffer pointer to continuous buffer with serialized CAS properties
|
||||
* @param version pointer to memory where we will put version
|
||||
* @return result of getting version, 0 success
|
||||
*/
|
||||
int cas_properites_parse_version(struct casdsk_props_conf *caches_serialized_conf,
|
||||
uint64_t *version);
|
||||
|
||||
/**
|
||||
* @brief Parse of given continuous buffer to CAS properties instance
|
||||
*
|
||||
* @param buffer pointer to continuous buffer with serialized CAS properties
|
||||
* @return handler to CAS properties instance
|
||||
*/
|
||||
struct cas_properties *
|
||||
cas_properites_parse(struct casdsk_props_conf *caches_serialized_conf);
|
||||
|
||||
/**
|
||||
* @brief Add unsigned integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Add signed integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_sint(struct cas_properties *props, const char *key,
|
||||
int64_t value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Add string to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_string(struct cas_properties *props, const char *key,
|
||||
const char *value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Get unsigned integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @return result of getting 0 success
|
||||
*/
|
||||
int cas_properties_get_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get signed integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @return result of getting 0 success
|
||||
*/
|
||||
int cas_properties_get_sint(struct cas_properties *props, const char *key,
|
||||
int64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get string integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @param size size of destination memory
|
||||
* @return result of getting 0 success, 1 error, 2 not enough space
|
||||
* in destination
|
||||
*/
|
||||
int cas_properties_get_string(struct cas_properties *props, const char *key,
|
||||
char *value, uint32_t size);
|
||||
|
||||
|
||||
void cas_properties_print(struct cas_properties *props);
|
||||
#endif /* UTILS_PROPERTIES_H_ */
|
262
modules/cas_cache/utils/utils_rpool.c
Normal file
262
modules/cas_cache/utils/utils_rpool.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "utils_rpool.h"
|
||||
#include "ocf_env.h"
|
||||
#include "../cas_cache.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
|
||||
|
||||
struct _cas_reserve_pool_per_cpu {
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
atomic_t count;
|
||||
};
|
||||
|
||||
struct cas_reserve_pool {
|
||||
uint32_t limit;
|
||||
char *name;
|
||||
uint32_t entry_size;
|
||||
struct _cas_reserve_pool_per_cpu *rpools;
|
||||
};
|
||||
|
||||
struct _cas_rpool_pre_alloc_info {
|
||||
struct work_struct ws;
|
||||
struct cas_reserve_pool *rpool_master;
|
||||
cas_rpool_new rpool_new;
|
||||
void *allocator_ctx;
|
||||
struct completion cmpl;
|
||||
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))
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int _cas_rpool_pre_alloc_schedule(int cpu,
|
||||
struct _cas_rpool_pre_alloc_info *info)
|
||||
{
|
||||
init_completion(&info->cmpl);
|
||||
INIT_WORK(&info->ws, _cas_rpool_pre_alloc_do);
|
||||
schedule_work_on(cpu, &info->ws);
|
||||
schedule();
|
||||
|
||||
wait_for_completion(&info->cmpl);
|
||||
return info->error;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (_cas_rpool_pre_alloc_schedule(i, &info))
|
||||
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();
|
||||
|
||||
*cpu = smp_processor_id();
|
||||
current_rpool = &rpool_master->rpools[*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);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
28
modules/cas_cache/utils/utils_rpool.h
Normal file
28
modules/cas_cache/utils/utils_rpool.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CAS_RPOOL_H__
|
||||
#define __CAS_RPOOL_H__
|
||||
|
||||
#define CAS_RPOOL_MIN_SIZE_ITEM sizeof(struct list_head)
|
||||
|
||||
struct cas_reserve_pool;
|
||||
|
||||
typedef void (*cas_rpool_del)(void *allocator_ctx, void *item);
|
||||
typedef void *(*cas_rpool_new)(void *allocator_ctx, int cpu);
|
||||
|
||||
struct cas_reserve_pool *cas_rpool_create(uint32_t limit, char *name,
|
||||
uint32_t item_size, cas_rpool_new rpool_new,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx);
|
||||
|
||||
void cas_rpool_destroy(struct cas_reserve_pool *rpool,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx);
|
||||
|
||||
void *cas_rpool_try_get(struct cas_reserve_pool *rpool, int *cpu);
|
||||
|
||||
int cas_rpool_try_put(struct cas_reserve_pool *rpool, void *item, int cpu);
|
||||
|
||||
#endif /* __CAS_RPOOL_H__ */
|
||||
|
Reference in New Issue
Block a user