text: /*
  * Copyright(c) 2012-2021 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	|
   * |------------|-------------------------------|---------------|
   * |promotion	|	promotion_policy	|	uint	|
   * |promotion	|	nhit_insertion_threshold|	uint	|
   * |promotion	|	nhit_insertion_trigger	|	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 EVICTION_POLICY_STR "cache_evp_policy"
  #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 PROMOTION_POLICY_STR "promotion_policy"
  #define PROMOTION_NHIT_INSERTION_STR "nhit_insertion_threshold"
  #define PROMOTION_NHIT_TRIGGER_STR "nhit_trigger_threshold"
  
  #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_VERSION_19_09_00 190900
  #define CAS_UPGRADE_IFACE_CURRENT_VERSION CAS_UPGRADE_IFACE_VERSION_19_09_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 {
      struct cas_properties *cache_props;
      int i;
      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_promotion(ocf_cache_t cache,
                                                    struct cas_properties *cache_props)
  {
      uint32_t promotion_type = ocf_promotion_default;
      uint32_t nhit_insertion_threshold;
      uint32_t nhit_trigger_threshold;
  
      int result = 0;
  
      result = cache_mngt_get_promotion_policy(cache, &promotion_type);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT
          "Unable to get promotion policy type\n");
          return result;
      }
  
      if (promotion_type == ocf_promotion_nhit) {
          result |= cache_mngt_get_promotion_param(cache,
                                                   ocf_promotion_nhit, ocf_nhit_insertion_threshold,
                                                   &nhit_insertion_threshold);
          result |= cache_mngt_get_promotion_param(cache,
                                                   ocf_promotion_nhit, ocf_nhit_trigger_threshold,
                                                   &nhit_trigger_threshold);
      }
  
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT
          "Unable to get promotion policy params\n");
          return result;
      }
  
      result = cas_properties_add_uint(cache_props, PROMOTION_POLICY_STR,
                                       promotion_type, CAS_PROPERTIES_CONST);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT
          "Error during adding promotion policy type\n");
          return result;
      }
  
      if (promotion_type == ocf_promotion_nhit) {
          result = cas_properties_add_uint(cache_props,
                                           PROMOTION_NHIT_INSERTION_STR,
                                           nhit_insertion_threshold,
                                           CAS_PROPERTIES_CONST);
          if (result) {
              printk(KERN_ERR OCF_PREFIX_SHORT
              "Error during adding nhit insertion "
              "threshold\n");
              return result;
          }
  
          result = cas_properties_add_uint(cache_props,
                                           PROMOTION_NHIT_TRIGGER_STR,
                                           nhit_trigger_threshold,
                                           CAS_PROPERTIES_CONST);
          if (result) {
              printk(KERN_ERR OCF_PREFIX_SHORT
              "Error during adding nhit trigger "
              "threshold\n");
              return result;
          }
      }
  
      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_promotion(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 {
      struct cas_properties **caches_props_array;
      struct casdsk_props_conf *caches_serialized_conf;
      int i;
      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_NAME_SIZE, ocf_cache_mode_pt, false);
      if (*result)
          return *result;
  
      *result = cache_mngt_flush_device(cache_name, OCF_CACHE_NAME_SIZE);
      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),
                                         OCF_CACHE_NAME_SIZE, 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, cache_evp_policy;
      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, EVICTION_POLICY_STR,
                                       &cache_evp_policy);
      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 - 1);
      cfg.cache_mode = cache_mode;
      cfg.eviction_policy = cache_evp_policy;
      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 = true;
  
      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_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,
                                             cfg.name, OCF_CORE_NAME_SIZE);
          if (result)
              goto error;
  
          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),
                                                OCF_CACHE_NAME_SIZE, &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_promotion(struct cas_properties *cache_props,
                                                 ocf_cache_t cache)
  {
      uint64_t promotion_type;
      uint64_t nhit_insertion_threshold;
      uint64_t nhit_trigger_threshold;
  
      int result = 0;
  
      result = cas_properties_get_uint(cache_props, PROMOTION_POLICY_STR,
                                       &promotion_type);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't retrieve promotion "
                                           "policy type \n");
          goto out;
      }
  
      result = cache_mngt_set_promotion_policy(cache, promotion_type);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't set promotion policy\n");
          goto out;
      }
  
      if (promotion_type != ocf_promotion_nhit)
          goto out;
  
      result = cas_properties_get_uint(cache_props,
                                       PROMOTION_NHIT_INSERTION_STR, &nhit_insertion_threshold);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't retrieve NHIT insertion "
                                           "threshold parameter \n");
          goto out;
      }
  
      result = cas_properties_get_uint(cache_props,
                                       PROMOTION_NHIT_TRIGGER_STR, &nhit_trigger_threshold);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't retrieve NHIT insertion "
                                           "trigger parameter \n");
          goto out;
      }
  
      result = cache_mngt_set_promotion_param(cache, ocf_promotion_nhit,
                                              ocf_nhit_insertion_threshold, nhit_insertion_threshold);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't set NHIT insertion "
                                           "threshold parameter \n");
          goto out;
      }
  
      result = cache_mngt_set_promotion_param(cache, ocf_promotion_nhit,
                                              ocf_nhit_trigger_threshold, nhit_trigger_threshold);
      if (result) {
          printk(KERN_ERR OCF_PREFIX_SHORT "Couldn't set NHIT trigger "
                                           "threshold parameter \n");
          goto out;
      }
  
      out:
      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),
                                         OCF_CACHE_NAME_SIZE, 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,
                                          OCF_CACHE_NAME_SIZE, &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_promotion(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,
                                          OCF_CACHE_NAME_SIZE, &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),
                                             OCF_CACHE_NAME_SIZE, 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,
                                          OCF_CACHE_NAME_SIZE, &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;
  }
  