open-cas-linux/modules/cas_cache/layer_upgrade.c
Michal Mielewczyk 56cbc6595b Pass name for newly initialized cache in array instead of pointer.
Unitl now pointer to local variable was passed to OCF and it's content could be
overwriten after leaving function responsible for preparing cache config.

Signed-off-by: Michal Mielewczyk <michal.mielewczyk@intel.com>
2019-08-20 03:42:05 -04:00

1512 lines
37 KiB
C

/*
* Copyright(c) 2012-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "cas_cache.h"
#define CAS_UPGRADE_DEBUG 0
#if 1 == CAS_UPGRADE_DEBUG
#define CAS_DEBUG_TRACE() \
printk(KERN_INFO "[Upgrade] %s\n", __func__)
#define CAS_DEBUG_MSG(msg) \
printk(KERN_INFO "[Upgrade] %s - %s\n", __func__, msg)
#define CAS_DEBUG_PARAM(format, ...) \
printk(KERN_INFO "[Upgrade] %s - "format"\n", \
__func__, ##__VA_ARGS__)
#else
#define CAS_DEBUG_TRACE()
#define CAS_DEBUG_MSG(msg)
#define CAS_DEBUG_PARAM(format, ...)
#endif
extern u32 max_writeback_queue_size;
extern u32 writeback_queue_unblock_size;
extern u32 metadata_layout;
extern u32 unaligned_io;
extern u32 seq_cut_off_mb;
extern u32 use_io_scheduler;
typedef int (*restore_callback_t) (struct cas_properties *cache_props);
static void _cas_upgrade_clear_state(void)
{
in_upgrade = false;
}
static void _cas_upgrade_set_state(void)
{
in_upgrade = true;
}
bool cas_upgrade_is_in_upgrade(void)
{
return in_upgrade;
}
/*
* Caches parameters to serialize
* +------------+-------------------------------+---------------+
* |Group | Key | Type |
* |------------|-------------------------------|---------------|
* |cache | cache_name | string |
* |cache | cache_path | string |
* |cache | cache_type | uint |
* |cache | cache_line_size | uint |
* |cache | cache_evp_policy | uint |
* |cache | cache_mode | uint |
* |cache | cache_seq_cutoff_thresh | uint |
* |cache | cache_seq_cutoff_policy | uint |
* |------------|-------------------------------|---------------|
* |core | core_no | uint |
* |core | core_X_name | string |
* |core | core_X_path | string |
* |core | core_X_type | uint |
* |------------|-------------------------------|---------------|
* |flush | flush_cleaning_policy | uint |
* |flush | flush_wake_up_time | uint |
* |flush | flush_staleness_time | uint |
* |flush | flush_max_buffers | uint |
* |flush | flush_threshold | uint |
* |flush | flush_acp_wake_up_time | uint |
* |flush | flush_acp_max_buffers | uint |
* |------------|-------------------------------|---------------|
* |io_class | io_class_no | uint |
* |io_class | io_class_X_name | string |
* |io_class | io_class_X_id | uint |
* |io_class | io_class_X_max | uint |
* |io_class | io_class_X_min | uint |
* |io_class | io_class_X_cache_mode | uint |
* |io_class | io_class_X_prio | uint |
* +------------+-------------------------------+---------------+
*
*/
#define UPGRADE_IFACE_VERSION_STR "upgrade_iface_version"
#define CACHE_NAME_STR "cache_name"
#define CACHE_PATH_STR "cache_path"
#define CACHE_LINE_SIZE_STR "cache_line_size"
#define CACHE_TYPE_STR "cache_type"
#define CACHE_MODE_STR "cache_mode"
#define CORE_NO_STR "core_no"
#define CORE_NAME_STR "core_%lu_name"
#define CORE_PATH_STR "core_%lu_path"
#define CORE_SEQ_CUTOFF_THRESHOLD_STR "core_%lu_seq_cutoff_thresh"
#define CORE_SEQ_CUTOFF_POLICY_STR "core_%lu_seq_cutoff_policy"
#define CLEANING_POLICY_STR "flush_cleaning_policy"
#define CLEANING_ALRU_WAKEUP_TIME_STR "flush_wakeup_time"
#define CLEANING_ALRU_STALENESS_TIME_STR "flush_staleness_time"
#define CLEANING_ALRU_MAX_BUFFERS_STR "flush_max_buffers"
#define CLEANING_ALRU_TRESHOLD_STR "flush_threshold"
#define CLEANING_ACP_WAKEUP_TIME_STR "flush_acp_wakeup_time"
#define CLEANING_ACP_MAX_BUFFERS_STR "flush_acp_max_buffers"
#define IO_CLASS_NO_STR "io_class_no"
#define IO_CLASS_NAME_STR "io_class_%lu_name"
#define IO_CLASS_MIN_STR "io_class_%lu_min"
#define IO_CLASS_ID_STR "io_class_%lu_id"
#define IO_CLASS_MAX_STR "io_class_%lu_max"
#define IO_CLASS_PRIO_STR "io_class_%lu_prio"
#define IO_CLASS_CACHE_MODE_STR "io_class_%lu_cache_mode"
#define CAS_UPGRADE_IFACE_VERSION_19_03_00 190300
#define CAS_UPGRADE_IFACE_CURRENT_VERSION CAS_UPGRADE_IFACE_VERSION_19_03_00
static int _cas_upgrade_dump_cache_conf_main(ocf_cache_t cache,
struct cas_properties *cache_props)
{
int result = 0;
CAS_DEBUG_TRACE();
result = cas_properties_add_uint(cache_props, UPGRADE_IFACE_VERSION_STR,
(uint64_t) CAS_UPGRADE_IFACE_CURRENT_VERSION,
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding interface version\n");
return result;
}
result = cas_properties_add_string(cache_props, CACHE_NAME_STR,
ocf_cache_get_name(cache),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding cache_name\n");
return result;
}
result = cas_properties_add_string(cache_props, CACHE_PATH_STR,
ocf_cache_get_uuid(cache)->data,
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding cache_path\n");
return result;
}
result = cas_properties_add_uint(cache_props, CACHE_LINE_SIZE_STR,
(uint64_t) ocf_cache_get_line_size(cache),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding cache_line_size\n");
return result;
}
result = cas_properties_add_uint(cache_props, CACHE_TYPE_STR,
(uint64_t) ocf_cache_get_type_id(cache),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT OCF_PREFIX_SHORT
"Error during adding cache_type\n");
return result;
}
result = cas_properties_add_uint(cache_props, CACHE_MODE_STR,
(uint64_t) ocf_cache_get_mode(cache),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT OCF_PREFIX_SHORT
"Error during adding cache_mode\n");
return result;
}
return result;
}
struct _ocf_core_visitor_ctx {
int i;
struct cas_properties *cache_props;
int error;
};
int _cas_upgrade_core_visitor(ocf_core_t core, void *cntx)
{
int result = 0;
char *value = NULL;
struct _ocf_core_visitor_ctx *core_visitor_ctx =
(struct _ocf_core_visitor_ctx*) cntx;
struct cas_properties *cache_props = core_visitor_ctx->cache_props;
unsigned long core_no = 0;
core_visitor_ctx->i++;
core_no = core_visitor_ctx->i;
value = kmalloc(sizeof(*value) * MAX_STR_LEN, GFP_KERNEL);
if (value == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
result = snprintf(value, MAX_STR_LEN, CORE_NAME_STR, core_no);
if (result < 0)
goto err;
result = cas_properties_add_string(cache_props, value,
ocf_core_get_name(core), CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT OCF_PREFIX_SHORT
"Error during adding core id\n");
goto err;
}
result = snprintf(value, MAX_STR_LEN, CORE_PATH_STR,
core_no);
if (result < 0)
goto err;
result = cas_properties_add_string(cache_props, value,
ocf_core_get_uuid(core)->data, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT OCF_PREFIX_SHORT
"Error during adding core path\n");
goto err;
}
result = snprintf(value, MAX_STR_LEN, CORE_SEQ_CUTOFF_POLICY_STR, core_no);
if (result < 0)
goto err;
result = cas_properties_add_uint(cache_props, value,
(uint64_t) ocf_core_get_seq_cutoff_policy(core),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR "Error during adding core seq cutoff policy\n");
goto err;
}
result = snprintf(value, MAX_STR_LEN, CORE_SEQ_CUTOFF_THRESHOLD_STR, core_no);
if (result < 0)
goto err;
result = cas_properties_add_uint(cache_props, value,
(uint64_t) ocf_core_get_seq_cutoff_threshold(core),
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR "Error during adding core seq cutoff threshold\n");
goto err;
}
err:
kfree(value);
core_visitor_ctx->error = result;
return result;
}
static int _cas_upgrade_dump_cache_conf_cores(ocf_cache_t device,
struct cas_properties *cache_props)
{
int result = 0;
struct _ocf_core_visitor_ctx core_visitor_ctx;
char *value = NULL;
CAS_DEBUG_TRACE();
value = kmalloc(sizeof(*value) * MAX_STR_LEN, GFP_KERNEL);
if (value == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
result = cas_properties_add_uint(cache_props, CORE_NO_STR,
(uint64_t) ocf_cache_get_core_count(device),
CAS_PROPERTIES_NON_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT OCF_PREFIX_SHORT
"Error during adding cores number\n");
goto err;
}
memset(&core_visitor_ctx, 0, sizeof(core_visitor_ctx));
core_visitor_ctx.cache_props = cache_props;
result |= ocf_core_visit(device, _cas_upgrade_core_visitor,
&core_visitor_ctx, true);
if (core_visitor_ctx.error) {
result = core_visitor_ctx.error;
goto err;
}
if (core_visitor_ctx.i > ocf_cache_get_core_count(device)) {
result = -OCF_ERR_INVAL;
goto err;
}
err:
kfree(value);
return result;
}
static int _cas_upgrade_dump_cache_conf_flush(ocf_cache_t cache,
struct cas_properties *cache_props)
{
uint32_t cleaning_type;
uint32_t alru_thread_wakeup_time;
uint32_t alru_stale_buffer_time;
uint32_t alru_flush_max_buffers;
uint32_t alru_activity_threshold;
uint32_t acp_thread_wakeup_time;
uint32_t acp_flush_max_buffers;
int result = 0;
CAS_DEBUG_TRACE();
result |= cache_mngt_get_cleaning_policy(cache, &cleaning_type);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_wake_up_time, &alru_thread_wakeup_time);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_stale_buffer_time, &alru_stale_buffer_time);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_flush_max_buffers, &alru_flush_max_buffers);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_activity_threshold, &alru_activity_threshold);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_acp,
ocf_acp_wake_up_time, &acp_thread_wakeup_time);
result |= cache_mngt_get_cleaning_param(cache, ocf_cleaning_acp,
ocf_acp_flush_max_buffers, &acp_flush_max_buffers);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Unable to get cleaning policy params\n");
return result;
}
result = cas_properties_add_uint(cache_props, CLEANING_POLICY_STR,
cleaning_type, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding cleaning policy type\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ALRU_WAKEUP_TIME_STR,
alru_thread_wakeup_time, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding alru wakeup time\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ALRU_STALENESS_TIME_STR,
alru_stale_buffer_time, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding alru staleness time\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ALRU_MAX_BUFFERS_STR,
alru_flush_max_buffers, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding alru max flush buffers\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ALRU_TRESHOLD_STR,
alru_activity_threshold, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding alru flush threshold\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ACP_WAKEUP_TIME_STR,
acp_thread_wakeup_time, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding acp wakeup time\n");
return result;
}
result = cas_properties_add_uint(cache_props,
CLEANING_ACP_MAX_BUFFERS_STR,
acp_flush_max_buffers, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding acp max flush buffers\n");
return result;
}
return result;
}
struct _cas_upgrade_dump_io_class_visit_ctx {
struct cas_properties *cache_props;
uint32_t io_class_counter;
int error;
};
int _cas_upgrade_dump_io_class_visitor(ocf_cache_t cache,
uint32_t io_class_id, void *ctx)
{
int result = 0;
struct ocf_io_class_info *info = NULL;
struct _cas_upgrade_dump_io_class_visit_ctx *io_class_visit_ctx =
(struct _cas_upgrade_dump_io_class_visit_ctx*) ctx;
char *key = NULL;
struct cas_properties *cache_props = io_class_visit_ctx->cache_props;
unsigned long io_class_counter;
CAS_DEBUG_TRACE();
key = kmalloc(sizeof(*key) * 4096, GFP_KERNEL);
if (key == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
result = -OCF_ERR_NO_MEM;
goto error;
}
result = ocf_cache_io_class_get_info(cache, io_class_id, info);
if (result)
goto error;
io_class_visit_ctx->io_class_counter++;
io_class_counter = io_class_visit_ctx->io_class_counter;
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_NAME_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_string(cache_props, key,
info->name, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class name\n");
goto error;
}
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_MIN_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_uint(cache_props, key,
info->min_size, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class min size\n");
goto error;
}
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_ID_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_uint(cache_props, key,
io_class_id, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class id\n");
goto error;
}
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_MAX_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_uint(cache_props, key,
info->max_size, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class max size\n");
goto error;
}
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_PRIO_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_uint(cache_props, key,
info->priority, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class priority\n");
goto error;
}
result = snprintf(key, MAX_STR_LEN,
IO_CLASS_CACHE_MODE_STR, io_class_counter);
if (result < 0)
goto error;
result = cas_properties_add_uint(cache_props, key,
info->cache_mode, CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class cache mode\n");
goto error;
}
error:
kfree(key);
kfree(info);
io_class_visit_ctx->error = result;
return result;
}
static int _cas_upgrade_dump_cache_conf_io_class(ocf_cache_t cache,
struct cas_properties *cache_props)
{
int result = 0;
struct _cas_upgrade_dump_io_class_visit_ctx io_class_visit_ctx;
CAS_DEBUG_TRACE();
result = cas_properties_add_uint(cache_props, IO_CLASS_NO_STR, 0,
CAS_PROPERTIES_NON_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class number\n");
goto error_after_alloc_buffer;
}
memset(&io_class_visit_ctx, 0, sizeof(io_class_visit_ctx));
io_class_visit_ctx.cache_props = cache_props;
ocf_io_class_visit(cache, _cas_upgrade_dump_io_class_visitor,
&io_class_visit_ctx);
if (io_class_visit_ctx.error) {
result = io_class_visit_ctx.error;
goto error_after_alloc_buffer;
}
result = cas_properties_add_uint(cache_props, IO_CLASS_NO_STR,
io_class_visit_ctx.io_class_counter,
CAS_PROPERTIES_CONST);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during adding io class number\n");
goto error_after_alloc_buffer;
}
error_after_alloc_buffer:
return result;
}
static int _cas_upgrade_dump_cache_conf(ocf_cache_t device,
struct cas_properties *cache_props)
{
int result = 0;
CAS_DEBUG_TRACE();
result = _cas_upgrade_dump_cache_conf_main(device, cache_props);
if (result)
return result;
result = _cas_upgrade_dump_cache_conf_cores(device, cache_props);
if (result)
return result;
result = _cas_upgrade_dump_cache_conf_flush(device, cache_props);
if (result)
return result;
result = _cas_upgrade_dump_cache_conf_io_class(device, cache_props);
if (result)
return result;
return result;
}
static void _cas_upgrade_destroy_props_array(
struct cas_properties **caches_props_array, int count)
{
int i;
CAS_DEBUG_TRACE();
for (i = 0; i < count ; i++) {
if (caches_props_array[i] && !IS_ERR(caches_props_array[i]))
cas_properties_destroy(caches_props_array[i]);
caches_props_array[i] = NULL;
}
}
static int _cas_upgrade_init_props_array(
struct cas_properties **caches_props_array, int count)
{
int i, result = 0;
CAS_DEBUG_TRACE();
for (i = 0; i < count ; i++) {
caches_props_array[i] = cas_properties_create();
if (IS_ERR(caches_props_array[i])) {
result = PTR_ERR(caches_props_array[i]);
break;
}
}
if (result)
_cas_upgrade_destroy_props_array(caches_props_array, i);
return result;
}
struct _cas_cache_dump_conf_visitor_ctx {
int i;
struct cas_properties **caches_props_array;
struct casdsk_props_conf *caches_serialized_conf;
int error;
};
int _cas_upgrade_dump_cache_conf_visitor(ocf_cache_t cache, void *cntx)
{
int result = 0;
struct _cas_cache_dump_conf_visitor_ctx *cache_visit_ctx =
(struct _cas_cache_dump_conf_visitor_ctx*) cntx;
struct cas_properties **caches_props_array =
cache_visit_ctx->caches_props_array;
struct casdsk_props_conf *caches_serialized_conf =
cache_visit_ctx->caches_serialized_conf;
result = _cas_upgrade_dump_cache_conf(cache,
caches_props_array[cache_visit_ctx->i]);
if (result)
goto error;
result = cas_properties_serialize(
caches_props_array[cache_visit_ctx->i],
&caches_serialized_conf[cache_visit_ctx->i]);
error:
cache_visit_ctx->i++;
cache_visit_ctx->error = result;
return result;
}
static int _cas_upgrade_dump_conf(void)
{
int result = 0, i = 0;
size_t caches_no = 0;
struct casdsk_props_conf *caches_serialized_conf = NULL;
struct _cas_cache_dump_conf_visitor_ctx cache_visit_ctx;
struct cas_properties **caches_props_array;
CAS_DEBUG_TRACE();
caches_no = ocf_mngt_cache_get_count(cas_ctx);
if (caches_no == 0)
return 0;
caches_props_array = kcalloc(caches_no,
sizeof(*caches_props_array), GFP_KERNEL);
if (caches_props_array == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
caches_serialized_conf = kcalloc(caches_no,
sizeof(*caches_serialized_conf), GFP_KERNEL);
if (caches_serialized_conf == NULL) {
kfree(caches_props_array);
result = -OCF_ERR_NO_MEM;
return result;
}
result = _cas_upgrade_init_props_array(caches_props_array, caches_no);
if (result) {
kfree(caches_props_array);
kfree(caches_serialized_conf);
return result;
}
/* Set up visitor context */
memset(&cache_visit_ctx, 0, sizeof(cache_visit_ctx));
cache_visit_ctx.caches_props_array = caches_props_array;
cache_visit_ctx.caches_serialized_conf = caches_serialized_conf;
result = ocf_mngt_cache_visit(cas_ctx, _cas_upgrade_dump_cache_conf_visitor,
&cache_visit_ctx);
if (result || cache_visit_ctx.error) {
result |= cache_visit_ctx.error;
goto err_after_init_props_array;
}
CAS_DEBUG_MSG("End of dump");
casdisk_functions.casdsk_store_config(caches_no, caches_serialized_conf);
CAS_DEBUG_MSG("Configuration stored to idisk");
err_after_init_props_array:
if (result) {
CAS_DEBUG_MSG("End of dump: ERROR");
for (; i >= 0; i--)
kfree(caches_serialized_conf[i].buffer);
kfree(caches_serialized_conf);
caches_no = 0;
}
_cas_upgrade_destroy_props_array(caches_props_array, caches_no);
kfree(caches_props_array);
return result;
}
int cas_upgrade_set_pt_and_flush_visitor_core(ocf_core_t core, void *cntx)
{
int *result = (int*) cntx;
ocf_volume_t vol;
vol = ocf_core_get_volume(core);
*result = casdisk_functions.casdsk_disk_set_pt(bd_object(vol)->dsk);
return *result;
}
int _cas_upgrade_set_pt_and_flush_visitor_cache(ocf_cache_t cache, void *cntx)
{
int *result = (int*) cntx;
const char *cache_name = ocf_cache_get_name(cache);
*result = cache_mngt_set_cache_mode(cache_name,
ocf_cache_mode_pt, false);
if (*result)
return *result;
*result = cache_mngt_flush_device(cache_name);
if (*result)
return *result;
ocf_core_visit(cache, cas_upgrade_set_pt_and_flush_visitor_core,
result, true);
return *result;
}
static int _cas_upgrade_set_pt_and_flush(void)
{
int result = 0, r = 0;
CAS_DEBUG_TRACE();
r = ocf_mngt_cache_visit_reverse(cas_ctx,
_cas_upgrade_set_pt_and_flush_visitor_cache, &result);
result |= r;
return result;
}
int _cas_upgrade_stop_devices_visitor_wait(ocf_cache_t cache, void *cntx)
{
cache_mngt_wait_for_rq_finish(cache);
return 0;
}
int _cas_upgrade_stop_devices_visitor_exit(ocf_cache_t cache, void *cntx)
{
int *result = (int*) cntx;
*result = cache_mngt_exit_instance(ocf_cache_get_name(cache), true);
return *result;
}
static int _cas_upgrade_stop_devices(void)
{
int result = 0, r = 0;
CAS_DEBUG_TRACE();
r = ocf_mngt_cache_visit(cas_ctx, _cas_upgrade_stop_devices_visitor_wait,
NULL);
if (r)
return r;
r = ocf_mngt_cache_visit_reverse(cas_ctx,
_cas_upgrade_stop_devices_visitor_exit, &result);
result |= r;
return result;
}
static int _cas_upgrade_restore_conf_main(struct cas_properties *cache_props,
char *cache_name)
{
int result = 0;
uint64_t cache_mode, cache_line_size;
uint64_t cache_type, version;
char *cache_path = NULL;
struct ocf_mngt_cache_config cfg = {};
struct ocf_mngt_cache_device_config device_cfg = {};
struct atomic_dev_params atomic_params = {};
CAS_DEBUG_TRACE();
cache_path = kzalloc(sizeof(*cache_path) * MAX_STR_LEN, GFP_KERNEL);
if (cache_path == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
result = cas_properties_get_uint(cache_props, UPGRADE_IFACE_VERSION_STR,
&version);
if (result)
goto error;
result = cas_properties_get_string(cache_props, CACHE_NAME_STR,
cache_name, OCF_CACHE_NAME_SIZE);
if (result)
goto error;
result = cas_properties_get_string(cache_props, CACHE_PATH_STR,
cache_path, MAX_STR_LEN);
if (result)
goto error;
result = cas_properties_get_uint(cache_props, CACHE_TYPE_STR,
&cache_type);
if (result)
goto error;
result = cas_properties_get_uint(cache_props, CACHE_LINE_SIZE_STR,
&cache_line_size);
if (result)
goto error;
result = cas_properties_get_uint(cache_props, CACHE_MODE_STR,
&cache_mode);
if (result)
goto error;
if (cache_mode >= ocf_cache_mode_max)
cache_mode = ocf_cache_mode_default;
strncpy(cfg.name, cache_name, OCF_CACHE_NAME_SIZE);
cfg.cache_mode = cache_mode;
/* cfg.eviction_policy = TODO */
cfg.cache_line_size = cache_line_size;
cfg.metadata_layout = metadata_layout;
cfg.pt_unaligned_io = !unaligned_io;
cfg.use_submit_io_fast = !use_io_scheduler;
cfg.locked = true;
cfg.metadata_volatile = false;
cfg.backfill.max_queue_size = max_writeback_queue_size;
cfg.backfill.queue_unblock_size = writeback_queue_unblock_size;
device_cfg.uuid.data = cache_path;
device_cfg.uuid.size = strnlen(cache_path, MAX_STR_LEN) + 1;
device_cfg.volume_type = cache_type;
device_cfg.cache_line_size = cache_line_size;
device_cfg.perform_test = true;
device_cfg.force = false;
if (device_cfg.volume_type == ATOMIC_DEVICE_VOLUME) {
result = cas_blk_identify_type_atomic(device_cfg.uuid.data,
&device_cfg.volume_type, &atomic_params);
if (result)
goto error;
if (device_cfg.volume_type != ATOMIC_DEVICE_VOLUME) {
result = -OCF_ERR_INVAL_VOLUME_TYPE;
goto error;
}
device_cfg.volume_params = &atomic_params;
}
result = cache_mngt_init_instance(&cfg, &device_cfg, NULL);
error:
kfree(cache_path);
return result;
}
static int _cas_upgrade_restore_conf_core(struct cas_properties *cache_props,
ocf_cache_t cache)
{
int result = 0;
unsigned long i = 0;
uint64_t core_no, version;
char core_name[OCF_CORE_NAME_SIZE];
char *core_path = NULL;
char *key = NULL;
struct ocf_mngt_core_config cfg = {};
CAS_DEBUG_TRACE();
key = kmalloc(sizeof(*key) * MAX_STR_LEN, GFP_KERNEL);
if (key == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
core_path = kzalloc(sizeof(*core_path) * MAX_STR_LEN, GFP_KERNEL);
if (core_path == NULL) {
kfree(key);
result = -OCF_ERR_NO_MEM;
return result;
}
result = cas_properties_get_uint(cache_props, UPGRADE_IFACE_VERSION_STR,
&version);
if (result)
goto error;
result = cas_properties_get_uint(cache_props, CORE_NO_STR, &core_no);
if (result)
goto error;
for (i = 1; i < core_no + 1; i++) {
result = snprintf(key, MAX_STR_LEN, CORE_PATH_STR, i);
if (result < 0)
goto error;
result = cas_properties_get_string(cache_props, key,
core_path, MAX_STR_LEN);
if (result)
goto error;
result = snprintf(key, MAX_STR_LEN, CORE_NAME_STR, i);
if (result < 0)
goto error;
result = cas_properties_get_string(cache_props, key,
core_name, OCF_CORE_NAME_SIZE);
if (result)
goto error;
cfg.name = core_name;
cfg.try_add = 0;
cfg.volume_type = BLOCK_DEVICE_VOLUME;
cfg.uuid.data = core_path;
cfg.uuid.size = strnlen(core_path, MAX_STR_LEN) + 1;
result = cache_mngt_add_core_to_cache(ocf_cache_get_name(cache),
&cfg, NULL);
if (result)
goto error;
}
error:
kfree(key);
kfree(core_path);
return result;
}
static int _cas_upgrade_restore_conf_flush(struct cas_properties *cache_props,
ocf_cache_t cache)
{
uint64_t cleaning_type;
uint64_t alru_thread_wakeup_time = OCF_ALRU_DEFAULT_WAKE_UP;
uint64_t alru_stale_buffer_time = OCF_ALRU_DEFAULT_STALENESS_TIME;
uint64_t alru_flush_max_buffers = OCF_ALRU_DEFAULT_FLUSH_MAX_BUFFERS;
uint64_t alru_activity_threshold = OCF_ALRU_DEFAULT_ACTIVITY_THRESHOLD;
uint64_t acp_thread_wakeup_time = OCF_ACP_DEFAULT_WAKE_UP;
uint64_t acp_flush_max_buffers = OCF_ACP_DEFAULT_FLUSH_MAX_BUFFERS;
uint64_t version;
int result = 0;
CAS_DEBUG_TRACE();
result = cas_properties_get_uint(cache_props, UPGRADE_IFACE_VERSION_STR,
&version);
if (result)
return result;
result = cas_properties_get_uint(cache_props,
CLEANING_POLICY_STR, &cleaning_type);
if (result)
return result;
if (cleaning_type >= ocf_cleaning_max)
cleaning_type = ocf_cleaning_default;
/*
* CLEANING_ALRU_WAKEUP_TIME PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ALRU_WAKEUP_TIME_STR,
&alru_thread_wakeup_time);
if (result)
return result;
/*
* CLEANING_ALRU_STALENESS_TIME PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ALRU_STALENESS_TIME_STR,
&alru_stale_buffer_time);
if (result)
return result;
/*
* CLEANING_ALRU_MAX_BUFFERS PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ALRU_MAX_BUFFERS_STR,
&alru_flush_max_buffers);
if (result)
return result;
/*
* CLEANING_ALRU_TRESHOLD PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ALRU_TRESHOLD_STR,
&alru_activity_threshold);
if (result)
return result;
/*
* CLEANING_ACP_WAKEUP_TIME PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ACP_WAKEUP_TIME_STR,
&acp_thread_wakeup_time);
if (result)
return result;
/*
* CLEANING_ACP_MAX_BUFFERS PARAM
*/
result = cas_properties_get_uint(cache_props,
CLEANING_ACP_MAX_BUFFERS_STR,
&acp_flush_max_buffers);
if (result)
return result;
result |= cache_mngt_set_cleaning_policy(cache, cleaning_type);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_wake_up_time, alru_thread_wakeup_time);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_stale_buffer_time, alru_stale_buffer_time);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_flush_max_buffers, alru_flush_max_buffers);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_alru,
ocf_alru_activity_threshold, alru_activity_threshold);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_acp,
ocf_acp_wake_up_time, acp_thread_wakeup_time);
result |= cache_mngt_set_cleaning_param(cache, ocf_cleaning_acp,
ocf_acp_flush_max_buffers, acp_flush_max_buffers);
return result;
}
static int _cas_upgrade_restore_conf_io_class(
struct cas_properties *cache_props, ocf_cache_t cache)
{
int result = 0;
unsigned long i = 0;
uint64_t io_class_no, min_size, max_size, priority, cache_mode, part_id;
char *name = NULL;
char *key = NULL;
struct kcas_io_classes *cfg;
CAS_DEBUG_TRACE();
key = kzalloc(sizeof(*key) * MAX_STR_LEN, GFP_KERNEL);
if (key == NULL) {
result = -OCF_ERR_NO_MEM;
return result;
}
name = kzalloc(sizeof(*name) * MAX_STR_LEN, GFP_KERNEL);
if (name == NULL) {
kfree(key);
result = -OCF_ERR_NO_MEM;
return result;
}
cfg = kzalloc(KCAS_IO_CLASSES_SIZE, GFP_KERNEL);
if (cfg == NULL) {
kfree(key);
kfree(name);
result = -OCF_ERR_NO_MEM;
return result;
}
result = cas_properties_get_uint(cache_props, IO_CLASS_NO_STR,
&io_class_no);
if (result)
goto error_after_alloc_buffers;
for (i = 1; i < io_class_no + 1; i++) {
result = snprintf(key, MAX_STR_LEN, IO_CLASS_NAME_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_string(cache_props, key, name,
MAX_STR_LEN);
if (result)
goto error_after_alloc_buffers;
result = snprintf(key, MAX_STR_LEN, IO_CLASS_ID_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_uint(cache_props, key, &part_id);
if (result)
goto error_after_alloc_buffers;
result = snprintf(key, MAX_STR_LEN, IO_CLASS_MIN_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_uint(cache_props, key, &min_size);
if (result)
goto error_after_alloc_buffers;
result = snprintf(key, MAX_STR_LEN, IO_CLASS_MAX_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_uint(cache_props, key, &max_size);
if (result)
goto error_after_alloc_buffers;
result = snprintf(key, MAX_STR_LEN, IO_CLASS_PRIO_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_uint(cache_props, key, &priority);
if (result)
goto error_after_alloc_buffers;
result = snprintf(key, MAX_STR_LEN, IO_CLASS_CACHE_MODE_STR, i);
if (result < 0)
goto error_after_alloc_buffers;
result = cas_properties_get_uint(cache_props, key, &cache_mode);
if (result)
goto error_after_alloc_buffers;
result = env_strncpy(cfg->info[part_id].name, OCF_IO_CLASS_NAME_MAX,
name, OCF_IO_CLASS_NAME_MAX);
if (result)
goto error_after_alloc_buffers;
cfg->info[part_id].priority = (int16_t)priority;
cfg->info[part_id].cache_mode = (ocf_cache_mode_t)cache_mode;
cfg->info[part_id].max_size = (uint32_t)max_size;
cfg->info[part_id].min_size = (uint32_t)min_size;
}
result = cache_mngt_set_partitions(ocf_cache_get_name(cache), cfg);
error_after_alloc_buffers:
kfree(key);
kfree(name);
kfree(cfg);
return result;
}
static int _cas_upgrade_restore_cache(struct cas_properties *cache_props)
{
int result = 0;
char cache_name[OCF_CACHE_NAME_SIZE];
ocf_cache_t cache;
CAS_DEBUG_TRACE();
result = _cas_upgrade_restore_conf_main(cache_props, cache_name);
if (result)
return result;
result = ocf_mngt_cache_get_by_name(cas_ctx, cache_name, &cache);
if (result)
return result;
result = _cas_upgrade_restore_conf_core(cache_props, cache);
if (result)
goto error;
result = _cas_upgrade_restore_conf_flush(cache_props, cache);
if (result)
goto error;
result = _cas_upgrade_restore_conf_io_class(cache_props, cache);
if (result)
goto error;
error:
ocf_mngt_cache_put(cache);
return result;
}
int _cas_upgrade_restore_cache_mode_visitor(ocf_core_t core, void *cntx)
{
int *result = (int*) cntx;
ocf_volume_t vol;
vol = ocf_core_get_volume(core);
*result = casdisk_functions.casdsk_disk_clear_pt(bd_object(vol)->dsk);
return *result;
}
static int _cas_upgrade_restore_cache_mode(struct cas_properties *cache_props)
{
int result = 0;
uint64_t cache_mode;
char cache_name[OCF_CACHE_NAME_SIZE];
ocf_cache_t cache;
CAS_DEBUG_TRACE();
result = cas_properties_get_string(cache_props, CACHE_NAME_STR,
cache_name, OCF_CACHE_NAME_SIZE);
if (result)
return result;
result = ocf_mngt_cache_get_by_name(cas_ctx, cache_name, &cache);
if (result)
return result;
result = cas_properties_get_uint(cache_props, CACHE_MODE_STR,
&cache_mode);
if (result)
goto error;
if (ocf_cache_get_mode(cache) != cache_mode) {
result = cache_mngt_set_cache_mode(ocf_cache_get_name(cache),
cache_mode, false);
if (result)
goto error;
result |= ocf_core_visit(cache,
_cas_upgrade_restore_cache_mode_visitor, &result, true);
}
error:
ocf_mngt_cache_put(cache);
return result;
}
static int _cas_upgrade_restore_cache_after_error(
struct cas_properties *cache_props)
{
int result = 0;
char cache_name[OCF_CACHE_NAME_SIZE];
ocf_cache_t cache = NULL;
CAS_DEBUG_TRACE();
result = cas_properties_get_string(cache_props, CACHE_NAME_STR,
cache_name, OCF_CACHE_NAME_SIZE);
if (result)
return result;
result = ocf_mngt_cache_get_by_name(cas_ctx, cache_name, &cache);
if (result == -OCF_ERR_CACHE_NOT_EXIST) {
result = _cas_upgrade_restore_cache(cache_props);
} else if (result == 0) {
result = _cas_upgrade_restore_cache_mode(cache_props);
ocf_mngt_cache_put(cache);
}
return result;
}
static int _cas_upgrade_restore_configuration(
struct casdsk_props_conf *caches_props_serialized_array,
size_t caches_no, restore_callback_t restore_callback)
{
int result = 0, i = 0;
struct cas_properties **caches_props_array = NULL;
CAS_DEBUG_TRACE();
caches_props_array = kcalloc(caches_no, sizeof(*caches_props_array),
GFP_KERNEL);
if (!caches_props_array) {
result = -OCF_ERR_NO_MEM;
return result;
}
for (i = 0; i < caches_no; i++) {
caches_props_array[i] = cas_properites_parse(
&caches_props_serialized_array[i]);
if (IS_ERR(caches_props_array[i])) {
result = PTR_ERR(caches_props_array[i]);
break;
}
if (caches_props_array[i]) {
#if 1 == CAS_UPGRADE_DEBUG
cas_properties_print(caches_props_array[i]);
#endif
result = restore_callback(caches_props_array[i]);
if (result) {
cas_properties_print(caches_props_array[i]);
break;
}
}
}
_cas_upgrade_destroy_props_array(caches_props_array, caches_no);
kfree(caches_props_array);
return result;
}
struct casdsk_props_conf *caches_serialized_conf_init;
size_t caches_no_init;
int cas_upgrade_get_configuration(void)
{
int result = 0;
struct casdsk_props_conf *buffer = NULL;
CAS_DEBUG_TRACE();
caches_no_init = casdisk_functions.casdsk_get_stored_config(&buffer);
if (caches_no_init == 0 || !buffer)
return -KCAS_ERR_NO_STORED_CONF;
_cas_upgrade_set_state();
caches_serialized_conf_init = buffer;
return result;
}
int cas_upgrade_check_ctx_visitor(ocf_cache_t cache, void *cntx)
{
int result = ocf_cache_is_incomplete(cache);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Upgrade error. Cannot start upgrade in flight"
" %s is in incomplete state\n",
ocf_cache_get_name(cache));
}
return result;
}
static int _cas_cache_attached_check_visitor(ocf_cache_t cache, void *cntx)
{
if (!ocf_cache_is_device_attached(cache)) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Upgrade error. Cannot start upgrade in flight"
" when cache drive is detached!\n");
return 1;
}
return 0;
}
static int _cas_upgrade_check_ctx_state(void)
{
if (ocf_mngt_core_pool_get_count(cas_ctx)) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Upgrade error. Cannot start upgrade in flight"
" when core pool list is not empty\n");
return -KCAS_ERR_CORE_POOL_NOT_EMPTY;
}
if (ocf_mngt_cache_visit(cas_ctx ,_cas_cache_attached_check_visitor,
NULL)) {
return -KCAS_ERR_NO_CACHE_ATTACHED;
}
if (ocf_mngt_cache_visit(cas_ctx, cas_upgrade_check_ctx_visitor,
NULL)) {
return -OCF_ERR_CACHE_IN_INCOMPLETE_STATE;
}
return 0;
}
int cas_upgrade(void)
{
int result = 0, result_rollback = 0;
restore_callback_t restore_callback = NULL;
CAS_DEBUG_TRACE();
result = _cas_upgrade_check_ctx_state();
if (result)
return result;
_cas_upgrade_set_state();
result = _cas_upgrade_dump_conf();
if (result)
goto dump_err;
result = _cas_upgrade_set_pt_and_flush();
if (result) {
restore_callback = _cas_upgrade_restore_cache_mode;
goto upgrade_err;
}
result = _cas_upgrade_stop_devices();
if (result) {
restore_callback = _cas_upgrade_restore_cache_after_error;
goto upgrade_err;
}
return 0;
upgrade_err:
printk(KERN_ERR OCF_PREFIX_SHORT "Upgrade error. Start rollback");
result_rollback = cas_upgrade_get_configuration();
if (result_rollback != -KCAS_ERR_NO_STORED_CONF) {
result_rollback = _cas_upgrade_restore_configuration(
caches_serialized_conf_init, caches_no_init,
restore_callback);
} else {
/* nothing to rool back - that's good */
result_rollback = 0;
}
if (result_rollback) {
/* rollback error */
/* TODO: FIXME this path loses information about original cache
mode if we managed to switch to PT - configuration stored in
inteldisk will be freed before returning from this function.
*/
result = -KCAS_ERR_ROLLBACK;
}
casdisk_functions.casdsk_free_stored_config();
dump_err:
_cas_upgrade_clear_state();
return result;
}
int cas_upgrade_finish(void)
{
int result = 0, rollback_result = 0;
CAS_DEBUG_TRACE();
result = _cas_upgrade_restore_configuration(caches_serialized_conf_init,
caches_no_init, _cas_upgrade_restore_cache);
if (result) {
printk(KERN_ERR OCF_PREFIX_SHORT
"Error during restoring configuration\n");
rollback_result = _cas_upgrade_set_pt_and_flush();
if (rollback_result)
result = rollback_result;
rollback_result = _cas_upgrade_stop_devices();
if (rollback_result)
result = rollback_result;
} else {
/*
* Remove configuration only in case when restoring finished
* successfully
*/
casdisk_functions.casdsk_free_stored_config();
}
_cas_upgrade_clear_state();
return result;
}
static int _cas_upgrade_restore_noop(struct cas_properties *cache_props)
{
return 0;
}
int cas_upgrade_verify(void)
{
int result = 0;
CAS_DEBUG_TRACE();
result = _cas_upgrade_restore_configuration(caches_serialized_conf_init,
caches_no_init,
_cas_upgrade_restore_noop);
return result;
}