364 lines
8.2 KiB
C
364 lines
8.2 KiB
C
/*
|
|
* Copyright(c) 2012-2018 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
*/
|
|
|
|
#include "ocf/ocf.h"
|
|
#include "../ocf_priv.h"
|
|
#include "../ocf_cache_priv.h"
|
|
#include "../ocf_volume_priv.h"
|
|
#include "../ocf_request.h"
|
|
#include "utils_io.h"
|
|
#include "utils_cache_line.h"
|
|
|
|
struct ocf_submit_volume_context {
|
|
env_atomic req_remaining;
|
|
int error;
|
|
ocf_submit_end_t cmpl;
|
|
void *priv;
|
|
};
|
|
|
|
static void _ocf_volume_flush_end(struct ocf_io *io, int error)
|
|
{
|
|
ocf_submit_end_t cmpl = io->priv1;
|
|
|
|
cmpl(io->priv2, error);
|
|
ocf_io_put(io);
|
|
}
|
|
|
|
void ocf_submit_volume_flush(ocf_volume_t volume,
|
|
ocf_submit_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_io *io;
|
|
|
|
io = ocf_volume_new_io(volume, NULL, 0, 0, OCF_WRITE, 0, 0);
|
|
if (!io)
|
|
OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM);
|
|
|
|
ocf_io_set_cmpl(io, cmpl, priv, _ocf_volume_flush_end);
|
|
|
|
ocf_volume_submit_flush(io);
|
|
}
|
|
|
|
static void ocf_submit_volume_end(struct ocf_io *io, int error)
|
|
{
|
|
struct ocf_submit_volume_context *context = io->priv1;
|
|
|
|
if (error)
|
|
context->error = error;
|
|
|
|
ocf_io_put(io);
|
|
|
|
if (env_atomic_dec_return(&context->req_remaining))
|
|
return;
|
|
|
|
context->cmpl(context->priv, context->error);
|
|
env_vfree(context);
|
|
}
|
|
|
|
void ocf_submit_volume_discard(ocf_volume_t volume, uint64_t addr,
|
|
uint64_t length, ocf_submit_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_submit_volume_context *context;
|
|
uint64_t bytes;
|
|
uint64_t max_length = (uint32_t)~0;
|
|
struct ocf_io *io;
|
|
|
|
context = env_vzalloc(sizeof(*context));
|
|
if (!context)
|
|
OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM);
|
|
|
|
env_atomic_set(&context->req_remaining, 1);
|
|
context->cmpl = cmpl;
|
|
context->priv = priv;
|
|
|
|
while (length) {
|
|
bytes = OCF_MIN(length, max_length);
|
|
|
|
io = ocf_volume_new_io(volume, NULL, addr, bytes,
|
|
OCF_WRITE, 0, 0);
|
|
if (!io) {
|
|
context->error = -OCF_ERR_NO_MEM;
|
|
break;
|
|
}
|
|
|
|
env_atomic_inc(&context->req_remaining);
|
|
|
|
ocf_io_set_cmpl(io, context, NULL, ocf_submit_volume_end);
|
|
ocf_volume_submit_discard(io);
|
|
|
|
addr += bytes;
|
|
length -= bytes;
|
|
}
|
|
|
|
if (env_atomic_dec_return(&context->req_remaining))
|
|
return;
|
|
|
|
cmpl(priv, context->error);
|
|
env_vfree(context);
|
|
}
|
|
|
|
void ocf_submit_write_zeros(ocf_volume_t volume, uint64_t addr,
|
|
uint64_t length, ocf_submit_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_submit_volume_context *context;
|
|
uint32_t bytes;
|
|
uint32_t max_length = ~((uint32_t)PAGE_SIZE - 1);
|
|
struct ocf_io *io;
|
|
|
|
context = env_vzalloc(sizeof(*context));
|
|
if (!context)
|
|
OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM);
|
|
|
|
env_atomic_set(&context->req_remaining, 1);
|
|
context->cmpl = cmpl;
|
|
context->priv = priv;
|
|
|
|
while (length) {
|
|
bytes = OCF_MIN(length, max_length);
|
|
|
|
io = ocf_volume_new_io(volume, NULL, addr, bytes,
|
|
OCF_WRITE, 0, 0);
|
|
if (!io) {
|
|
context->error = -OCF_ERR_NO_MEM;
|
|
break;
|
|
}
|
|
|
|
env_atomic_inc(&context->req_remaining);
|
|
|
|
ocf_io_set_cmpl(io, context, NULL, ocf_submit_volume_end);
|
|
ocf_volume_submit_write_zeroes(io);
|
|
|
|
addr += bytes;
|
|
length -= bytes;
|
|
}
|
|
|
|
if (env_atomic_dec_return(&context->req_remaining))
|
|
return;
|
|
|
|
cmpl(priv, context->error);
|
|
env_vfree(context);
|
|
}
|
|
|
|
struct ocf_submit_cache_page_context {
|
|
ocf_cache_t cache;
|
|
void *buffer;
|
|
ocf_submit_end_t cmpl;
|
|
void *priv;
|
|
};
|
|
|
|
static void ocf_submit_cache_page_end(struct ocf_io *io, int error)
|
|
{
|
|
struct ocf_submit_cache_page_context *context = io->priv1;
|
|
ctx_data_t *data = ocf_io_get_data(io);
|
|
|
|
if (io->dir == OCF_READ) {
|
|
ctx_data_rd_check(context->cache->owner, context->buffer,
|
|
data, PAGE_SIZE);
|
|
}
|
|
|
|
context->cmpl(context->priv, error);
|
|
ctx_data_free(context->cache->owner, data);
|
|
env_vfree(context);
|
|
ocf_io_put(io);
|
|
}
|
|
|
|
void ocf_submit_cache_page(ocf_cache_t cache, uint64_t addr, int dir,
|
|
void *buffer, ocf_submit_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_submit_cache_page_context *context;
|
|
ctx_data_t *data;
|
|
struct ocf_io *io;
|
|
int result = 0;
|
|
|
|
context = env_vmalloc(sizeof(*context));
|
|
if (!context)
|
|
OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM);
|
|
|
|
context->cache = cache;
|
|
context->buffer = buffer;
|
|
context->cmpl = cmpl;
|
|
context->priv = priv;
|
|
|
|
io = ocf_new_cache_io(cache, NULL, addr, PAGE_SIZE, dir, 0, 0);
|
|
if (!io) {
|
|
result = -OCF_ERR_NO_MEM;
|
|
goto err_io;
|
|
}
|
|
|
|
data = ctx_data_alloc(cache->owner, 1);
|
|
if (!data) {
|
|
result = -OCF_ERR_NO_MEM;
|
|
goto err_data;
|
|
}
|
|
|
|
if (dir == OCF_WRITE)
|
|
ctx_data_wr_check(cache->owner, data, buffer, PAGE_SIZE);
|
|
|
|
result = ocf_io_set_data(io, data, 0);
|
|
if (result)
|
|
goto err_set_data;
|
|
|
|
ocf_io_set_cmpl(io, context, NULL, ocf_submit_cache_page_end);
|
|
|
|
ocf_volume_submit_io(io);
|
|
return;
|
|
|
|
err_set_data:
|
|
ctx_data_free(cache->owner, data);
|
|
err_data:
|
|
ocf_io_put(io);
|
|
err_io:
|
|
env_vfree(context);
|
|
cmpl(priv, result);
|
|
}
|
|
|
|
static void ocf_submit_volume_req_cmpl(struct ocf_io *io, int error)
|
|
{
|
|
struct ocf_request *req = io->priv1;
|
|
ocf_req_end_t callback = io->priv2;
|
|
|
|
callback(req, error);
|
|
|
|
ocf_io_put(io);
|
|
}
|
|
|
|
void ocf_submit_cache_reqs(struct ocf_cache *cache,
|
|
struct ocf_request *req, int dir, uint64_t offset,
|
|
uint64_t size, unsigned int reqs, ocf_req_end_t callback)
|
|
{
|
|
struct ocf_counters_block *cache_stats;
|
|
uint64_t flags = req->ioi.io.flags;
|
|
uint32_t class = req->ioi.io.io_class;
|
|
uint64_t addr, bytes, total_bytes = 0;
|
|
struct ocf_io *io;
|
|
int err;
|
|
uint32_t i;
|
|
uint32_t first_cl = ocf_bytes_2_lines(cache, req->byte_position +
|
|
offset) - ocf_bytes_2_lines(cache, req->byte_position);
|
|
|
|
ENV_BUG_ON(req->byte_length < offset + size);
|
|
ENV_BUG_ON(first_cl + reqs > req->core_line_count);
|
|
|
|
cache_stats = &req->core->counters->cache_blocks;
|
|
|
|
if (reqs == 1) {
|
|
addr = ocf_metadata_map_lg2phy(cache,
|
|
req->map[first_cl].coll_idx);
|
|
addr *= ocf_line_size(cache);
|
|
addr += cache->device->metadata_offset;
|
|
addr += ((req->byte_position + offset) % ocf_line_size(cache));
|
|
bytes = size;
|
|
|
|
io = ocf_new_cache_io(cache, req->io_queue,
|
|
addr, bytes, dir, class, flags);
|
|
if (!io) {
|
|
callback(req, -OCF_ERR_NO_MEM);
|
|
goto update_stats;
|
|
}
|
|
|
|
ocf_io_set_cmpl(io, req, callback, ocf_submit_volume_req_cmpl);
|
|
|
|
err = ocf_io_set_data(io, req->data, offset);
|
|
if (err) {
|
|
ocf_io_put(io);
|
|
callback(req, err);
|
|
goto update_stats;
|
|
}
|
|
|
|
ocf_volume_submit_io(io);
|
|
total_bytes = bytes;
|
|
|
|
goto update_stats;
|
|
}
|
|
|
|
/* Issue requests to cache. */
|
|
for (i = 0; i < reqs; i++) {
|
|
addr = ocf_metadata_map_lg2phy(cache,
|
|
req->map[first_cl + i].coll_idx);
|
|
addr *= ocf_line_size(cache);
|
|
addr += cache->device->metadata_offset;
|
|
bytes = ocf_line_size(cache);
|
|
|
|
if (i == 0) {
|
|
uint64_t seek = ((req->byte_position + offset) %
|
|
ocf_line_size(cache));
|
|
|
|
addr += seek;
|
|
bytes -= seek;
|
|
} else if (i == (reqs - 1)) {
|
|
uint64_t skip = (ocf_line_size(cache) -
|
|
((req->byte_position + offset + size) %
|
|
ocf_line_size(cache))) % ocf_line_size(cache);
|
|
|
|
bytes -= skip;
|
|
}
|
|
|
|
bytes = OCF_MIN(bytes, size - total_bytes);
|
|
ENV_BUG_ON(bytes == 0);
|
|
|
|
io = ocf_new_cache_io(cache, req->io_queue,
|
|
addr, bytes, dir, class, flags);
|
|
if (!io) {
|
|
/* Finish all IOs which left with ERROR */
|
|
for (; i < reqs; i++)
|
|
callback(req, -OCF_ERR_NO_MEM);
|
|
goto update_stats;
|
|
}
|
|
|
|
ocf_io_set_cmpl(io, req, callback, ocf_submit_volume_req_cmpl);
|
|
|
|
err = ocf_io_set_data(io, req->data, offset + total_bytes);
|
|
if (err) {
|
|
ocf_io_put(io);
|
|
/* Finish all IOs which left with ERROR */
|
|
for (; i < reqs; i++)
|
|
callback(req, err);
|
|
goto update_stats;
|
|
}
|
|
ocf_volume_submit_io(io);
|
|
total_bytes += bytes;
|
|
}
|
|
|
|
ENV_BUG_ON(total_bytes != size);
|
|
|
|
update_stats:
|
|
if (dir == OCF_WRITE)
|
|
env_atomic64_add(total_bytes, &cache_stats->write_bytes);
|
|
else if (dir == OCF_READ)
|
|
env_atomic64_add(total_bytes, &cache_stats->read_bytes);
|
|
}
|
|
|
|
void ocf_submit_volume_req(ocf_volume_t volume, struct ocf_request *req,
|
|
ocf_req_end_t callback)
|
|
{
|
|
struct ocf_counters_block *core_stats;
|
|
uint64_t flags = req->ioi.io.flags;
|
|
uint32_t class = req->ioi.io.io_class;
|
|
int dir = req->rw;
|
|
struct ocf_io *io;
|
|
int err;
|
|
|
|
core_stats = &req->core->counters->core_blocks;
|
|
if (dir == OCF_WRITE)
|
|
env_atomic64_add(req->byte_length, &core_stats->write_bytes);
|
|
else if (dir == OCF_READ)
|
|
env_atomic64_add(req->byte_length, &core_stats->read_bytes);
|
|
|
|
io = ocf_volume_new_io(volume, req->io_queue, req->byte_position,
|
|
req->byte_length, dir, class, flags);
|
|
if (!io) {
|
|
callback(req, -OCF_ERR_NO_MEM);
|
|
return;
|
|
}
|
|
|
|
ocf_io_set_cmpl(io, req, callback, ocf_submit_volume_req_cmpl);
|
|
err = ocf_io_set_data(io, req->data, 0);
|
|
if (err) {
|
|
ocf_io_put(io);
|
|
callback(req, err);
|
|
return;
|
|
}
|
|
ocf_volume_submit_io(io);
|
|
}
|