374 lines · 10.2 KB · C
RawDownload
// 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);}