Skip to content

Commit e630b81

Browse files
Merge pull request Open-CAS#858 from robertbaldyga/attach-fix-race-condition
Fix race condition during cache attach
2 parents 2c28f33 + b850727 commit e630b81

File tree

6 files changed

+75
-13
lines changed

6 files changed

+75
-13
lines changed

src/engine/cache_engine.c

+5
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ void ocf_resolve_effective_cache_mode(ocf_cache_t cache,
162162
return;
163163
}
164164

165+
if (env_atomic_read(&cache->attach_pt)) {
166+
req->cache_mode = ocf_req_cache_mode_pt;
167+
return;
168+
}
169+
165170
if (cache->pt_unaligned_io && !ocf_req_is_4k(req->addr, req->bytes)) {
166171
req->cache_mode = ocf_req_cache_mode_pt;
167172
return;

src/mngt/ocf_mngt_cache.c

+20-3
Original file line numberDiff line numberDiff line change
@@ -1901,18 +1901,35 @@ static void _ocf_mngt_attach_shutdown_status(ocf_pipeline_t pipeline,
19011901
_ocf_mngt_attach_shutdown_status_complete, context);
19021902
}
19031903

1904+
1905+
static void _ocf_mngt_attach_post_init_finish(void *priv)
1906+
{
1907+
struct ocf_cache_attach_context *context = priv;
1908+
ocf_cache_t cache = context->cache;
1909+
1910+
ocf_refcnt_unfreeze(&cache->refcnt.d2c);
1911+
1912+
env_atomic_set(&cache->attach_pt, 0);
1913+
1914+
ocf_cache_log(cache, log_debug, "Cache attached\n");
1915+
1916+
ocf_pipeline_next(context->pipeline);
1917+
}
1918+
19041919
static void _ocf_mngt_attach_post_init(ocf_pipeline_t pipeline,
19051920
void *priv, ocf_pipeline_arg_t arg)
19061921
{
19071922
struct ocf_cache_attach_context *context = priv;
19081923
ocf_cache_t cache = context->cache;
19091924

1925+
env_atomic_set(&cache->attach_pt, 1);
1926+
19101927
ocf_cleaner_refcnt_unfreeze(cache);
19111928
ocf_refcnt_unfreeze(&cache->refcnt.metadata);
19121929

1913-
ocf_cache_log(cache, log_debug, "Cache attached\n");
1914-
1915-
ocf_pipeline_next(pipeline);
1930+
ocf_refcnt_freeze(&cache->refcnt.d2c);
1931+
ocf_refcnt_register_zero_cb(&cache->refcnt.d2c,
1932+
_ocf_mngt_attach_post_init_finish, context);
19161933
}
19171934

19181935
static void _ocf_mngt_attach_handle_error(

src/ocf_cache_priv.h

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ struct ocf_cache {
106106
env_atomic flush_in_progress;
107107
env_mutex flush_mutex;
108108

109+
env_atomic attach_pt;
110+
109111
struct ocf_cleaner cleaner;
110112

111113
struct list_head io_queues;

tests/functional/pyocf/types/cache.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ def alloc_device_config(self, device, perform_test=True):
569569
def free_device_config(self, cfg):
570570
lib = OcfLib.getInstance().ocf_volume_destroy(cfg._volume)
571571

572-
def attach_device(
572+
def attach_device_async(
573573
self,
574574
device,
575575
force=False,
@@ -593,14 +593,39 @@ def attach_device(
593593

594594
self.write_lock()
595595

596-
c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)])
596+
def callback(c):
597+
self.write_unlock()
598+
self.free_device_config(device_config)
599+
600+
c = OcfCompletion(
601+
[("cache", c_void_p), ("priv", c_void_p), ("error", c_int)],
602+
callback=callback
603+
)
597604

598605
self.owner.lib.ocf_mngt_cache_attach(self.cache_handle, byref(attach_cfg), c, None)
599-
c.wait()
600606

601-
self.write_unlock()
607+
return c
602608

603-
self.free_device_config(device_config)
609+
def attach_device(
610+
self,
611+
device,
612+
force=False,
613+
perform_test=False,
614+
cache_line_size=None,
615+
open_cores=False,
616+
disable_cleaner=False,
617+
):
618+
619+
c = self.attach_device_async(
620+
device,
621+
force,
622+
perform_test,
623+
cache_line_size,
624+
open_cores,
625+
disable_cleaner
626+
)
627+
628+
c.wait()
604629

605630
if c.results["error"]:
606631
raise OcfError(

tests/functional/pyocf/types/shared.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def __getitem__(self, key):
8383
except KeyError:
8484
raise KeyError(f"No completion argument {key} specified")
8585

86-
def __init__(self, completion_args: list, context=None):
86+
def __init__(self, completion_args: list, context=None, callback=None):
8787
"""
8888
Provide ctypes arg list, and optionally index of status argument in
8989
completion function which will be extracted (default - last argument).
@@ -95,13 +95,16 @@ def __init__(self, completion_args: list, context=None):
9595
self.results = OcfCompletion.CompletionResult(completion_args)
9696
self._as_parameter_ = self.callback
9797
self.context = context
98+
self.user_callback = callback
9899

99100
@property
100101
def callback(self):
101102
@CFUNCTYPE(c_void_p, *self.results.arg_types)
102103
def complete(*args):
103104
self.results.results = args
104105
self.e.set()
106+
if self.user_callback:
107+
self.user_callback(self)
105108

106109
return complete
107110

tests/functional/tests/engine/test_d2c.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55

66

7+
from time import sleep
78
import pytest
89

910

@@ -18,7 +19,6 @@
1819
CORE_SIZE = 4096
1920

2021

21-
@pytest.mark.xfail(reason="Data corruption when switching from D2C")
2222
def test_d2c_io(pyocf_ctx):
2323
"""
2424
Start cache in D2C
@@ -46,7 +46,8 @@ def test_d2c_io(pyocf_ctx):
4646
d2c_data.write(b"a" * CORE_SIZE, CORE_SIZE)
4747
d2c_io.set_data(d2c_data)
4848

49-
cache.attach_device(cache_device)
49+
c = cache.attach_device_async(cache_device)
50+
sleep(1)
5051

5152
wt_io = vol.new_io(queue, 0, CORE_SIZE, IoDir.WRITE, 0, 0)
5253
wt_data = Data(CORE_SIZE)
@@ -55,18 +56,27 @@ def test_d2c_io(pyocf_ctx):
5556

5657
wt_completion = Sync(wt_io).submit()
5758
assert int(wt_completion.results["err"]) == 0
58-
assert cache.get_stats()["req"]["wr_full_misses"]["value"] == 1
5959

6060
d2c_completion = Sync(d2c_io).submit()
6161
assert int(d2c_completion.results["err"]) == 0
62-
assert cache.get_stats()["req"]["wr_pt"]["value"] == 1
62+
63+
c.wait()
64+
65+
if c.results["error"]:
66+
raise OcfError(
67+
f"Attaching cache device failed",
68+
c.results["error"],
69+
)
70+
71+
assert cache.get_stats()["req"]["wr_pt"]["value"] == 2
6372

6473
read_io = vol.new_io(queue, 0, CORE_SIZE, IoDir.READ, 0, 0)
6574
read_data = Data(CORE_SIZE)
6675
read_io.set_data(read_data)
6776

6877
read_completion = Sync(read_io).submit()
6978
assert int(read_completion.results["err"]) == 0
79+
assert cache.get_stats()["req"]["rd_full_misses"]["value"] == 1
7080

7181
cache.stop()
7282

0 commit comments

Comments
 (0)