
- Queue allocation is now separated from starting cache. - Queue can be created and destroyed in runtime. - All queue ops accept queue handle instead of queue id. - Cache stores queues as list instead of array. Signed-off-by: Michal Mielewczyk <michal.mielewczyk@intel.com> Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
638 lines
14 KiB
C
638 lines
14 KiB
C
/*
|
|
* Copyright(c) 2012-2018 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
*/
|
|
#include "metadata.h"
|
|
#include "metadata_io.h"
|
|
#include "../ocf_priv.h"
|
|
#include "../engine/cache_engine.h"
|
|
#include "../engine/engine_common.h"
|
|
#include "../engine/engine_bf.h"
|
|
#include "../utils/utils_cache_line.h"
|
|
#include "../utils/utils_allocator.h"
|
|
#include "../utils/utils_io.h"
|
|
#include "../ocf_def_priv.h"
|
|
|
|
#define OCF_METADATA_IO_DEBUG 0
|
|
|
|
#if 1 == OCF_METADATA_IO_DEBUG
|
|
#define OCF_DEBUG_TRACE(cache) \
|
|
ocf_cache_log(cache, log_info, "[Metadata][IO] %s\n", __func__)
|
|
|
|
#define OCF_DEBUG_MSG(cache, msg) \
|
|
ocf_cache_log(cache, log_info, "[Metadata][IO] %s - %s\n", \
|
|
__func__, msg)
|
|
|
|
#define OCF_DEBUG_PARAM(cache, format, ...) \
|
|
ocf_cache_log(cache, log_info, "[Metadata][IO] %s - "format"\n", \
|
|
__func__, ##__VA_ARGS__)
|
|
#else
|
|
#define OCF_DEBUG_TRACE(cache)
|
|
#define OCF_DEBUG_MSG(cache, msg)
|
|
#define OCF_DEBUG_PARAM(cache, format, ...)
|
|
#endif
|
|
|
|
static void metadata_io_write_i_asynch_end(struct metadata_io_request *request,
|
|
int error);
|
|
static int ocf_restart_meta_io(struct ocf_request *req);
|
|
|
|
static struct ocf_io_if meta_restart_if = {
|
|
.read = ocf_restart_meta_io,
|
|
.write = ocf_restart_meta_io
|
|
};
|
|
|
|
/*
|
|
* Get max pages for IO
|
|
*/
|
|
static uint32_t metadata_io_max_page(ocf_cache_t cache)
|
|
{
|
|
return ocf_volume_get_max_io_size(&cache->device->volume) / PAGE_SIZE;
|
|
}
|
|
|
|
/*
|
|
* Iterative read end callback
|
|
*/
|
|
static void metadata_io_read_i_atomic_end(struct ocf_io *io, int error)
|
|
{
|
|
struct metadata_io_request_atomic *meta_atom_req = io->priv1;
|
|
|
|
OCF_DEBUG_TRACE(ocf_volume_get_cache(io->volume));
|
|
|
|
meta_atom_req->error |= error;
|
|
env_completion_complete(&meta_atom_req->complete);
|
|
}
|
|
|
|
/*
|
|
* Iterative read request
|
|
*/
|
|
int metadata_io_read_i_atomic(ocf_cache_t cache,
|
|
ocf_metadata_atomic_io_event_t hndl)
|
|
{
|
|
uint64_t i;
|
|
uint64_t max_sectors_count = PAGE_SIZE / OCF_ATOMIC_METADATA_SIZE;
|
|
uint64_t io_sectors_count = cache->device->collision_table_entries *
|
|
ocf_line_sectors(cache);
|
|
uint64_t count, curr_count;
|
|
int result = 0;
|
|
struct ocf_io *io;
|
|
ctx_data_t *data;
|
|
struct metadata_io_request_atomic meta_atom_req;
|
|
unsigned char step = 0;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
/* Allocate one 4k page for metadata*/
|
|
data = ctx_data_alloc(cache->owner, 1);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
count = io_sectors_count;
|
|
for (i = 0; i < io_sectors_count; i += curr_count) {
|
|
/* Get sectors count of this IO iteration */
|
|
curr_count = OCF_MIN(max_sectors_count, count);
|
|
|
|
env_completion_init(&meta_atom_req.complete);
|
|
meta_atom_req.error = 0;
|
|
|
|
/* Reset position in data buffer */
|
|
ctx_data_seek(cache->owner, data, ctx_data_seek_begin, 0);
|
|
|
|
/* Allocate new IO */
|
|
io = ocf_new_cache_io(cache);
|
|
if (!io) {
|
|
result = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
/* Setup IO */
|
|
ocf_io_configure(io,
|
|
cache->device->metadata_offset +
|
|
SECTORS_TO_BYTES(i),
|
|
SECTORS_TO_BYTES(curr_count),
|
|
OCF_READ, 0, 0);
|
|
ocf_io_set_cmpl(io, &meta_atom_req, NULL,
|
|
metadata_io_read_i_atomic_end);
|
|
result = ocf_io_set_data(io, data, 0);
|
|
if (result) {
|
|
ocf_io_put(io);
|
|
break;
|
|
}
|
|
|
|
/* Submit IO */
|
|
ocf_volume_submit_metadata(io);
|
|
ocf_io_put(io);
|
|
|
|
/* Wait for completion of IO */
|
|
env_completion_wait(&meta_atom_req.complete);
|
|
|
|
/* Check for error */
|
|
if (meta_atom_req.error) {
|
|
result = meta_atom_req.error;
|
|
break;
|
|
}
|
|
|
|
result |= hndl(cache, i, curr_count, data);
|
|
if (result)
|
|
break;
|
|
|
|
count -= curr_count;
|
|
|
|
OCF_COND_RESCHED(step, 128);
|
|
}
|
|
|
|
/* Memory free */
|
|
ctx_data_free(cache->owner, data);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void metadata_io_write_i_asynch_cmpl(struct ocf_io *io, int error)
|
|
{
|
|
struct metadata_io_request *request = io->priv1;
|
|
|
|
metadata_io_write_i_asynch_end(request, error);
|
|
|
|
ocf_io_put(io);
|
|
}
|
|
|
|
static int ocf_restart_meta_io(struct ocf_request *req)
|
|
{
|
|
struct ocf_io *io;
|
|
struct metadata_io_request *meta_io_req;
|
|
ocf_cache_t cache;
|
|
int i;
|
|
int ret;
|
|
|
|
cache = req->cache;
|
|
meta_io_req = req->priv;
|
|
|
|
/* Fill with the latest metadata. */
|
|
OCF_METADATA_LOCK_RD();
|
|
for (i = 0; i < meta_io_req->count; i++) {
|
|
meta_io_req->on_meta_fill(cache, meta_io_req->data,
|
|
meta_io_req->page + i, meta_io_req->context);
|
|
|
|
}
|
|
OCF_METADATA_UNLOCK_RD();
|
|
|
|
io = ocf_new_cache_io(cache);
|
|
if (!io) {
|
|
metadata_io_write_i_asynch_end(meta_io_req, -ENOMEM);
|
|
return 0;
|
|
}
|
|
|
|
/* Setup IO */
|
|
ocf_io_configure(io,
|
|
PAGES_TO_BYTES(meta_io_req->page),
|
|
PAGES_TO_BYTES(meta_io_req->count),
|
|
OCF_WRITE, 0, 0);
|
|
|
|
ocf_io_set_cmpl(io, meta_io_req, NULL, metadata_io_write_i_asynch_cmpl);
|
|
ret = ocf_io_set_data(io, meta_io_req->data, 0);
|
|
if (ret) {
|
|
ocf_io_put(io);
|
|
metadata_io_write_i_asynch_end(meta_io_req, ret);
|
|
return ret;
|
|
}
|
|
ocf_volume_submit_io(io);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Iterative asynchronous write callback
|
|
*/
|
|
static void metadata_io_write_i_asynch_end(struct metadata_io_request *request,
|
|
int error)
|
|
{
|
|
struct metadata_io_request_asynch *a_req;
|
|
ocf_cache_t cache;
|
|
|
|
OCF_CHECK_NULL(request);
|
|
|
|
cache = request->cache;
|
|
|
|
a_req = request->asynch;
|
|
OCF_CHECK_NULL(a_req);
|
|
OCF_CHECK_NULL(a_req->on_complete);
|
|
|
|
if (error) {
|
|
request->error |= error;
|
|
request->asynch->error |= error;
|
|
}
|
|
|
|
if (env_atomic_dec_return(&request->req_remaining))
|
|
return;
|
|
|
|
OCF_DEBUG_PARAM(cache, "Page = %u", request->page);
|
|
|
|
ctx_data_free(cache->owner, request->data);
|
|
request->data = NULL;
|
|
|
|
if (env_atomic_dec_return(&a_req->req_remaining)) {
|
|
env_atomic_set(&request->finished, 1);
|
|
ocf_metadata_updater_kick(cache);
|
|
return;
|
|
}
|
|
|
|
OCF_DEBUG_MSG(cache, "Asynchronous IO completed");
|
|
|
|
/* All IOs have been finished, call IO end callback */
|
|
a_req->on_complete(request->cache, a_req->context, request->error);
|
|
|
|
/*
|
|
* If it's last request, we mark is as finished
|
|
* after calling IO end callback
|
|
*/
|
|
env_atomic_set(&request->finished, 1);
|
|
ocf_metadata_updater_kick(cache);
|
|
}
|
|
|
|
static void metadata_io_req_error(ocf_cache_t cache,
|
|
struct metadata_io_request_asynch *a_req,
|
|
uint32_t i, int error)
|
|
{
|
|
a_req->error |= error;
|
|
a_req->reqs[i].error |= error;
|
|
a_req->reqs[i].count = 0;
|
|
if (a_req->reqs[i].data)
|
|
ctx_data_free(cache->owner, a_req->reqs[i].data);
|
|
a_req->reqs[i].data = NULL;
|
|
}
|
|
|
|
/*
|
|
* Iterative write request asynchronously
|
|
*/
|
|
int metadata_io_write_i_asynch(ocf_cache_t cache, ocf_queue_t queue,
|
|
void *context, uint32_t page, uint32_t count,
|
|
ocf_metadata_io_event_t fill_hndl,
|
|
ocf_metadata_io_hndl_on_write_t compl_hndl)
|
|
{
|
|
uint32_t curr_count, written;
|
|
uint32_t max_count = metadata_io_max_page(cache);
|
|
uint32_t io_count = OCF_DIV_ROUND_UP(count, max_count);
|
|
uint32_t i, i_fill;
|
|
int error = 0, ret;
|
|
struct ocf_io *io;
|
|
|
|
/* Allocation and initialization of asynchronous metadata IO request */
|
|
struct metadata_io_request_asynch *a_req;
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
a_req = env_zalloc(sizeof(*a_req), ENV_MEM_NOIO);
|
|
if (!a_req)
|
|
return -OCF_ERR_NO_MEM;
|
|
|
|
env_atomic_set(&a_req->req_remaining, io_count);
|
|
env_atomic_set(&a_req->req_active, io_count);
|
|
a_req->on_complete = compl_hndl;
|
|
a_req->context = context;
|
|
a_req->page = page;
|
|
|
|
/* Allocate particular requests and initialize them */
|
|
OCF_REALLOC_CP(&a_req->reqs, sizeof(a_req->reqs[0]),
|
|
io_count, &a_req->reqs_limit);
|
|
if (!a_req->reqs) {
|
|
env_free(a_req);
|
|
ocf_cache_log(cache, log_warn,
|
|
"No memory during metadata IO\n");
|
|
return -OCF_ERR_NO_MEM;
|
|
}
|
|
/* IO Requests initialization */
|
|
for (i = 0; i < io_count; i++) {
|
|
env_atomic_set(&(a_req->reqs[i].req_remaining), 1);
|
|
env_atomic_set(&(a_req->reqs[i].finished), 0);
|
|
a_req->reqs[i].asynch = a_req;
|
|
}
|
|
|
|
OCF_DEBUG_PARAM(cache, "IO count = %u", io_count);
|
|
|
|
i = 0;
|
|
written = 0;
|
|
while (count) {
|
|
/* Get pages count of this IO iteration */
|
|
if (count > max_count)
|
|
curr_count = max_count;
|
|
else
|
|
curr_count = count;
|
|
|
|
/* Fill request */
|
|
a_req->reqs[i].cache = cache;
|
|
a_req->reqs[i].context = context;
|
|
a_req->reqs[i].page = page + written;
|
|
a_req->reqs[i].count = curr_count;
|
|
a_req->reqs[i].on_meta_fill = fill_hndl;
|
|
a_req->reqs[i].fl_req.io_if = &meta_restart_if;
|
|
a_req->reqs[i].fl_req.io_queue = queue;
|
|
a_req->reqs[i].fl_req.cache = cache;
|
|
a_req->reqs[i].fl_req.priv = &a_req->reqs[i];
|
|
a_req->reqs[i].fl_req.info.internal = true;
|
|
|
|
/*
|
|
* We don't want allocate map for this request in
|
|
* threads.
|
|
*/
|
|
a_req->reqs[i].fl_req.map = LIST_POISON1;
|
|
|
|
INIT_LIST_HEAD(&a_req->reqs[i].list);
|
|
|
|
a_req->reqs[i].data = ctx_data_alloc(cache->owner, curr_count);
|
|
if (!a_req->reqs[i].data) {
|
|
error = -OCF_ERR_NO_MEM;
|
|
metadata_io_req_error(cache, a_req, i, error);
|
|
break;
|
|
}
|
|
|
|
/* Issue IO if it is not overlapping with anything else */
|
|
ret = metadata_updater_check_overlaps(cache, &a_req->reqs[i]);
|
|
if (ret == 0) {
|
|
/* Allocate new IO */
|
|
io = ocf_new_cache_io(cache);
|
|
if (!io) {
|
|
error = -OCF_ERR_NO_MEM;
|
|
metadata_io_req_error(cache, a_req, i, error);
|
|
break;
|
|
}
|
|
|
|
for (i_fill = 0; i_fill < curr_count; i_fill++) {
|
|
fill_hndl(cache, a_req->reqs[i].data,
|
|
page + written + i_fill,
|
|
context);
|
|
}
|
|
|
|
/* Setup IO */
|
|
ocf_io_configure(io,
|
|
PAGES_TO_BYTES(a_req->reqs[i].page),
|
|
PAGES_TO_BYTES(a_req->reqs[i].count),
|
|
OCF_WRITE, 0, 0);
|
|
|
|
ocf_io_set_cmpl(io, &a_req->reqs[i], NULL,
|
|
metadata_io_write_i_asynch_cmpl);
|
|
error = ocf_io_set_data(io, a_req->reqs[i].data, 0);
|
|
if (error) {
|
|
ocf_io_put(io);
|
|
metadata_io_req_error(cache, a_req, i, error);
|
|
break;
|
|
}
|
|
|
|
ocf_volume_submit_io(io);
|
|
}
|
|
|
|
count -= curr_count;
|
|
written += curr_count;
|
|
i++;
|
|
}
|
|
|
|
if (error == 0) {
|
|
/* No error, return 0 that indicates operation successful */
|
|
return 0;
|
|
}
|
|
|
|
OCF_DEBUG_MSG(cache, "ERROR");
|
|
|
|
if (i == 0) {
|
|
/*
|
|
* If no requests were submitted, we just call completion
|
|
* callback, free memory and return error.
|
|
*/
|
|
compl_hndl(cache, context, error);
|
|
|
|
OCF_REALLOC_DEINIT(&a_req->reqs, &a_req->reqs_limit);
|
|
env_free(a_req);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Decrement total reaming requests with IO that were not triggered.
|
|
* If we reached zero, we need to call completion callback.
|
|
*/
|
|
if (env_atomic_sub_return(io_count - i, &a_req->req_remaining) == 0)
|
|
compl_hndl(cache, context, error);
|
|
|
|
/*
|
|
* Decrement total active requests with IO that were not triggered.
|
|
* If we reached zero, we need to free memory.
|
|
*/
|
|
if (env_atomic_sub_return(io_count - i, &a_req->req_active) == 0) {
|
|
OCF_REALLOC_DEINIT(&a_req->reqs, &a_req->reqs_limit);
|
|
env_free(a_req);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
int ocf_metadata_io_init(ocf_cache_t cache)
|
|
{
|
|
return ocf_metadata_updater_init(cache);
|
|
}
|
|
|
|
void ocf_metadata_io_deinit(ocf_cache_t cache)
|
|
{
|
|
ocf_metadata_updater_stop(cache);
|
|
}
|
|
|
|
static void metadata_io_end(struct ocf_io *io, int error)
|
|
{
|
|
struct metadata_io *mio = io->priv1;
|
|
ctx_data_t *data = ocf_io_get_data(io);
|
|
uint32_t page = BYTES_TO_PAGES(io->addr);
|
|
uint32_t count = BYTES_TO_PAGES(io->bytes);
|
|
ocf_cache_t cache = mio->cache;
|
|
uint32_t i = 0;
|
|
|
|
if (error) {
|
|
mio->error |= error;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; mio->dir == OCF_READ && i < count; i++) {
|
|
mio->error |= mio->hndl_fn(cache, data, page + i,
|
|
mio->hndl_cntx);
|
|
}
|
|
|
|
out:
|
|
ctx_data_free(cache->owner, data);
|
|
ocf_io_put(io);
|
|
|
|
if (env_atomic_dec_return(&mio->req_remaining))
|
|
return;
|
|
|
|
env_completion_complete(&mio->completion);
|
|
}
|
|
|
|
static int metadata_submit_io(
|
|
ocf_cache_t cache,
|
|
struct metadata_io *mio,
|
|
uint32_t count,
|
|
uint32_t written)
|
|
{
|
|
ctx_data_t *data;
|
|
struct ocf_io *io;
|
|
int err;
|
|
int i;
|
|
|
|
/* Allocate IO */
|
|
io = ocf_new_cache_io(cache);
|
|
if (!io) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Allocate data buffer for this IO */
|
|
data = ctx_data_alloc(cache->owner, count);
|
|
if (!data) {
|
|
err = -ENOMEM;
|
|
goto put_io;
|
|
}
|
|
|
|
/* Fill data */
|
|
for (i = 0; mio->dir == OCF_WRITE && i < count; i++) {
|
|
err = mio->hndl_fn(cache, data,
|
|
mio->page + written + i, mio->hndl_cntx);
|
|
if (err)
|
|
goto free_data;
|
|
}
|
|
|
|
/* Setup IO */
|
|
ocf_io_configure(io,
|
|
PAGES_TO_BYTES(mio->page + written),
|
|
PAGES_TO_BYTES(count),
|
|
mio->dir, 0, 0);
|
|
ocf_io_set_cmpl(io, mio, NULL, metadata_io_end);
|
|
err = ocf_io_set_data(io, data, 0);
|
|
if (err)
|
|
goto free_data;
|
|
|
|
/* Submit IO */
|
|
env_atomic_inc(&mio->req_remaining);
|
|
ocf_volume_submit_io(io);
|
|
|
|
return 0;
|
|
|
|
free_data:
|
|
ctx_data_free(cache->owner, data);
|
|
put_io:
|
|
ocf_io_put(io);
|
|
error:
|
|
mio->error = err;
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static int metadata_io(struct metadata_io *mio)
|
|
{
|
|
uint32_t max_count = metadata_io_max_page(mio->cache);
|
|
uint32_t this_count, written = 0;
|
|
uint32_t count = mio->count;
|
|
unsigned char step = 0;
|
|
int err;
|
|
|
|
ocf_cache_t cache = mio->cache;
|
|
|
|
/* Check direction value correctness */
|
|
switch (mio->dir) {
|
|
case OCF_WRITE:
|
|
case OCF_READ:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
env_atomic_set(&mio->req_remaining, 1);
|
|
env_completion_init(&mio->completion);
|
|
|
|
while (count) {
|
|
this_count = OCF_MIN(count, max_count);
|
|
|
|
err = metadata_submit_io(cache, mio, this_count, written);
|
|
if (err)
|
|
break;
|
|
|
|
/* Update counters */
|
|
count -= this_count;
|
|
written += this_count;
|
|
|
|
OCF_COND_RESCHED(step, 128);
|
|
}
|
|
|
|
if (env_atomic_dec_return(&mio->req_remaining) == 0)
|
|
env_completion_complete(&mio->completion);
|
|
|
|
/* Wait for all IO to be finished */
|
|
env_completion_wait(&mio->completion);
|
|
|
|
return mio->error;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
int metadata_io_write_i(ocf_cache_t cache,
|
|
uint32_t page, uint32_t count,
|
|
ocf_metadata_io_event_t hndl_fn, void *hndl_cntx)
|
|
{
|
|
struct metadata_io mio = {
|
|
.dir = OCF_WRITE,
|
|
.cache = cache,
|
|
.page = page,
|
|
.count = count,
|
|
.hndl_fn = hndl_fn,
|
|
.hndl_cntx = hndl_cntx,
|
|
};
|
|
|
|
return metadata_io(&mio);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
int metadata_io_read_i(ocf_cache_t cache,
|
|
uint32_t page, uint32_t count,
|
|
ocf_metadata_io_event_t hndl_fn, void *hndl_cntx)
|
|
{
|
|
struct metadata_io mio = {
|
|
.dir = OCF_READ,
|
|
.cache = cache,
|
|
.page = page,
|
|
.count = count,
|
|
.hndl_fn = hndl_fn,
|
|
.hndl_cntx = hndl_cntx,
|
|
};
|
|
|
|
return metadata_io(&mio);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static int metadata_io_write_fill(ocf_cache_t cache,
|
|
ctx_data_t *data, uint32_t page, void *context)
|
|
{
|
|
ctx_data_wr_check(cache->owner, data, context, PAGE_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write request
|
|
*/
|
|
int metadata_io_write(ocf_cache_t cache,
|
|
void *data, uint32_t page)
|
|
{
|
|
struct metadata_io mio = {
|
|
.dir = OCF_WRITE,
|
|
.cache = cache,
|
|
.page = page,
|
|
.count = 1,
|
|
.hndl_fn = metadata_io_write_fill,
|
|
.hndl_cntx = data,
|
|
};
|
|
|
|
|
|
return metadata_io(&mio);
|
|
}
|