Initial commit

Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
Robert Baldyga
2019-03-29 08:39:34 +01:00
commit 94e8ca09e0
140 changed files with 37144 additions and 0 deletions

View 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__ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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

View 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_ */

View 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

View 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_ */

View 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;
}

View 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_ */

View 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, &current_rpool->list);
atomic_inc(&current_rpool->count);
}
CAS_DEBUG_PARAM("Added [%d] pre allocated items to reserve poll [%s]"
" for cpu %d", atomic_read(&current_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(&current_rpool->count))
continue;
list_for_each_safe(item, next, &current_rpool->list) {
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
list_del(item);
rpool_del(allocator_ctx, entry);
atomic_dec(&current_rpool->count);
}
if (atomic_read(&current_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(&current_rpool->lock);
INIT_LIST_HEAD(&current_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(&current_rpool->lock, flags);
if (!list_empty(&current_rpool->list)) {
item = LIST_FIRST_ITEM(current_rpool->list);
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
list_del(item);
atomic_dec(&current_rpool->count);
}
spin_unlock_irqrestore(&current_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(&current_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(&current_rpool->lock, flags);
if (atomic_read(&current_rpool->count) >= rpool_master->limit) {
ret = 1;
goto error;
}
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
list_add_tail(item, &current_rpool->list);
atomic_inc(&current_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(&current_rpool->count));
spin_unlock_irqrestore(&current_rpool->lock, flags);
return ret;
}

View 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__ */