From 0192ce23dd73515fc22f9ceb5a74d8b42dce7101 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Thu, 17 Jun 2021 09:42:58 +0200 Subject: [PATCH 1/3] Reorder metadata updating patter in WB mode In WB mode metadata should be updated only if the actuall data had been saved on disk. Otherwise metadata might be flushed too early and consequently data corruption might occur. Signed-off-by: Michal Mielewczyk --- src/engine/engine_wb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/engine/engine_wb.c b/src/engine/engine_wb.c index 7823abb..284a0d5 100644 --- a/src/engine/engine_wb.c +++ b/src/engine/engine_wb.c @@ -74,6 +74,8 @@ static int ocf_write_wb_do_flush_metadata(struct ocf_request *req) env_atomic_set(&req->req_remaining, 1); /* One core IO */ + _ocf_write_wb_update_bits(req); + if (req->info.flush_metadata) { OCF_DEBUG_RQ(req, "Flush metadata"); ocf_metadata_flush_do_asynch(cache, req, @@ -152,9 +154,6 @@ int ocf_write_wb_do(struct ocf_request *req) /* Get OCF request - increase reference counter */ ocf_req_get(req); - /* Update status bits */ - _ocf_write_wb_update_bits(req); - /* Submit IO */ _ocf_write_wb_submit(req); From c9294d1f069013c20b5448720789b7bb707341c8 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Fri, 18 Jun 2021 12:49:31 +0200 Subject: [PATCH 2/3] Reorder metadata updating pattern in WT mode There's a possibility that WT write is performed to dirty cache line (i.e. after switching WB->WT without flush) and status bits change from dirty to clean. If power failure occurs it might happen that recovery would ignore recent data from cache and would assume that data is clean while backend storage data is out of date. Signed-off-by: Michal Mielewczyk --- src/engine/engine_wt.c | 131 +++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 52 deletions(-) diff --git a/src/engine/engine_wt.c b/src/engine/engine_wt.c index 3b0b33f..dd2e0c4 100644 --- a/src/engine/engine_wt.c +++ b/src/engine/engine_wt.c @@ -18,6 +18,84 @@ #define OCF_ENGINE_DEBUG_IO_NAME "wt" #include "engine_debug.h" +static void _ocf_write_wt_update_bits(struct ocf_request *req) +{ + bool miss = ocf_engine_is_miss(req); + bool dirty_any = req->info.dirty_any; + bool repart = ocf_engine_needs_repart(req); + + if (!miss && !dirty_any && !repart) + return; + + ocf_hb_req_prot_lock_wr(req); + + if (miss) { + /* Update valid status bits */ + ocf_set_valid_map_info(req); + } + + if (dirty_any) { + /* Writes goes to both cache and core, need to update + * status bits from dirty to clean + */ + ocf_set_clean_map_info(req); + } + + if (repart) { + OCF_DEBUG_RQ(req, "Re-Part"); + /* Probably some cache lines are assigned into wrong + * partition. Need to move it to new one + */ + ocf_user_part_move(req); + } + + ocf_hb_req_prot_unlock_wr(req); +} + +static void _ocf_write_wt_do_flush_metadata_compl(struct ocf_request *req, + int error) +{ + if (error) + req->error = error; + + if (env_atomic_dec_return(&req->req_remaining)) + return; + + if (req->error) + ocf_engine_error(req, true, "Failed to write data to cache"); + + ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req); + + req->complete(req, req->info.core_error ? req->error : 0); + + ocf_req_put(req); +} + +static int ocf_write_wt_do_flush_metadata(struct ocf_request *req) +{ + struct ocf_cache *cache = req->cache; + + env_atomic_set(&req->req_remaining, 1); + + _ocf_write_wt_update_bits(req); + + if (req->info.flush_metadata) { + /* Metadata flush IO */ + + ocf_metadata_flush_do_asynch(cache, req, + _ocf_write_wt_do_flush_metadata_compl); + } + + _ocf_write_wt_do_flush_metadata_compl(req, 0); + + return 0; +} + +static const struct ocf_io_if _io_if_wt_flush_metadata = { + .read = ocf_write_wt_do_flush_metadata, + .write = ocf_write_wt_do_flush_metadata, +}; + static void _ocf_write_wt_req_complete(struct ocf_request *req) { if (env_atomic_dec_return(&req->req_remaining)) @@ -33,14 +111,7 @@ static void _ocf_write_wt_req_complete(struct ocf_request *req) ocf_engine_invalidate(req); } else { - /* Unlock reqest from WRITE access */ - ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req); - - /* Complete request */ - req->complete(req, req->info.core_error ? req->error : 0); - - /* Release OCF request */ - ocf_req_put(req); + ocf_engine_push_req_front_if(req, &_io_if_wt_flush_metadata, true); } } @@ -79,13 +150,6 @@ static inline void _ocf_write_wt_submit(struct ocf_request *req) env_atomic_set(&req->req_remaining, ocf_engine_io_count(req)); /* Cache IO */ env_atomic_inc(&req->req_remaining); /* Core device IO */ - if (req->info.flush_metadata) { - /* Metadata flush IO */ - - ocf_metadata_flush_do_asynch(cache, req, - _ocf_write_wt_cache_complete); - } - /* To cache */ ocf_submit_cache_reqs(cache, req, OCF_WRITE, 0, req->byte_length, ocf_engine_io_count(req), _ocf_write_wt_cache_complete); @@ -95,48 +159,11 @@ static inline void _ocf_write_wt_submit(struct ocf_request *req) _ocf_write_wt_core_complete); } -static void _ocf_write_wt_update_bits(struct ocf_request *req) -{ - bool miss = ocf_engine_is_miss(req); - bool dirty_any = req->info.dirty_any; - bool repart = ocf_engine_needs_repart(req); - - if (!miss && !dirty_any && !repart) - return; - - ocf_hb_req_prot_lock_wr(req); - - if (miss) { - /* Update valid status bits */ - ocf_set_valid_map_info(req); - } - - if (dirty_any) { - /* Writes goes to both cache and core, need to update - * status bits from dirty to clean - */ - ocf_set_clean_map_info(req); - } - - if (repart) { - OCF_DEBUG_RQ(req, "Re-Part"); - /* Probably some cache lines are assigned into wrong - * partition. Need to move it to new one - */ - ocf_user_part_move(req); - } - - ocf_hb_req_prot_unlock_wr(req); -} - static int _ocf_write_wt_do(struct ocf_request *req) { /* Get OCF request - increase reference counter */ ocf_req_get(req); - /* Update status bits */ - _ocf_write_wt_update_bits(req); - /* Submit IO */ _ocf_write_wt_submit(req); From f0564dcf75506571d9f73bbf869c5d642a7f438d Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 23 Jun 2021 09:27:36 +0200 Subject: [PATCH 3/3] Avoid unnecessary metadata flushes in WT Flushing metadata in WT is required only if at least of the request's cacheline changed its state to clean. Signed-off-by: Michal Mielewczyk --- src/engine/engine_wt.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/engine/engine_wt.c b/src/engine/engine_wt.c index dd2e0c4..4714325 100644 --- a/src/engine/engine_wt.c +++ b/src/engine/engine_wt.c @@ -110,8 +110,19 @@ static void _ocf_write_wt_req_complete(struct ocf_request *req) req->complete(req, req->info.core_error ? req->error : 0); ocf_engine_invalidate(req); - } else { + + return; + } + + if (req->info.dirty_any) { + /* Some of the request's cachelines changed its state to clean */ ocf_engine_push_req_front_if(req, &_io_if_wt_flush_metadata, true); + } else { + ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req); + + req->complete(req, req->info.core_error ? req->error : 0); + + ocf_req_put(req); } } @@ -164,6 +175,13 @@ static int _ocf_write_wt_do(struct ocf_request *req) /* Get OCF request - increase reference counter */ ocf_req_get(req); + if (!req->info.dirty_any) { + /* Set metadata bits before the request submission only if the dirty + status for any of the request's cachelines won't change */ + _ocf_write_wt_update_bits(req); + ENV_BUG_ON(req->info.flush_metadata); + } + /* Submit IO */ _ocf_write_wt_submit(req);