From 1156531590d935d239d412cd927438a2a8c3cb93 Mon Sep 17 00:00:00 2001 From: Tomasz Rybicki Date: Thu, 20 Dec 2018 14:10:21 +0100 Subject: [PATCH] Added tracing functionality Signed-off-by: Tomasz Rybicki --- inc/ocf.h | 1 + inc/ocf_io.h | 8 +- inc/ocf_trace.h | 188 ++++++++++++++++++++++++++++++++++++++ src/engine/cache_engine.c | 2 +- src/ocf_cache_priv.h | 16 ++++ src/ocf_core.c | 36 ++++++-- src/ocf_core_priv.h | 6 ++ src/ocf_queue_priv.h | 6 ++ src/ocf_trace.c | 156 +++++++++++++++++++++++++++++++ src/ocf_trace_priv.h | 130 ++++++++++++++++++++++++++ src/utils/utils_io.c | 4 +- 11 files changed, 537 insertions(+), 16 deletions(-) create mode 100644 inc/ocf_trace.h create mode 100644 src/ocf_trace.c create mode 100644 src/ocf_trace_priv.h diff --git a/inc/ocf.h b/inc/ocf.h index 6ace002..d641413 100644 --- a/inc/ocf.h +++ b/inc/ocf.h @@ -33,5 +33,6 @@ #include "ocf_mngt.h" #include "ocf_ctx.h" #include "ocf_err.h" +#include "ocf_trace.h" #endif /* __OCF_H__ */ diff --git a/inc/ocf_io.h b/inc/ocf_io.h index 36cfadb..57f6b60 100644 --- a/inc/ocf_io.h +++ b/inc/ocf_io.h @@ -76,7 +76,7 @@ struct ocf_io { /** * @brief OCF IO destination class */ - uint32_t class; + uint32_t io_class; /** * @brief OCF IO direction @@ -157,15 +157,15 @@ void *ocf_io_get_priv(struct ocf_io *io); * @param[in] addr OCF IO destination address * @param[in] bytes OCF IO size in bytes * @param[in] dir OCF IO direction - * @param[in] class OCF IO destination class + * @param[in] io_class OCF IO destination class * @param[in] flags OCF IO flags */ static inline void ocf_io_configure(struct ocf_io *io, uint64_t addr, - uint32_t bytes, uint32_t dir, uint32_t class, uint64_t flags) + uint32_t bytes, uint32_t dir, uint32_t io_class, uint64_t flags) { io->addr = addr; io->bytes = bytes; - io->class = class; + io->io_class = io_class; io->flags = flags; io->dir = dir; } diff --git a/inc/ocf_trace.h b/inc/ocf_trace.h new file mode 100644 index 0000000..3971427 --- /dev/null +++ b/inc/ocf_trace.h @@ -0,0 +1,188 @@ +/* + * Copyright(c) 2012-2018 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __OCF_TRACE_H__ +#define __OCF_TRACE_H__ + +#include "ocf_def.h" +#include "ocf_types.h" + +typedef uint64_t log_sid_t; + +#define OCF_EVENT_VERSION 1 +#define OCF_TRACING_STOP 1 + +/** + * @brief OCF trace (event) type + */ +typedef enum { + /** IO trace description, this event is pushed first to indicate version + * of traces, number of cores and provides details about cache */ + ocf_event_type_cache_desc, + + /** Event describing ocf core */ + ocf_event_type_core_desc, + + /** IO */ + ocf_event_type_io, + + /** IO completion */ + ocf_event_type_io_cmpl, + + /** IO in file domain */ + ocf_event_type_io_file, +} ocf_event_type; + +/** + * @brief Generic OCF trace event + */ +struct ocf_event_hdr { + /** Event sequence ID */ + log_sid_t sid; + + /** Time stamp */ + uint64_t timestamp; + + /** Trace event type */ + ocf_event_type type; + + /** Size of this event */ + uint32_t size; +}; + +/** + * @brief Cache trace description +*/ +struct ocf_event_cache_desc { + /** Event header */ + struct ocf_event_hdr hdr; + + /** Cache Id */ + ocf_cache_id_t id; + + /** Cache line size */ + ocf_cache_line_size_t cache_line_size; + + /** Cache mode */ + ocf_cache_mode_t cache_mode; + + /** Cache size in bytes*/ + uint64_t cache_size; + + /** Number of cores */ + uint32_t cores_no; + + /** Number of IO queues */ + uint32_t io_queues_no; + + /** Trace version */ + uint32_t version; +}; + +/** + * @brief Core trace description +*/ +struct ocf_event_core_desc { + /** Event header */ + struct ocf_event_hdr hdr; + + /** Core Id */ + ocf_core_id_t id; + + /** Core size in bytes */ + uint64_t core_size; +}; + +/** @brief IO operation */ +typedef enum { + /** Read */ + ocf_event_operation_rd = 'R', + + /** Write */ + ocf_event_operation_wr = 'W', + + /** Flush */ + ocf_event_operation_flush = 'F', + + /** Discard */ + ocf_event_operation_discard = 'D', +} ocf_event_operation_t; + +/** + * @brief IO trace event + */ +struct ocf_event_io { + /** Trace event header */ + struct ocf_event_hdr hdr; + + /** Address of IO in bytes */ + uint64_t addr; + + /** Size of IO in bytes */ + uint32_t len; + + /** IO class of IO */ + uint32_t io_class; + + /** Core ID */ + ocf_core_id_t core_id; + + /** Operation type: read, write, trim or flush **/ + ocf_event_operation_t operation; +}; + +/** + * @brief IO completion event + */ +struct ocf_event_io_cmpl { + /** Trace event header */ + struct ocf_event_hdr hdr; + + /** Reference event sequence ID */ + log_sid_t rsid; + + /** Was IO a cache hit or miss */ + bool is_hit; +}; + + +/** @brief Push log callback. + * + * @param[in] cache OCF cache + * @param[in] trace_ctx Tracing context + * @param[in] qid Queue Id + * @param[out] trace Event log + * @param[out] size Size of event log + * + * @return 0 If pushing trace succeeded + * @return Non-zero error + */ +typedef void (*ocf_trace_callback_t)(ocf_cache_t cache, void *trace_ctx, + uint32_t qid, const void* trace, const uint32_t size); + +/** + * @brief Start tracing + * + * @param[in] cache OCF cache + * @param[in] trace_ctx Tracing context + * @param[in] trace_callback Callback used for pushing logs + * + * @retval 0 Tracing started successfully + * @retval Non-zero Error + */ +int ocf_mgnt_start_trace(ocf_cache_t cache, void *trace_ctx, + ocf_trace_callback_t trace_callback); + +/** + * @brief Stop tracing + * + * @param[in] cache OCF cache + * + * @retval 0 Tracing stopped successfully + * @retval Non-zero Error + */ +int ocf_mgnt_stop_trace(ocf_cache_t cache); + +#endif /* __OCF_TRACE_H__ */ diff --git a/src/engine/cache_engine.c b/src/engine/cache_engine.c index 0ff8dc6..075b9ab 100644 --- a/src/engine/cache_engine.c +++ b/src/engine/cache_engine.c @@ -222,7 +222,7 @@ ocf_cache_mode_t ocf_get_effective_cache_mode(ocf_cache_t cache, return ocf_cache_mode_pt; mode = ocf_part_get_cache_mode(cache, - ocf_part_class2id(cache, io->class)); + ocf_part_class2id(cache, io->io_class)); if (!ocf_cache_mode_is_valid(mode)) mode = cache->conf_meta->cache_mode; diff --git a/src/ocf_cache_priv.h b/src/ocf_cache_priv.h index 0d82164..bfe735b 100644 --- a/src/ocf_cache_priv.h +++ b/src/ocf_cache_priv.h @@ -17,10 +17,24 @@ #include "ocf_stats_priv.h" #include "cleaning/cleaning.h" #include "ocf_logger_priv.h" +#include "ocf/ocf_trace.h" #define DIRTY_FLUSHED 1 #define DIRTY_NOT_FLUSHED 0 +/** + * @brief Structure used for aggregating trace-related ocf_cache fields + */ +struct ocf_trace { + /* Placeholder for push_event callback */ + volatile ocf_trace_callback_t trace_callback; + + /* Telemetry context */ + volatile void *trace_ctx; + + env_atomic64 trace_seq_ref; +}; + struct ocf_metadata_uuid { uint32_t size; uint8_t data[OCF_DATA_OBJ_UUID_MAX_SIZE]; @@ -208,6 +222,8 @@ struct ocf_cache { bool use_submit_io_fast; void *cleaning_policy_context; + + struct ocf_trace trace; }; #define ocf_cache_log_prefix(cache, lvl, prefix, fmt, ...) \ diff --git a/src/ocf_core.c b/src/ocf_core.c index 0364101..45e1b98 100644 --- a/src/ocf_core.c +++ b/src/ocf_core.c @@ -13,6 +13,7 @@ #include "utils/utils_part.h" #include "utils/utils_device.h" #include "ocf_request.h" +#include "ocf_trace_priv.h" struct ocf_core_dobj { ocf_core_t core; @@ -287,7 +288,7 @@ static inline int ocf_core_validate_io(struct ocf_io *io) if (io->addr + io->bytes > ocf_dobj_get_length(io->obj)) return -EINVAL; - if (io->class >= OCF_IO_CLASS_MAX) + if (io->io_class >= OCF_IO_CLASS_MAX) return -EINVAL; if (io->dir != OCF_READ && io->dir != OCF_WRITE) @@ -304,6 +305,9 @@ static inline int ocf_core_validate_io(struct ocf_io *io) static void ocf_req_complete(struct ocf_request *req, int error) { + /* Log trace */ + ocf_trace_io_cmpl(ocf_io_to_core_io(req->io), req->cache); + /* Complete IO */ ocf_io_end(req->io, error); @@ -335,6 +339,8 @@ void ocf_core_submit_io_mode(struct ocf_io *io, ocf_cache_mode_t cache_mode) core = ocf_data_obj_to_core(io->obj); cache = ocf_core_get_cache(core); + ocf_trace_init_io(core_io, cache); + if (unlikely(!env_bit_test(ocf_cache_state_running, &cache->cache_state))) { ocf_io_end(io, -EIO); @@ -358,9 +364,6 @@ void ocf_core_submit_io_mode(struct ocf_io *io, ocf_cache_mode_t cache_mode) dec_counter_if_req_was_dirty(core_io, cache); } - if (cache->conf_meta->valid_parts_no <= 1) - io->class = 0; - core_io->req = ocf_req_new(cache, ocf_core_get_id(core), io->addr, io->bytes, io->dir); if (!core_io->req) { @@ -373,7 +376,7 @@ void ocf_core_submit_io_mode(struct ocf_io *io, ocf_cache_mode_t cache_mode) req_cache_mode = ocf_req_cache_mode_d2c; core_io->req->io_queue = io->io_queue; - core_io->req->part_id = ocf_part_class2id(cache, io->class); + core_io->req->part_id = ocf_part_class2id(cache, io->io_class); core_io->req->data = core_io->data; core_io->req->complete = ocf_req_complete; core_io->req->io = io; @@ -382,6 +385,11 @@ void ocf_core_submit_io_mode(struct ocf_io *io, ocf_cache_mode_t cache_mode) ocf_core_update_stats(core, io); + if (io->dir == OCF_WRITE) + ocf_trace_io(core_io, ocf_event_operation_wr, cache); + else if (io->dir == OCF_READ) + ocf_trace_io(core_io, ocf_event_operation_rd, cache); + ocf_io_get(io); ret = ocf_engine_hndl_req(core_io->req, req_cache_mode); if (ret) { @@ -395,6 +403,7 @@ int ocf_core_submit_io_fast(struct ocf_io *io) { struct ocf_core_io *core_io; ocf_req_cache_mode_t req_cache_mode; + struct ocf_event_io trace_event; struct ocf_request *req; ocf_core_t core; ocf_cache_t cache; @@ -444,9 +453,6 @@ int ocf_core_submit_io_fast(struct ocf_io *io) req_cache_mode = ocf_req_cache_mode_fast; } - if (cache->conf_meta->valid_parts_no <= 1) - io->class = 0; - core_io->req = ocf_req_new_extended(cache, ocf_core_get_id(core), io->addr, io->bytes, io->dir); // We need additional pointer to req in case completion arrives before @@ -465,16 +471,26 @@ int ocf_core_submit_io_fast(struct ocf_io *io) } req->io_queue = io->io_queue; - req->part_id = ocf_part_class2id(cache, io->class); + req->part_id = ocf_part_class2id(cache, io->io_class); req->data = core_io->data; req->complete = ocf_req_complete; req->io = io; ocf_core_update_stats(core, io); + + if (cache->trace.trace_callback) { + if (io->dir == OCF_WRITE) + ocf_trace_prep_io_event(&trace_event, core_io, ocf_event_operation_wr); + else if (io->dir == OCF_READ) + ocf_trace_prep_io_event(&trace_event, core_io, ocf_event_operation_rd); + } + ocf_io_get(io); fast = ocf_engine_hndl_fast_req(req, req_cache_mode); if (fast != OCF_FAST_PATH_NO) { + ocf_trace_push(cache, core_io->req->io_queue, + &trace_event, sizeof(trace_event)); ocf_seq_cutoff_update(core, req); return 0; } @@ -529,6 +545,7 @@ static void ocf_core_data_obj_submit_flush(struct ocf_io *io) core_io->req->io = io; core_io->req->data = core_io->data; + ocf_trace_io(core_io, ocf_event_operation_flush, cache); ocf_io_get(io); ocf_engine_hndl_ops_req(core_io->req); } @@ -571,6 +588,7 @@ static void ocf_core_data_obj_submit_discard(struct ocf_io *io) core_io->req->io = io; core_io->req->data = core_io->data; + ocf_trace_io(core_io, ocf_event_operation_discard, cache); ocf_io_get(io); ocf_engine_hndl_discard_req(core_io->req); } diff --git a/src/ocf_core_priv.h b/src/ocf_core_priv.h index a6597ae..a887602 100644 --- a/src/ocf_core_priv.h +++ b/src/ocf_core_priv.h @@ -24,6 +24,12 @@ struct ocf_core_io { struct ocf_request *req; ctx_data_t *data; + + log_sid_t sid; + /*!< Sequence ID */ + + uint64_t timestamp; + /*!< Timestamp */ }; struct ocf_core { diff --git a/src/ocf_queue_priv.h b/src/ocf_queue_priv.h index a067675..9989b11 100644 --- a/src/ocf_queue_priv.h +++ b/src/ocf_queue_priv.h @@ -17,6 +17,12 @@ struct ocf_queue { struct list_head io_list; env_spinlock io_list_lock; + /* Tracing reference counter */ + env_atomic64 trace_ref_cntr; + + /* Tracing stop request */ + env_atomic trace_stop; + void *priv; }; diff --git a/src/ocf_trace.c b/src/ocf_trace.c new file mode 100644 index 0000000..520c80b --- /dev/null +++ b/src/ocf_trace.c @@ -0,0 +1,156 @@ +/* + * Copyright(c) 2012-2018 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#include "ocf_env.h" +#include "ocf_priv.h" +#include "ocf/ocf.h" +#include "ocf/ocf_trace.h" +#include "ocf_core_priv.h" +#include "ocf_cache_priv.h" +#include "ocf_trace_priv.h" + +struct core_trace_visitor_ctx { + ocf_cache_t cache; + uint32_t io_queue; +}; + +static int _ocf_core_desc(ocf_core_t core, void *ctx) +{ + struct ocf_event_core_desc core_desc; + struct core_trace_visitor_ctx *visitor_ctx = + (struct core_trace_visitor_ctx *) ctx; + ocf_cache_t cache = visitor_ctx->cache; + + ocf_event_init_hdr(&core_desc.hdr, ocf_event_type_core_desc, + ocf_trace_seq_id(cache), env_get_tick_ns(), sizeof(core_desc)); + core_desc.id = ocf_core_get_id(core); + core_desc.core_size = ocf_dobj_get_length( + ocf_core_get_data_object(core)); + + ocf_trace_push(cache, visitor_ctx->io_queue, + &core_desc, sizeof(core_desc)); + + return 0; +} + +static int _ocf_trace_cache_info(ocf_cache_t cache, uint32_t io_queue) +{ + struct ocf_event_cache_desc cache_desc; + int retval; + struct core_trace_visitor_ctx visitor_ctx; + + ocf_event_init_hdr(&cache_desc.hdr, ocf_event_type_cache_desc, + ocf_trace_seq_id(cache), env_get_tick_ns(), sizeof(cache_desc)); + + cache_desc.id = ocf_cache_get_id(cache); + cache_desc.cache_line_size = ocf_cache_get_line_size(cache); + cache_desc.cache_mode = ocf_cache_get_mode(cache); + + if (ocf_cache_is_device_attached(cache)) { + /* Attached cache */ + cache_desc.cache_size = ocf_dobj_get_length( + ocf_cache_get_data_object(cache)); + } else { + cache_desc.cache_size = 0; + } + + cache_desc.cores_no = ocf_cache_get_core_count(cache); + cache_desc.version = OCF_EVENT_VERSION; + cache_desc.io_queues_no = cache->io_queues_no; + + ocf_trace_push(cache, io_queue, &cache_desc, sizeof(cache_desc)); + + visitor_ctx.cache = cache; + visitor_ctx.io_queue = io_queue; + + retval = ocf_core_visit(cache, _ocf_core_desc, &visitor_ctx, true); + + return retval; +} + +int ocf_mgnt_start_trace(ocf_cache_t cache, void *trace_ctx, + ocf_trace_callback_t trace_callback) +{ + int result; + uint32_t i; + + OCF_CHECK_NULL(cache); + + if (!trace_callback) { + return -EINVAL; + } + + result = ocf_mngt_cache_lock(cache); + if (result) + return result; + + if (cache->trace.trace_callback) { + ocf_cache_log(cache, log_err, + "Tracing already started for cache %u\n", + ocf_cache_get_id(cache)); + ocf_mngt_cache_unlock(cache); + return -EINVAL; + } + + cache->trace.trace_callback = trace_callback; + cache->trace.trace_ctx = trace_ctx; + + // Reset trace stop flag + for (int queue = 0; queue < cache->io_queues_no; queue++) { + env_atomic_set(&cache->io_queues[queue].trace_stop, 0); + } + + for (i = 0; i < cache->io_queues_no; i++) { + result = _ocf_trace_cache_info(cache, i); + if (result) + goto trace_deinit; + } + + ocf_cache_log(cache, log_info, + "Tracing started for cache %u\n", ocf_cache_get_id(cache)); + + ocf_mngt_cache_unlock(cache); + return result; + +trace_deinit: + cache->trace.trace_callback = NULL; + ocf_mngt_cache_unlock(cache); + + return result; +} + +int ocf_mgnt_stop_trace(ocf_cache_t cache) +{ + int result; + + result = ocf_mngt_cache_lock(cache); + if (result) + return result; + + if (!cache->trace.trace_callback) { + ocf_cache_log(cache, log_err, + "Tracing not started for cache %u\n", + ocf_cache_get_id(cache)); + ocf_mngt_cache_unlock(cache); + return -EINVAL; + } + + // Set trace stop flag + for (int queue = 0; queue < cache->io_queues_no; queue++) { + env_atomic_set(&cache->io_queues[queue].trace_stop, OCF_TRACING_STOP); + } + + cache->trace.trace_callback = NULL; + cache->trace.trace_ctx = NULL; + + // Poll for all ongoing traces completion + while (ocf_is_trace_ongoing(cache)) { + env_msleep(20); + } + + ocf_mngt_cache_unlock(cache); + + return result; +} diff --git a/src/ocf_trace_priv.h b/src/ocf_trace_priv.h new file mode 100644 index 0000000..d447285 --- /dev/null +++ b/src/ocf_trace_priv.h @@ -0,0 +1,130 @@ +/* + * Copyright(c) 2012-2018 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __OCF_TRACE_PRIV_H__ +#define __OCF_TRACE_PRIV_H__ + +#include "ocf/ocf_trace.h" +#include "engine/engine_common.h" +#include "ocf_request.h" +#include "ocf_core_priv.h" +#include "ocf_queue_priv.h" + +static inline bool ocf_is_trace_ongoing(ocf_cache_t cache) +{ + for (int i = 0; i < cache->io_queues_no; i++) { + if (env_atomic64_read(&cache->io_queues[i].trace_ref_cntr)) { + return true; + } + } + + return false; +} +static inline void ocf_event_init_hdr(struct ocf_event_hdr *hdr, + ocf_event_type type, uint64_t sid, uint64_t timestamp, + uint32_t size) +{ + hdr->sid = sid; + hdr->timestamp = timestamp; + hdr->type = type; + hdr->size = size; +} + +static inline uint64_t ocf_trace_seq_id(ocf_cache_t cache) +{ + return env_atomic64_inc_return(&cache->trace.trace_seq_ref); +} + +static inline void ocf_trace_init_io(struct ocf_core_io *io, ocf_cache_t cache) +{ + io->timestamp = env_get_tick_ns(); + io->sid = ocf_trace_seq_id(cache); +} + +static inline void ocf_trace_prep_io_event(struct ocf_event_io *ev, + struct ocf_core_io *io, ocf_event_operation_t op) +{ + struct ocf_request *rq = io->req; + + ocf_event_init_hdr(&ev->hdr, ocf_event_type_io, io->sid, + io->timestamp, sizeof(*ev)); + + ev->addr = rq->byte_position; + if (op == ocf_event_operation_discard) + ev->len = rq->discard.nr_sects << ENV_SECTOR_SHIFT; + else + ev->len = rq->byte_length; + + ev->operation = op; + ev->core_id = rq->core_id; + + ev->io_class = rq->io->io_class; +} + +static inline void ocf_trace_push(ocf_cache_t cache, uint32_t io_queue, + void *trace, uint32_t size) +{ + ocf_trace_callback_t trace_callback; + void *trace_ctx; + + if (cache->trace.trace_callback == NULL) + return; + + env_atomic64_inc(&cache->io_queues[io_queue].trace_ref_cntr); + + if (env_atomic_read(&cache->io_queues[io_queue].trace_stop)) { + // Tracing stop was requested + env_atomic64_dec(&cache->io_queues[io_queue].trace_ref_cntr); + return; + } + + /* + * Remember callback and context pointers. + * These will be valid even when later on original pointers + * will be set to NULL as cleanup will wait till trace + * reference counter is zero + */ + trace_callback = cache->trace.trace_callback; + trace_ctx = cache->trace.trace_ctx; + + if (trace_callback && trace_ctx) { + trace_callback(cache, trace_ctx, io_queue, trace, size); + } + + env_atomic64_dec(&cache->io_queues[io_queue].trace_ref_cntr); +} + +static inline void ocf_trace_io(struct ocf_core_io *io, ocf_event_operation_t dir, ocf_cache_t cache) +{ + struct ocf_event_io ev; + struct ocf_request *rq; + + if (!cache->trace.trace_callback) + return; + + rq = io->req; + ocf_trace_prep_io_event(&ev, io, dir); + + ocf_trace_push(cache, rq->io_queue, &ev, sizeof(ev)); +} + +static inline void ocf_trace_io_cmpl(struct ocf_core_io *io, ocf_cache_t cache) +{ + struct ocf_event_io_cmpl ev; + struct ocf_request *rq; + + if (!cache->trace.trace_callback) + return; + + rq = io->req; + ocf_event_init_hdr(&ev.hdr, ocf_event_type_io_cmpl, + ocf_trace_seq_id(cache), env_get_tick_ns(), sizeof(ev)); + ev.rsid = io->sid; + ev.is_hit = ocf_engine_is_hit(rq); + + ocf_trace_push(cache, rq->io_queue, &ev, sizeof(ev)); +} + +#endif /* __OCF_TRACE_PRIV_H__ */ diff --git a/src/utils/utils_io.c b/src/utils/utils_io.c index ca632ea..30e1bae 100644 --- a/src/utils/utils_io.c +++ b/src/utils/utils_io.c @@ -217,7 +217,7 @@ void ocf_submit_cache_reqs(struct ocf_cache *cache, { struct ocf_counters_block *cache_stats; uint64_t flags = req->io ? req->io->flags : 0; - uint32_t class = req->io ? req->io->class : 0; + uint32_t class = req->io ? req->io->io_class : 0; uint64_t addr, bytes, total_bytes = 0; struct ocf_io *io; uint32_t i; @@ -317,7 +317,7 @@ void ocf_submit_obj_req(ocf_data_obj_t obj, struct ocf_request *req, struct ocf_cache *cache = req->cache; struct ocf_counters_block *core_stats; uint64_t flags = req->io ? req->io->flags : 0; - uint32_t class = req->io ? req->io->class : 0; + uint32_t class = req->io ? req->io->io_class : 0; int dir = req->rw; struct ocf_io *io; int err;