#include "cache.h"
#include "backing_dev.h"
#include "cache_dev.h"
#include "dm_pcache.h"
static int cache_data_head_init(struct pcache_cache *cache)
{
struct pcache_cache_segment *next_seg;
struct pcache_cache_data_head *data_head;
data_head = get_data_head(cache);
next_seg = get_cache_segment(cache);
if (!next_seg)
return -EBUSY;
cache_seg_get(next_seg);
data_head->head_pos.cache_seg = next_seg;
data_head->head_pos.seg_off = 0;
return 0;
}
static int cache_data_alloc(struct pcache_cache *cache, struct pcache_cache_key *key)
{
struct pcache_cache_data_head *data_head;
struct pcache_cache_pos *head_pos;
struct pcache_cache_segment *cache_seg;
u32 seg_remain;
u32 allocated = 0, to_alloc;
int ret = 0;
preempt_disable();
data_head = get_data_head(cache);
again:
to_alloc = key->len - allocated;
if (!data_head->head_pos.cache_seg) {
seg_remain = 0;
} else {
cache_pos_copy(&key->cache_pos, &data_head->head_pos);
key->seg_gen = key->cache_pos.cache_seg->gen;
head_pos = &data_head->head_pos;
cache_seg = head_pos->cache_seg;
seg_remain = cache_seg_remain(head_pos);
}
if (seg_remain > to_alloc) {
cache_pos_advance(head_pos, to_alloc);
allocated += to_alloc;
cache_seg_get(cache_seg);
} else if (seg_remain) {
cache_pos_advance(head_pos, seg_remain);
key->len = seg_remain;
cache_seg_get(cache_seg);
cache_seg_put(head_pos->cache_seg);
head_pos->cache_seg = NULL;
} else {
ret = cache_data_head_init(cache);
if (ret)
goto out;
goto again;
}
out:
preempt_enable();
return ret;
}
static int cache_copy_from_req_bio(struct pcache_cache *cache, struct pcache_cache_key *key,
struct pcache_request *pcache_req, u32 bio_off)
{
struct pcache_cache_pos *pos = &key->cache_pos;
struct pcache_segment *segment;
segment = &pos->cache_seg->segment;
return segment_copy_from_bio(segment, pos->seg_off, key->len, pcache_req->bio, bio_off);
}
static int cache_copy_to_req_bio(struct pcache_cache *cache, struct pcache_request *pcache_req,
u32 bio_off, u32 len, struct pcache_cache_pos *pos, u64 key_gen)
{
struct pcache_cache_segment *cache_seg = pos->cache_seg;
struct pcache_segment *segment = &cache_seg->segment;
int ret;
spin_lock(&cache_seg->gen_lock);
if (key_gen < cache_seg->gen) {
spin_unlock(&cache_seg->gen_lock);
return -EINVAL;
}
ret = segment_copy_to_bio(segment, pos->seg_off, len, pcache_req->bio, bio_off);
spin_unlock(&cache_seg->gen_lock);
return ret;
}
static void miss_read_end_req(struct pcache_backing_dev_req *backing_req, int read_ret)
{
void *priv_data = backing_req->priv_data;
struct pcache_request *pcache_req = backing_req->req.upper_req;
struct pcache_cache *cache = backing_req->backing_dev->cache;
int ret;
if (priv_data) {
struct pcache_cache_key *key;
struct pcache_cache_subtree *cache_subtree;
key = (struct pcache_cache_key *)priv_data;
cache_subtree = key->cache_subtree;
spin_lock(&cache_subtree->tree_lock);
if (cache_key_empty(key)) {
if (read_ret) {
cache_key_delete(key);
goto unlock;
}
ret = cache_data_alloc(cache, key);
if (ret) {
cache_key_delete(key);
goto unlock;
}
ret = cache_copy_from_req_bio(cache, key, pcache_req, backing_req->req.bio_off);
if (ret) {
cache_seg_put(key->cache_pos.cache_seg);
cache_key_delete(key);
goto unlock;
}
key->flags &= ~PCACHE_CACHE_KEY_FLAGS_EMPTY;
key->flags |= PCACHE_CACHE_KEY_FLAGS_CLEAN;
ret = cache_key_append(cache, key, false);
if (ret) {
cache_seg_put(key->cache_pos.cache_seg);
cache_key_delete(key);
goto unlock;
}
}
unlock:
spin_unlock(&cache_subtree->tree_lock);
cache_key_put(key);
}
}
static void submit_cache_miss_req(struct pcache_cache *cache, struct pcache_backing_dev_req *backing_req)
{
if (backing_req->priv_data) {
struct pcache_cache_key *key;
key = (struct pcache_cache_key *)backing_req->priv_data;
cache_key_insert(&cache->req_key_tree, key, true);
}
backing_dev_req_submit(backing_req, false);
}
static void cache_miss_req_free(struct pcache_backing_dev_req *backing_req)
{
struct pcache_cache_key *key;
if (backing_req->priv_data) {
key = backing_req->priv_data;
backing_req->priv_data = NULL;
cache_key_put(key);
cache_key_put(key);
}
backing_dev_req_end(backing_req);
}
static struct pcache_backing_dev_req *cache_miss_req_alloc(struct pcache_cache *cache,
struct pcache_request *parent,
gfp_t gfp_mask)
{
struct pcache_backing_dev *backing_dev = cache->backing_dev;
struct pcache_backing_dev_req *backing_req;
struct pcache_cache_key *key = NULL;
struct pcache_backing_dev_req_opts req_opts = { 0 };
req_opts.type = BACKING_DEV_REQ_TYPE_REQ;
req_opts.gfp_mask = gfp_mask;
req_opts.req.upper_req = parent;
backing_req = backing_dev_req_alloc(backing_dev, &req_opts);
if (!backing_req)
return NULL;
key = cache_key_alloc(&cache->req_key_tree, gfp_mask);
if (!key)
goto free_backing_req;
cache_key_get(key);
backing_req->priv_data = key;
return backing_req;
free_backing_req:
cache_miss_req_free(backing_req);
return NULL;
}
static void cache_miss_req_init(struct pcache_cache *cache,
struct pcache_backing_dev_req *backing_req,
struct pcache_request *parent,
u32 off, u32 len, bool insert_key)
{
struct pcache_cache_key *key;
struct pcache_backing_dev_req_opts req_opts = { 0 };
req_opts.type = BACKING_DEV_REQ_TYPE_REQ;
req_opts.req.upper_req = parent;
req_opts.req.req_off = off;
req_opts.req.len = len;
req_opts.end_fn = miss_read_end_req;
backing_dev_req_init(backing_req, &req_opts);
if (insert_key) {
key = backing_req->priv_data;
key->off = parent->off + off;
key->len = len;
key->flags |= PCACHE_CACHE_KEY_FLAGS_EMPTY;
} else {
key = backing_req->priv_data;
backing_req->priv_data = NULL;
cache_key_put(key);
cache_key_put(key);
}
}
static struct pcache_backing_dev_req *get_pre_alloc_req(struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_request *pcache_req = ctx->pcache_req;
struct pcache_backing_dev_req *backing_req;
if (ctx->pre_alloc_req) {
backing_req = ctx->pre_alloc_req;
ctx->pre_alloc_req = NULL;
return backing_req;
}
return cache_miss_req_alloc(cache, pcache_req, GFP_NOWAIT);
}
static int read_before(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp,
struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_backing_dev_req *backing_req;
struct pcache_cache *cache = ctx->cache_tree->cache;
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, key->len, true);
list_add(&backing_req->node, ctx->submit_req_list);
ctx->req_done += key->len;
cache_key_cutfront(key, key->len);
return SUBTREE_WALK_RET_OK;
}
static int read_overlap_tail(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp,
struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_backing_dev_req *backing_req;
u32 io_len;
int ret;
io_len = cache_key_lstart(key_tmp) - cache_key_lstart(key);
if (io_len) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, io_len, true);
list_add(&backing_req->node, ctx->submit_req_list);
ctx->req_done += io_len;
cache_key_cutfront(key, io_len);
}
io_len = cache_key_lend(key) - cache_key_lstart(key_tmp);
if (cache_key_empty(key_tmp)) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, io_len, false);
submit_cache_miss_req(cache, backing_req);
} else {
ret = cache_copy_to_req_bio(ctx->cache_tree->cache, ctx->pcache_req, ctx->req_done,
io_len, &key_tmp->cache_pos, key_tmp->seg_gen);
if (ret) {
if (ret == -EINVAL) {
cache_key_delete(key_tmp);
return SUBTREE_WALK_RET_RESEARCH;
}
ctx->ret = ret;
return SUBTREE_WALK_RET_ERR;
}
}
ctx->req_done += io_len;
cache_key_cutfront(key, io_len);
return SUBTREE_WALK_RET_OK;
}
static int read_overlap_contain(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp,
struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_backing_dev_req *backing_req;
u32 io_len;
int ret;
io_len = cache_key_lstart(key_tmp) - cache_key_lstart(key);
if (io_len) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, io_len, true);
list_add(&backing_req->node, ctx->submit_req_list);
ctx->req_done += io_len;
cache_key_cutfront(key, io_len);
}
io_len = key_tmp->len;
if (cache_key_empty(key_tmp)) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, io_len, false);
submit_cache_miss_req(cache, backing_req);
} else {
ret = cache_copy_to_req_bio(ctx->cache_tree->cache, ctx->pcache_req, ctx->req_done,
io_len, &key_tmp->cache_pos, key_tmp->seg_gen);
if (ret) {
if (ret == -EINVAL) {
cache_key_delete(key_tmp);
return SUBTREE_WALK_RET_RESEARCH;
}
ctx->ret = ret;
return SUBTREE_WALK_RET_ERR;
}
}
ctx->req_done += io_len;
cache_key_cutfront(key, io_len);
return SUBTREE_WALK_RET_OK;
}
static int read_overlap_contained(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp,
struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_backing_dev_req *backing_req;
struct pcache_cache_pos pos;
int ret;
if (cache_key_empty(key_tmp)) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, key->len, false);
submit_cache_miss_req(cache, backing_req);
} else {
cache_pos_copy(&pos, &key_tmp->cache_pos);
cache_pos_advance(&pos, cache_key_lstart(key) - cache_key_lstart(key_tmp));
ret = cache_copy_to_req_bio(ctx->cache_tree->cache, ctx->pcache_req, ctx->req_done,
key->len, &pos, key_tmp->seg_gen);
if (ret) {
if (ret == -EINVAL) {
cache_key_delete(key_tmp);
return SUBTREE_WALK_RET_RESEARCH;
}
ctx->ret = ret;
return SUBTREE_WALK_RET_ERR;
}
}
ctx->req_done += key->len;
cache_key_cutfront(key, key->len);
return SUBTREE_WALK_RET_OK;
}
static int read_overlap_head(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp,
struct pcache_cache_subtree_walk_ctx *ctx)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_backing_dev_req *backing_req;
struct pcache_cache_pos pos;
u32 io_len;
int ret;
io_len = cache_key_lend(key_tmp) - cache_key_lstart(key);
if (cache_key_empty(key_tmp)) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, io_len, false);
submit_cache_miss_req(cache, backing_req);
} else {
cache_pos_copy(&pos, &key_tmp->cache_pos);
cache_pos_advance(&pos, cache_key_lstart(key) - cache_key_lstart(key_tmp));
ret = cache_copy_to_req_bio(ctx->cache_tree->cache, ctx->pcache_req, ctx->req_done,
io_len, &pos, key_tmp->seg_gen);
if (ret) {
if (ret == -EINVAL) {
cache_key_delete(key_tmp);
return SUBTREE_WALK_RET_RESEARCH;
}
ctx->ret = ret;
return SUBTREE_WALK_RET_ERR;
}
}
ctx->req_done += io_len;
cache_key_cutfront(key, io_len);
return SUBTREE_WALK_RET_OK;
}
static int read_walk_finally(struct pcache_cache_subtree_walk_ctx *ctx, int ret)
{
struct pcache_cache *cache = ctx->cache_tree->cache;
struct pcache_backing_dev_req *backing_req, *next_req;
struct pcache_cache_key *key = ctx->key;
list_for_each_entry_safe(backing_req, next_req, ctx->submit_req_list, node) {
list_del_init(&backing_req->node);
submit_cache_miss_req(ctx->cache_tree->cache, backing_req);
}
if (ret != SUBTREE_WALK_RET_OK)
return ret;
if (key->len) {
backing_req = get_pre_alloc_req(ctx);
if (!backing_req)
return SUBTREE_WALK_RET_NEED_REQ;
cache_miss_req_init(cache, backing_req, ctx->pcache_req, ctx->req_done, key->len, true);
submit_cache_miss_req(cache, backing_req);
ctx->req_done += key->len;
}
return SUBTREE_WALK_RET_OK;
}
static bool read_walk_done(struct pcache_cache_subtree_walk_ctx *ctx)
{
return (ctx->req_done >= ctx->pcache_req->data_len);
}
static int cache_read(struct pcache_cache *cache, struct pcache_request *pcache_req)
{
struct pcache_cache_key key_data = { .off = pcache_req->off, .len = pcache_req->data_len };
struct pcache_cache_subtree *cache_subtree;
struct pcache_cache_key *key_tmp = NULL, *key_next;
struct rb_node *prev_node = NULL;
struct pcache_cache_key *key = &key_data;
struct pcache_cache_subtree_walk_ctx walk_ctx = { 0 };
struct pcache_backing_dev_req *backing_req, *next_req;
LIST_HEAD(delete_key_list);
LIST_HEAD(submit_req_list);
int ret;
walk_ctx.cache_tree = &cache->req_key_tree;
walk_ctx.req_done = 0;
walk_ctx.pcache_req = pcache_req;
walk_ctx.before = read_before;
walk_ctx.overlap_tail = read_overlap_tail;
walk_ctx.overlap_head = read_overlap_head;
walk_ctx.overlap_contain = read_overlap_contain;
walk_ctx.overlap_contained = read_overlap_contained;
walk_ctx.walk_finally = read_walk_finally;
walk_ctx.walk_done = read_walk_done;
walk_ctx.delete_key_list = &delete_key_list;
walk_ctx.submit_req_list = &submit_req_list;
next:
key->off = pcache_req->off + walk_ctx.req_done;
key->len = pcache_req->data_len - walk_ctx.req_done;
if (key->len > PCACHE_CACHE_SUBTREE_SIZE - (key->off & PCACHE_CACHE_SUBTREE_SIZE_MASK))
key->len = PCACHE_CACHE_SUBTREE_SIZE - (key->off & PCACHE_CACHE_SUBTREE_SIZE_MASK);
cache_subtree = get_subtree(&cache->req_key_tree, key->off);
spin_lock(&cache_subtree->tree_lock);
search:
prev_node = cache_subtree_search(cache_subtree, key, NULL, NULL, &delete_key_list);
if (!list_empty(&delete_key_list)) {
list_for_each_entry_safe(key_tmp, key_next, &delete_key_list, list_node) {
list_del_init(&key_tmp->list_node);
cache_key_delete(key_tmp);
}
goto search;
}
walk_ctx.start_node = prev_node;
walk_ctx.key = key;
ret = cache_subtree_walk(&walk_ctx);
if (ret == SUBTREE_WALK_RET_RESEARCH)
goto search;
spin_unlock(&cache_subtree->tree_lock);
if (ret == SUBTREE_WALK_RET_ERR) {
ret = walk_ctx.ret;
goto out;
}
if (ret == SUBTREE_WALK_RET_NEED_REQ) {
walk_ctx.pre_alloc_req = cache_miss_req_alloc(cache, pcache_req, GFP_NOIO);
pcache_dev_debug(CACHE_TO_PCACHE(cache), "allocate pre_alloc_req with GFP_NOIO");
}
if (walk_ctx.req_done < pcache_req->data_len)
goto next;
ret = 0;
out:
if (walk_ctx.pre_alloc_req)
cache_miss_req_free(walk_ctx.pre_alloc_req);
list_for_each_entry_safe(backing_req, next_req, &submit_req_list, node) {
list_del_init(&backing_req->node);
backing_dev_req_end(backing_req);
}
return ret;
}
static int cache_write(struct pcache_cache *cache, struct pcache_request *pcache_req)
{
struct pcache_cache_subtree *cache_subtree;
struct pcache_cache_key *key;
u64 offset = pcache_req->off;
u32 length = pcache_req->data_len;
u32 io_done = 0;
int ret;
while (true) {
if (io_done >= length)
break;
key = cache_key_alloc(&cache->req_key_tree, GFP_NOIO);
key->off = offset + io_done;
key->len = length - io_done;
if (key->len > PCACHE_CACHE_SUBTREE_SIZE - (key->off & PCACHE_CACHE_SUBTREE_SIZE_MASK))
key->len = PCACHE_CACHE_SUBTREE_SIZE - (key->off & PCACHE_CACHE_SUBTREE_SIZE_MASK);
ret = cache_data_alloc(cache, key);
if (ret) {
cache_key_put(key);
goto err;
}
ret = cache_copy_from_req_bio(cache, key, pcache_req, io_done);
if (ret) {
cache_seg_put(key->cache_pos.cache_seg);
cache_key_put(key);
goto err;
}
cache_subtree = get_subtree(&cache->req_key_tree, key->off);
spin_lock(&cache_subtree->tree_lock);
cache_key_insert(&cache->req_key_tree, key, true);
ret = cache_key_append(cache, key, pcache_req->bio->bi_opf & REQ_FUA);
if (ret) {
cache_seg_put(key->cache_pos.cache_seg);
cache_key_delete(key);
goto unlock;
}
io_done += key->len;
spin_unlock(&cache_subtree->tree_lock);
}
return 0;
unlock:
spin_unlock(&cache_subtree->tree_lock);
err:
return ret;
}
int pcache_cache_flush(struct pcache_cache *cache)
{
struct pcache_cache_kset *kset;
int ret;
u32 i;
for (i = 0; i < cache->n_ksets; i++) {
kset = get_kset(cache, i);
spin_lock(&kset->kset_lock);
ret = cache_kset_close(cache, kset);
spin_unlock(&kset->kset_lock);
if (ret)
return ret;
}
return 0;
}
int pcache_cache_handle_req(struct pcache_cache *cache, struct pcache_request *pcache_req)
{
struct bio *bio = pcache_req->bio;
if (unlikely(bio->bi_opf & REQ_PREFLUSH))
return pcache_cache_flush(cache);
if (bio_data_dir(bio) == READ)
return cache_read(cache, pcache_req);
return cache_write(cache, pcache_req);
}