// SPDX-License-Identifier: GPL-2.0-or-later#include <linux/blkdev.h> #include "../dm-core.h"#include "pcache_internal.h"#include "cache_dev.h"#include "backing_dev.h"#include "cache.h"#include "dm_pcache.h" static struct kmem_cache *backing_req_cache;static struct kmem_cache *backing_bvec_cache; static void backing_dev_exit(struct pcache_backing_dev *backing_dev){ mempool_exit(&backing_dev->req_pool); mempool_exit(&backing_dev->bvec_pool);} static void req_submit_fn(struct work_struct *work);static void req_complete_fn(struct work_struct *work);static int backing_dev_init(struct dm_pcache *pcache){ struct pcache_backing_dev *backing_dev = &pcache->backing_dev; int ret; ret = mempool_init_slab_pool(&backing_dev->req_pool, 128, backing_req_cache); if (ret) goto err; ret = mempool_init_slab_pool(&backing_dev->bvec_pool, 128, backing_bvec_cache); if (ret) goto req_pool_exit; INIT_LIST_HEAD(&backing_dev->submit_list); INIT_LIST_HEAD(&backing_dev->complete_list); spin_lock_init(&backing_dev->submit_lock); spin_lock_init(&backing_dev->complete_lock); INIT_WORK(&backing_dev->req_submit_work, req_submit_fn); INIT_WORK(&backing_dev->req_complete_work, req_complete_fn); atomic_set(&backing_dev->inflight_reqs, 0); init_waitqueue_head(&backing_dev->inflight_wq); return 0; req_pool_exit: mempool_exit(&backing_dev->req_pool);err: return ret;} int backing_dev_start(struct dm_pcache *pcache){ struct pcache_backing_dev *backing_dev = &pcache->backing_dev; int ret; ret = backing_dev_init(pcache); if (ret) return ret; backing_dev->dev_size = bdev_nr_sectors(backing_dev->dm_dev->bdev); return 0;} void backing_dev_stop(struct dm_pcache *pcache){ struct pcache_backing_dev *backing_dev = &pcache->backing_dev; /* * There should not be any new request comming, just wait * inflight requests done. */ wait_event(backing_dev->inflight_wq, atomic_read(&backing_dev->inflight_reqs) == 0); flush_work(&backing_dev->req_submit_work); flush_work(&backing_dev->req_complete_work); backing_dev_exit(backing_dev);} /* pcache_backing_dev_req functions */void backing_dev_req_end(struct pcache_backing_dev_req *backing_req){ struct pcache_backing_dev *backing_dev = backing_req->backing_dev; if (backing_req->end_req) backing_req->end_req(backing_req, backing_req->ret); switch (backing_req->type) { case BACKING_DEV_REQ_TYPE_REQ: if (backing_req->req.upper_req) pcache_req_put(backing_req->req.upper_req, backing_req->ret); break; case BACKING_DEV_REQ_TYPE_KMEM: if (backing_req->kmem.bvecs != backing_req->kmem.inline_bvecs) mempool_free(backing_req->kmem.bvecs, &backing_dev->bvec_pool); break; default: BUG(); } mempool_free(backing_req, &backing_dev->req_pool); if (atomic_dec_and_test(&backing_dev->inflight_reqs)) wake_up(&backing_dev->inflight_wq);} static void req_complete_fn(struct work_struct *work){ struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_complete_work); struct pcache_backing_dev_req *backing_req; LIST_HEAD(tmp_list); spin_lock_irq(&backing_dev->complete_lock); list_splice_init(&backing_dev->complete_list, &tmp_list); spin_unlock_irq(&backing_dev->complete_lock); while (!list_empty(&tmp_list)) { backing_req = list_first_entry(&tmp_list, struct pcache_backing_dev_req, node); list_del_init(&backing_req->node); backing_dev_req_end(backing_req); }} static void backing_dev_bio_end(struct bio *bio){ struct pcache_backing_dev_req *backing_req = bio->bi_private; struct pcache_backing_dev *backing_dev = backing_req->backing_dev; unsigned long flags; backing_req->ret = blk_status_to_errno(bio->bi_status); spin_lock_irqsave(&backing_dev->complete_lock, flags); list_move_tail(&backing_req->node, &backing_dev->complete_list); queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_complete_work); spin_unlock_irqrestore(&backing_dev->complete_lock, flags);} static void req_submit_fn(struct work_struct *work){ struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_submit_work); struct pcache_backing_dev_req *backing_req; LIST_HEAD(tmp_list); spin_lock(&backing_dev->submit_lock); list_splice_init(&backing_dev->submit_list, &tmp_list); spin_unlock(&backing_dev->submit_lock); while (!list_empty(&tmp_list)) { backing_req = list_first_entry(&tmp_list, struct pcache_backing_dev_req, node); list_del_init(&backing_req->node); submit_bio_noacct(&backing_req->bio); }} void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req, bool direct){ struct pcache_backing_dev *backing_dev = backing_req->backing_dev; if (direct) { submit_bio_noacct(&backing_req->bio); return; } spin_lock(&backing_dev->submit_lock); list_add_tail(&backing_req->node, &backing_dev->submit_list); queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_submit_work); spin_unlock(&backing_dev->submit_lock);} static void bio_map(struct bio *bio, void *base, size_t size){ struct page *page; unsigned int offset; unsigned int len; if (!is_vmalloc_addr(base)) { page = virt_to_page(base); offset = offset_in_page(base); BUG_ON(!bio_add_page(bio, page, size, offset)); return; } flush_kernel_vmap_range(base, size); while (size) { page = vmalloc_to_page(base); offset = offset_in_page(base); len = min_t(size_t, PAGE_SIZE - offset, size); BUG_ON(!bio_add_page(bio, page, len, offset)); size -= len; base += len; }} static struct pcache_backing_dev_req *req_type_req_alloc(struct pcache_backing_dev *backing_dev, struct pcache_backing_dev_req_opts *opts){ struct pcache_request *pcache_req = opts->req.upper_req; struct pcache_backing_dev_req *backing_req; struct bio *orig = pcache_req->bio; backing_req = mempool_alloc(&backing_dev->req_pool, opts->gfp_mask); if (!backing_req) return NULL; memset(backing_req, 0, sizeof(struct pcache_backing_dev_req)); bio_init_clone(backing_dev->dm_dev->bdev, &backing_req->bio, orig, opts->gfp_mask); backing_req->type = BACKING_DEV_REQ_TYPE_REQ; backing_req->backing_dev = backing_dev; atomic_inc(&backing_dev->inflight_reqs); return backing_req;} static struct pcache_backing_dev_req *kmem_type_req_alloc(struct pcache_backing_dev *backing_dev, struct pcache_backing_dev_req_opts *opts){ struct pcache_backing_dev_req *backing_req; u32 n_vecs = bio_add_max_vecs(opts->kmem.data, opts->kmem.len); backing_req = mempool_alloc(&backing_dev->req_pool, opts->gfp_mask); if (!backing_req) return NULL; memset(backing_req, 0, sizeof(struct pcache_backing_dev_req)); if (n_vecs > BACKING_DEV_REQ_INLINE_BVECS) { backing_req->kmem.bvecs = mempool_alloc(&backing_dev->bvec_pool, opts->gfp_mask); if (!backing_req->kmem.bvecs) goto free_backing_req; } else { backing_req->kmem.bvecs = backing_req->kmem.inline_bvecs; } backing_req->kmem.n_vecs = n_vecs; backing_req->type = BACKING_DEV_REQ_TYPE_KMEM; backing_req->backing_dev = backing_dev; atomic_inc(&backing_dev->inflight_reqs); return backing_req; free_backing_req: mempool_free(backing_req, &backing_dev->req_pool); return NULL;} struct pcache_backing_dev_req *backing_dev_req_alloc(struct pcache_backing_dev *backing_dev, struct pcache_backing_dev_req_opts *opts){ if (opts->type == BACKING_DEV_REQ_TYPE_REQ) return req_type_req_alloc(backing_dev, opts); if (opts->type == BACKING_DEV_REQ_TYPE_KMEM) return kmem_type_req_alloc(backing_dev, opts); BUG();} static void req_type_req_init(struct pcache_backing_dev_req *backing_req, struct pcache_backing_dev_req_opts *opts){ struct pcache_request *pcache_req = opts->req.upper_req; struct bio *clone; u32 off = opts->req.req_off; u32 len = opts->req.len; clone = &backing_req->bio; BUG_ON(off & SECTOR_MASK); BUG_ON(len & SECTOR_MASK); bio_trim(clone, off >> SECTOR_SHIFT, len >> SECTOR_SHIFT); clone->bi_iter.bi_sector = (pcache_req->off + off) >> SECTOR_SHIFT; clone->bi_private = backing_req; clone->bi_end_io = backing_dev_bio_end; INIT_LIST_HEAD(&backing_req->node); backing_req->end_req = opts->end_fn; pcache_req_get(pcache_req); backing_req->req.upper_req = pcache_req; backing_req->req.bio_off = off;} static void kmem_type_req_init(struct pcache_backing_dev_req *backing_req, struct pcache_backing_dev_req_opts *opts){ struct pcache_backing_dev *backing_dev = backing_req->backing_dev; struct bio *backing_bio; bio_init(&backing_req->bio, backing_dev->dm_dev->bdev, backing_req->kmem.bvecs, backing_req->kmem.n_vecs, opts->kmem.opf); backing_bio = &backing_req->bio; bio_map(backing_bio, opts->kmem.data, opts->kmem.len); backing_bio->bi_iter.bi_sector = (opts->kmem.backing_off) >> SECTOR_SHIFT; backing_bio->bi_private = backing_req; backing_bio->bi_end_io = backing_dev_bio_end; INIT_LIST_HEAD(&backing_req->node); backing_req->end_req = opts->end_fn; backing_req->priv_data = opts->priv_data;} void backing_dev_req_init(struct pcache_backing_dev_req *backing_req, struct pcache_backing_dev_req_opts *opts){ if (opts->type == BACKING_DEV_REQ_TYPE_REQ) return req_type_req_init(backing_req, opts); if (opts->type == BACKING_DEV_REQ_TYPE_KMEM) return kmem_type_req_init(backing_req, opts); BUG();} struct pcache_backing_dev_req *backing_dev_req_create(struct pcache_backing_dev *backing_dev, struct pcache_backing_dev_req_opts *opts){ struct pcache_backing_dev_req *backing_req; backing_req = backing_dev_req_alloc(backing_dev, opts); if (!backing_req) return NULL; backing_dev_req_init(backing_req, opts); return backing_req;} void backing_dev_flush(struct pcache_backing_dev *backing_dev){ blkdev_issue_flush(backing_dev->dm_dev->bdev);} int pcache_backing_init(void){ u32 max_bvecs = (PCACHE_CACHE_SUBTREE_SIZE >> PAGE_SHIFT) + 1; int ret; backing_req_cache = KMEM_CACHE(pcache_backing_dev_req, 0); if (!backing_req_cache) { ret = -ENOMEM; goto err; } backing_bvec_cache = kmem_cache_create("pcache-bvec-slab", max_bvecs * sizeof(struct bio_vec), 0, 0, NULL); if (!backing_bvec_cache) { ret = -ENOMEM; goto destroy_req_cache; } return 0;destroy_req_cache: kmem_cache_destroy(backing_req_cache);err: return ret;} void pcache_backing_exit(void){ kmem_cache_destroy(backing_bvec_cache); kmem_cache_destroy(backing_req_cache);}
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/blkdev.h>
#include "../dm-core.h"
#include "pcache_internal.h"
#include "cache_dev.h"
#include "backing_dev.h"
#include "cache.h"
#include "dm_pcache.h"
static struct kmem_cache *backing_req_cache;
static struct kmem_cache *backing_bvec_cache;
static void backing_dev_exit(struct pcache_backing_dev *backing_dev)
{
mempool_exit(&backing_dev->req_pool);
mempool_exit(&backing_dev->bvec_pool);
}
static void req_submit_fn(struct work_struct *work);
static void req_complete_fn(struct work_struct *work);
static int backing_dev_init(struct dm_pcache *pcache)
{
struct pcache_backing_dev *backing_dev = &pcache->backing_dev;
int ret;
ret = mempool_init_slab_pool(&backing_dev->req_pool, 128, backing_req_cache);
if (ret)
goto err;
ret = mempool_init_slab_pool(&backing_dev->bvec_pool, 128, backing_bvec_cache);
if (ret)
goto req_pool_exit;
INIT_LIST_HEAD(&backing_dev->submit_list);
INIT_LIST_HEAD(&backing_dev->complete_list);
spin_lock_init(&backing_dev->submit_lock);
spin_lock_init(&backing_dev->complete_lock);
INIT_WORK(&backing_dev->req_submit_work, req_submit_fn);
INIT_WORK(&backing_dev->req_complete_work, req_complete_fn);
atomic_set(&backing_dev->inflight_reqs, 0);
init_waitqueue_head(&backing_dev->inflight_wq);
return 0;
req_pool_exit:
mempool_exit(&backing_dev->req_pool);
err:
return ret;
}
int backing_dev_start(struct dm_pcache *pcache)
{
struct pcache_backing_dev *backing_dev = &pcache->backing_dev;
int ret;
ret = backing_dev_init(pcache);
if (ret)
return ret;
backing_dev->dev_size = bdev_nr_sectors(backing_dev->dm_dev->bdev);
return 0;
}
void backing_dev_stop(struct dm_pcache *pcache)
{
struct pcache_backing_dev *backing_dev = &pcache->backing_dev;
/*
* There should not be any new request comming, just wait
* inflight requests done.
*/
wait_event(backing_dev->inflight_wq,
atomic_read(&backing_dev->inflight_reqs) == 0);
flush_work(&backing_dev->req_submit_work);
flush_work(&backing_dev->req_complete_work);
backing_dev_exit(backing_dev);
}
/* pcache_backing_dev_req functions */
void backing_dev_req_end(struct pcache_backing_dev_req *backing_req)
{
struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
if (backing_req->end_req)
backing_req->end_req(backing_req, backing_req->ret);
switch (backing_req->type) {
case BACKING_DEV_REQ_TYPE_REQ:
if (backing_req->req.upper_req)
pcache_req_put(backing_req->req.upper_req, backing_req->ret);
break;
case BACKING_DEV_REQ_TYPE_KMEM:
if (backing_req->kmem.bvecs != backing_req->kmem.inline_bvecs)
mempool_free(backing_req->kmem.bvecs, &backing_dev->bvec_pool);
break;
default:
BUG();
}
mempool_free(backing_req, &backing_dev->req_pool);
if (atomic_dec_and_test(&backing_dev->inflight_reqs))
wake_up(&backing_dev->inflight_wq);
}
static void req_complete_fn(struct work_struct *work)
{
struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_complete_work);
struct pcache_backing_dev_req *backing_req;
LIST_HEAD(tmp_list);
spin_lock_irq(&backing_dev->complete_lock);
list_splice_init(&backing_dev->complete_list, &tmp_list);
spin_unlock_irq(&backing_dev->complete_lock);
while (!list_empty(&tmp_list)) {
backing_req = list_first_entry(&tmp_list,
struct pcache_backing_dev_req, node);
list_del_init(&backing_req->node);
backing_dev_req_end(backing_req);
}
}
static void backing_dev_bio_end(struct bio *bio)
{
struct pcache_backing_dev_req *backing_req = bio->bi_private;
struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
unsigned long flags;
backing_req->ret = blk_status_to_errno(bio->bi_status);
spin_lock_irqsave(&backing_dev->complete_lock, flags);
list_move_tail(&backing_req->node, &backing_dev->complete_list);
queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_complete_work);
spin_unlock_irqrestore(&backing_dev->complete_lock, flags);
}
static void req_submit_fn(struct work_struct *work)
{
struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_submit_work);
struct pcache_backing_dev_req *backing_req;
LIST_HEAD(tmp_list);
spin_lock(&backing_dev->submit_lock);
list_splice_init(&backing_dev->submit_list, &tmp_list);
spin_unlock(&backing_dev->submit_lock);
while (!list_empty(&tmp_list)) {
backing_req = list_first_entry(&tmp_list,
struct pcache_backing_dev_req, node);
list_del_init(&backing_req->node);
submit_bio_noacct(&backing_req->bio);
}
}
void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req, bool direct)
{
struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
if (direct) {
submit_bio_noacct(&backing_req->bio);
return;
}
spin_lock(&backing_dev->submit_lock);
list_add_tail(&backing_req->node, &backing_dev->submit_list);
queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_submit_work);
spin_unlock(&backing_dev->submit_lock);
}
static void bio_map(struct bio *bio, void *base, size_t size)
{
struct page *page;
unsigned int offset;
unsigned int len;
if (!is_vmalloc_addr(base)) {
page = virt_to_page(base);
offset = offset_in_page(base);
BUG_ON(!bio_add_page(bio, page, size, offset));
return;
}
flush_kernel_vmap_range(base, size);
while (size) {
page = vmalloc_to_page(base);
offset = offset_in_page(base);
len = min_t(size_t, PAGE_SIZE - offset, size);
BUG_ON(!bio_add_page(bio, page, len, offset));
size -= len;
base += len;
}
}
static struct pcache_backing_dev_req *req_type_req_alloc(struct pcache_backing_dev *backing_dev,
struct pcache_backing_dev_req_opts *opts)
{
struct pcache_request *pcache_req = opts->req.upper_req;
struct pcache_backing_dev_req *backing_req;
struct bio *orig = pcache_req->bio;
backing_req = mempool_alloc(&backing_dev->req_pool, opts->gfp_mask);
if (!backing_req)
return NULL;
memset(backing_req, 0, sizeof(struct pcache_backing_dev_req));
bio_init_clone(backing_dev->dm_dev->bdev, &backing_req->bio, orig, opts->gfp_mask);
backing_req->type = BACKING_DEV_REQ_TYPE_REQ;
backing_req->backing_dev = backing_dev;
atomic_inc(&backing_dev->inflight_reqs);
return backing_req;
}
static struct pcache_backing_dev_req *kmem_type_req_alloc(struct pcache_backing_dev *backing_dev,
struct pcache_backing_dev_req_opts *opts)
{
struct pcache_backing_dev_req *backing_req;
u32 n_vecs = bio_add_max_vecs(opts->kmem.data, opts->kmem.len);
backing_req = mempool_alloc(&backing_dev->req_pool, opts->gfp_mask);
if (!backing_req)
return NULL;
memset(backing_req, 0, sizeof(struct pcache_backing_dev_req));
if (n_vecs > BACKING_DEV_REQ_INLINE_BVECS) {
backing_req->kmem.bvecs = mempool_alloc(&backing_dev->bvec_pool, opts->gfp_mask);
if (!backing_req->kmem.bvecs)
goto free_backing_req;
} else {
backing_req->kmem.bvecs = backing_req->kmem.inline_bvecs;
}
backing_req->kmem.n_vecs = n_vecs;
backing_req->type = BACKING_DEV_REQ_TYPE_KMEM;
backing_req->backing_dev = backing_dev;
atomic_inc(&backing_dev->inflight_reqs);
return backing_req;
free_backing_req:
mempool_free(backing_req, &backing_dev->req_pool);
return NULL;
}
struct pcache_backing_dev_req *backing_dev_req_alloc(struct pcache_backing_dev *backing_dev,
struct pcache_backing_dev_req_opts *opts)
{
if (opts->type == BACKING_DEV_REQ_TYPE_REQ)
return req_type_req_alloc(backing_dev, opts);
if (opts->type == BACKING_DEV_REQ_TYPE_KMEM)
return kmem_type_req_alloc(backing_dev, opts);
BUG();
}
static void req_type_req_init(struct pcache_backing_dev_req *backing_req,
struct pcache_backing_dev_req_opts *opts)
{
struct pcache_request *pcache_req = opts->req.upper_req;
struct bio *clone;
u32 off = opts->req.req_off;
u32 len = opts->req.len;
clone = &backing_req->bio;
BUG_ON(off & SECTOR_MASK);
BUG_ON(len & SECTOR_MASK);
bio_trim(clone, off >> SECTOR_SHIFT, len >> SECTOR_SHIFT);
clone->bi_iter.bi_sector = (pcache_req->off + off) >> SECTOR_SHIFT;
clone->bi_private = backing_req;
clone->bi_end_io = backing_dev_bio_end;
INIT_LIST_HEAD(&backing_req->node);
backing_req->end_req = opts->end_fn;
pcache_req_get(pcache_req);
backing_req->req.upper_req = pcache_req;
backing_req->req.bio_off = off;
}
static void kmem_type_req_init(struct pcache_backing_dev_req *backing_req,
struct pcache_backing_dev_req_opts *opts)
{
struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
struct bio *backing_bio;
bio_init(&backing_req->bio, backing_dev->dm_dev->bdev, backing_req->kmem.bvecs,
backing_req->kmem.n_vecs, opts->kmem.opf);
backing_bio = &backing_req->bio;
bio_map(backing_bio, opts->kmem.data, opts->kmem.len);
backing_bio->bi_iter.bi_sector = (opts->kmem.backing_off) >> SECTOR_SHIFT;
backing_bio->bi_private = backing_req;
backing_bio->bi_end_io = backing_dev_bio_end;
INIT_LIST_HEAD(&backing_req->node);
backing_req->end_req = opts->end_fn;
backing_req->priv_data = opts->priv_data;
}
void backing_dev_req_init(struct pcache_backing_dev_req *backing_req,
struct pcache_backing_dev_req_opts *opts)
{
if (opts->type == BACKING_DEV_REQ_TYPE_REQ)
return req_type_req_init(backing_req, opts);
if (opts->type == BACKING_DEV_REQ_TYPE_KMEM)
return kmem_type_req_init(backing_req, opts);
BUG();
}
struct pcache_backing_dev_req *backing_dev_req_create(struct pcache_backing_dev *backing_dev,
struct pcache_backing_dev_req_opts *opts)
{
struct pcache_backing_dev_req *backing_req;
backing_req = backing_dev_req_alloc(backing_dev, opts);
if (!backing_req)
return NULL;
backing_dev_req_init(backing_req, opts);
return backing_req;
}
void backing_dev_flush(struct pcache_backing_dev *backing_dev)
{
blkdev_issue_flush(backing_dev->dm_dev->bdev);
}
int pcache_backing_init(void)
{
u32 max_bvecs = (PCACHE_CACHE_SUBTREE_SIZE >> PAGE_SHIFT) + 1;
int ret;
backing_req_cache = KMEM_CACHE(pcache_backing_dev_req, 0);
if (!backing_req_cache) {
ret = -ENOMEM;
goto err;
}
backing_bvec_cache = kmem_cache_create("pcache-bvec-slab",
max_bvecs * sizeof(struct bio_vec),
0, 0, NULL);
if (!backing_bvec_cache) {
ret = -ENOMEM;
goto destroy_req_cache;
}
return 0;
destroy_req_cache:
kmem_cache_destroy(backing_req_cache);
err:
return ret;
}
void pcache_backing_exit(void)
{
kmem_cache_destroy(backing_bvec_cache);
kmem_cache_destroy(backing_req_cache);
}