Synchronize access to cleaner shared structures

Cleaning policy callbacks are typically  called with hash buckets or
cache lines locked. However cleaning policies maintain structures
which are shared across multiple cache lines (e.g. ALRU list).
Additional synchronization is required for theese structures to
maintain integrity.

ACP already implements hash bucket locks. This patch is adding
ALRU list mutex.

Signed-off-by: Adam Rutkowski <adam.j.rutkowski@intel.com>
This commit is contained in:
Adam Rutkowski 2019-09-23 11:08:27 -04:00
parent 5e28474322
commit 937b010ef6
2 changed files with 56 additions and 53 deletions

View File

@ -636,6 +636,8 @@ void cleaning_policy_acp_purge_block(struct ocf_cache *cache,
struct acp_cleaning_policy_meta *acp_meta; struct acp_cleaning_policy_meta *acp_meta;
struct acp_chunk_info *chunk; struct acp_chunk_info *chunk;
ACP_LOCK_CHUNKS_WR();
acp_meta = _acp_meta_get(cache, cache_line, &policy_meta); acp_meta = _acp_meta_get(cache, cache_line, &policy_meta);
chunk = _acp_get_chunk(cache, cache_line); chunk = _acp_get_chunk(cache, cache_line);
@ -646,6 +648,8 @@ void cleaning_policy_acp_purge_block(struct ocf_cache *cache,
} }
_acp_update_bucket(acp, chunk); _acp_update_bucket(acp, chunk);
ACP_UNLOCK_CHUNKS_WR();
} }
int cleaning_policy_acp_purge_range(struct ocf_cache *cache, int cleaning_policy_acp_purge_range(struct ocf_cache *cache,

View File

@ -43,12 +43,6 @@
#define OCF_DEBUG_PARAM(cache, format, ...) #define OCF_DEBUG_PARAM(cache, format, ...)
#endif #endif
struct flush_merge_struct {
ocf_cache_line_t cache_line;
ocf_core_id_t core_id;
uint64_t core_sector;
};
struct alru_flush_ctx { struct alru_flush_ctx {
struct ocf_cleaner_attribs attribs; struct ocf_cleaner_attribs attribs;
bool flush_perfomed; bool flush_perfomed;
@ -59,6 +53,12 @@ struct alru_flush_ctx {
size_t flush_data_limit; size_t flush_data_limit;
}; };
struct alru_context {
struct alru_flush_ctx flush_ctx;
env_spinlock list_lock[OCF_IO_CLASS_MAX];
};
/* -- Start of ALRU functions -- */ /* -- Start of ALRU functions -- */
@ -319,20 +319,29 @@ void cleaning_policy_alru_init_cache_block(struct ocf_cache *cache,
void cleaning_policy_alru_purge_cache_block(struct ocf_cache *cache, void cleaning_policy_alru_purge_cache_block(struct ocf_cache *cache,
uint32_t cache_line) uint32_t cache_line)
{ {
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line); cache_line);
env_spinlock_lock(&alru->list_lock[part_id]);
remove_alru_list(cache, part_id, cache_line); remove_alru_list(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
} }
static void __cleaning_policy_alru_purge_cache_block_any( static void __cleaning_policy_alru_purge_cache_block_any(
struct ocf_cache *cache, uint32_t cache_line) struct ocf_cache *cache, uint32_t cache_line)
{ {
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line); cache_line);
env_spinlock_lock(&alru->list_lock[part_id]);
if (is_on_alru_list(cache, part_id, cache_line)) if (is_on_alru_list(cache, part_id, cache_line))
remove_alru_list(cache, part_id, cache_line); remove_alru_list(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
} }
int cleaning_policy_alru_purge_range(struct ocf_cache *cache, int core_id, int cleaning_policy_alru_purge_range(struct ocf_cache *cache, int core_id,
@ -357,6 +366,7 @@ int cleaning_policy_alru_purge_range(struct ocf_cache *cache, int core_id,
void cleaning_policy_alru_set_hot_cache_line(struct ocf_cache *cache, void cleaning_policy_alru_set_hot_cache_line(struct ocf_cache *cache,
uint32_t cache_line) uint32_t cache_line)
{ {
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line); cache_line);
struct ocf_user_part *part = &cache->user_parts[part_id]; struct ocf_user_part *part = &cache->user_parts[part_id];
@ -368,6 +378,8 @@ void cleaning_policy_alru_set_hot_cache_line(struct ocf_cache *cache,
ENV_WARN_ON(!metadata_test_dirty(cache, cache_line)); ENV_WARN_ON(!metadata_test_dirty(cache, cache_line));
ENV_WARN_ON(!metadata_test_valid_any(cache, cache_line)); ENV_WARN_ON(!metadata_test_valid_any(cache, cache_line));
env_spinlock_lock(&alru->list_lock[part_id]);
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy); ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
next_lru_node = policy.meta.alru.lru_next; next_lru_node = policy.meta.alru.lru_next;
prev_lru_node = policy.meta.alru.lru_prev; prev_lru_node = policy.meta.alru.lru_prev;
@ -381,6 +393,8 @@ void cleaning_policy_alru_set_hot_cache_line(struct ocf_cache *cache,
remove_alru_list(cache, part_id, cache_line); remove_alru_list(cache, part_id, cache_line);
add_alru_head(cache, part_id, cache_line); add_alru_head(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
} }
static void _alru_rebuild(struct ocf_cache *cache) static void _alru_rebuild(struct ocf_cache *cache)
@ -452,15 +466,19 @@ int cleaning_policy_alru_initialize(ocf_cache_t cache, int init_metadata)
{ {
struct ocf_user_part *part; struct ocf_user_part *part;
ocf_part_id_t part_id; ocf_part_id_t part_id;
struct alru_flush_ctx *fctx; struct alru_context *alru;
unsigned i;
fctx = env_vzalloc(sizeof(*fctx)); alru = env_vzalloc(sizeof(*alru));
if (!fctx) { if (!alru) {
ocf_cache_log(cache, log_err, "alru ctx allocation error\n"); ocf_cache_log(cache, log_err, "alru context allocation error\n");
return -OCF_ERR_NO_MEM; return -OCF_ERR_NO_MEM;
} }
cache->cleaner.cleaning_policy_context = fctx; for (i = 0; i < OCF_IO_CLASS_MAX; i++)
env_spinlock_init(&alru->list_lock[i]);
cache->cleaner.cleaning_policy_context = alru;
for_each_part(cache, part, part_id) { for_each_part(cache, part, part_id) {
cleaning_policy_alru_initialize_part(cache, cleaning_policy_alru_initialize_part(cache,
@ -477,6 +495,12 @@ int cleaning_policy_alru_initialize(ocf_cache_t cache, int init_metadata)
void cleaning_policy_alru_deinitialize(struct ocf_cache *cache) void cleaning_policy_alru_deinitialize(struct ocf_cache *cache)
{ {
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
unsigned i;
for (i = 0; i < OCF_IO_CLASS_MAX; i++)
env_spinlock_destroy(&alru->list_lock[i]);
env_vfree(cache->cleaner.cleaning_policy_context); env_vfree(cache->cleaner.cleaning_policy_context);
cache->cleaner.cleaning_policy_context = NULL; cache->cleaner.cleaning_policy_context = NULL;
} }
@ -587,36 +611,6 @@ static int check_for_io_activity(struct ocf_cache *cache,
return 0; return 0;
} }
static int cmp_ocf_user_parts(const void *p1, const void *p2) {
const struct ocf_user_part *t1 = *(const struct ocf_user_part**)p1;
const struct ocf_user_part *t2 = *(const struct ocf_user_part**)p2;
if (t1->config->priority > t2->config->priority)
return 1;
else if (t1->config->priority < t2->config->priority)
return -1;
return 0;
}
static void swp_ocf_user_part(void *part1, void *part2, int size) {
void *tmp = *(void **)part1;
*(void **)part1 = *(void **) part2;
*(void **)part2 = tmp;
}
static void get_parts_sorted(struct ocf_user_part **parts,
struct ocf_cache *cache) {
int i;
for (i = 0; i < OCF_IO_CLASS_MAX; i++)
parts[i] = &cache->user_parts[i];
env_sort(parts, OCF_IO_CLASS_MAX, sizeof(struct ocf_user_part*),
cmp_ocf_user_parts, swp_ocf_user_part);
}
static bool clean_later(ocf_cache_t cache, uint32_t *delta) static bool clean_later(ocf_cache_t cache, uint32_t *delta)
{ {
struct alru_cleaning_policy_config *config; struct alru_cleaning_policy_config *config;
@ -706,24 +700,24 @@ static bool block_is_busy(struct ocf_cache *cache,
return false; return false;
} }
static int get_data_to_flush(struct alru_flush_ctx *fctx) static int get_data_to_flush(struct alru_context *alru)
{ {
struct alru_flush_ctx *fctx = &alru->flush_ctx;
ocf_cache_t cache = fctx->cache; ocf_cache_t cache = fctx->cache;
struct alru_cleaning_policy_config *config; struct alru_cleaning_policy_config *config;
struct cleaning_policy_meta policy; struct cleaning_policy_meta policy;
ocf_cache_line_t cache_line; ocf_cache_line_t cache_line;
struct ocf_user_part *parts[OCF_IO_CLASS_MAX]; struct ocf_user_part *part;
uint32_t last_access; uint32_t last_access;
int to_flush = 0; int to_flush = 0;
int part_id = OCF_IO_CLASS_ID_MAX; int part_id = OCF_IO_CLASS_ID_MAX;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data; config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
get_parts_sorted(parts, cache); for_each_part(cache, part, part_id) {
env_spinlock_lock(&alru->list_lock[part_id]);
while (part_id >= OCF_IO_CLASS_ID_MIN) { cache_line = part->runtime->cleaning.policy.alru.lru_tail;
cache_line =
parts[part_id]->runtime->cleaning.policy.alru.lru_tail;
last_access = compute_timestamp(config); last_access = compute_timestamp(config);
@ -732,8 +726,10 @@ static int get_data_to_flush(struct alru_flush_ctx *fctx)
policy.meta.alru.timestamp < last_access); policy.meta.alru.timestamp < last_access);
while (more_blocks_to_flush(cache, cache_line, last_access)) { while (more_blocks_to_flush(cache, cache_line, last_access)) {
if (to_flush >= fctx->clines_no) if (to_flush >= fctx->clines_no) {
env_spinlock_unlock(&alru->list_lock[part_id]);
goto end; goto end;
}
if (!block_is_busy(cache, cache_line)) { if (!block_is_busy(cache, cache_line)) {
get_block_to_flush(&fctx->flush_data[to_flush], cache_line, get_block_to_flush(&fctx->flush_data[to_flush], cache_line,
@ -744,7 +740,8 @@ static int get_data_to_flush(struct alru_flush_ctx *fctx)
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy); ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
cache_line = policy.meta.alru.lru_prev; cache_line = policy.meta.alru.lru_prev;
} }
part_id--;
env_spinlock_unlock(&alru->list_lock[part_id]);
} }
end: end:
@ -769,8 +766,9 @@ static void alru_clean_complete(void *priv, int err)
fctx->cmpl(&fctx->cache->cleaner, interval); fctx->cmpl(&fctx->cache->cleaner, interval);
} }
static void alru_clean(struct alru_flush_ctx *fctx) static void alru_clean(struct alru_context *alru)
{ {
struct alru_flush_ctx *fctx = &alru->flush_ctx;
ocf_cache_t cache = fctx->cache; ocf_cache_t cache = fctx->cache;
int to_clean; int to_clean;
@ -792,7 +790,7 @@ static void alru_clean(struct alru_flush_ctx *fctx)
goto end; goto end;
} }
to_clean = get_data_to_flush(fctx); to_clean = get_data_to_flush(alru);
if (to_clean > 0) { if (to_clean > 0) {
fctx->flush_perfomed = true; fctx->flush_perfomed = true;
ocf_cleaner_do_flush_data_async(cache, fctx->flush_data, to_clean, ocf_cleaner_do_flush_data_async(cache, fctx->flush_data, to_clean,
@ -812,7 +810,8 @@ end:
void cleaning_alru_perform_cleaning(ocf_cache_t cache, ocf_cleaner_end_t cmpl) void cleaning_alru_perform_cleaning(ocf_cache_t cache, ocf_cleaner_end_t cmpl)
{ {
struct alru_flush_ctx *fctx = cache->cleaner.cleaning_policy_context; struct alru_context *alru = cache->cleaner.cleaning_policy_context;
struct alru_flush_ctx *fctx = &alru->flush_ctx;
struct alru_cleaning_policy_config *config; struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data; config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
@ -830,5 +829,5 @@ void cleaning_alru_perform_cleaning(ocf_cache_t cache, ocf_cleaner_end_t cmpl)
fctx->cmpl = cmpl; fctx->cmpl = cmpl;
fctx->flush_perfomed = false; fctx->flush_perfomed = false;
alru_clean(fctx); alru_clean(alru);
} }