new file mode 100644
@@ -0,0 +1,593 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/blkdev.h>
+
+#include "pcache_internal.h"
+#include "cache_dev.h"
+#include "cache.h"
+#include "backing_dev.h"
+#include "logic_dev.h"
+#include "meta_segment.h"
+
+static ssize_t path_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+
+ return sprintf(buf, "%s\n", backing_dev->backing_dev_info.path);
+}
+static DEVICE_ATTR_ADMIN_RO(path);
+
+static ssize_t mapped_id_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+
+ return sprintf(buf, "%u\n", backing_dev->logic_dev->mapped_id);
+}
+static DEVICE_ATTR_ADMIN_RO(mapped_id);
+
+/* sysfs for cache */
+static ssize_t cache_segs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+
+ return sprintf(buf, "%u\n", backing_dev->cache_segs);
+}
+static DEVICE_ATTR_ADMIN_RO(cache_segs);
+
+static ssize_t cache_used_segs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pcache_backing_dev *backing_dev;
+ u32 segs_used;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+ segs_used = bitmap_weight(backing_dev->cache->seg_map, backing_dev->cache->n_segs);
+ return sprintf(buf, "%u\n", segs_used);
+}
+static DEVICE_ATTR_ADMIN_RO(cache_used_segs);
+
+static ssize_t cache_gc_percent_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+
+ return sprintf(buf, "%u\n", backing_dev->backing_dev_info.cache_info.gc_percent);
+}
+
+static ssize_t cache_gc_percent_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct pcache_backing_dev *backing_dev;
+ unsigned long val;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ backing_dev = container_of(dev, struct pcache_backing_dev, device);
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val < PCACHE_CACHE_GC_PERCENT_MIN ||
+ val > PCACHE_CACHE_GC_PERCENT_MAX)
+ return -EINVAL;
+
+ backing_dev->backing_dev_info.cache_info.gc_percent = val;
+ backing_dev_info_write(backing_dev);
+
+ return size;
+}
+static DEVICE_ATTR_ADMIN_RW(cache_gc_percent);
+
+static struct attribute *backing_dev_attrs[] = {
+ &dev_attr_path.attr,
+ &dev_attr_mapped_id.attr,
+ &dev_attr_cache_segs.attr,
+ &dev_attr_cache_used_segs.attr,
+ &dev_attr_cache_gc_percent.attr,
+ NULL
+};
+
+static struct attribute_group backing_dev_attr_group = {
+ .attrs = backing_dev_attrs,
+};
+
+static const struct attribute_group *backing_dev_attr_groups[] = {
+ &backing_dev_attr_group,
+ NULL
+};
+
+static void backing_dev_release(struct device *dev)
+{
+}
+
+const struct device_type backing_dev_type = {
+ .name = "backing_dev",
+ .groups = backing_dev_attr_groups,
+ .release = backing_dev_release,
+};
+
+void backing_dev_info_write(struct pcache_backing_dev *backing_dev)
+{
+ struct pcache_backing_dev_info *info;
+ struct pcache_meta_header *meta;
+
+ mutex_lock(&backing_dev->info_lock);
+
+ meta = &backing_dev->backing_dev_info.header;
+ meta->seq++;
+
+ info = pcache_meta_find_oldest(&backing_dev->backing_dev_info_addr->header, PCACHE_BACKING_DEV_INFO_SIZE);
+ memcpy(info, &backing_dev->backing_dev_info, sizeof(struct pcache_backing_dev_info));
+ info->header.crc = pcache_meta_crc(&info->header, PCACHE_BACKING_DEV_INFO_SIZE);
+
+ cache_dev_flush(backing_dev->cache_dev, info, PCACHE_BACKING_DEV_INFO_SIZE);
+ mutex_unlock(&backing_dev->info_lock);
+}
+
+static int backing_dev_info_load(struct pcache_backing_dev *backing_dev)
+{
+ struct pcache_backing_dev_info *info;
+ int ret = 0;
+
+ mutex_lock(&backing_dev->info_lock);
+
+ info = pcache_meta_find_latest(&backing_dev->backing_dev_info_addr->header, PCACHE_BACKING_DEV_INFO_SIZE);
+ if (!info) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ memcpy(&backing_dev->backing_dev_info, info, sizeof(struct pcache_backing_dev_info));
+unlock:
+ mutex_unlock(&backing_dev->info_lock);
+ return ret;
+}
+
+static void backing_dev_free(struct pcache_backing_dev *backing_dev)
+{
+ drain_workqueue(backing_dev->task_wq);
+ destroy_workqueue(backing_dev->task_wq);
+ kmem_cache_destroy(backing_dev->backing_req_cache);
+ kfree(backing_dev);
+}
+
+static void req_submit_fn(struct work_struct *work);
+static void req_complete_fn(struct work_struct *work);
+static struct pcache_backing_dev *backing_dev_alloc(struct pcache_cache_dev *cache_dev)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = kzalloc(sizeof(struct pcache_backing_dev), GFP_KERNEL);
+ if (!backing_dev)
+ return NULL;
+
+ backing_dev->backing_req_cache = KMEM_CACHE(pcache_backing_dev_req, 0);
+ if (!backing_dev->backing_req_cache)
+ goto free_backing_dev;
+
+ backing_dev->task_wq = alloc_workqueue("pcache-backing-wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
+ if (!backing_dev->task_wq)
+ goto destroy_io_cache;
+
+ backing_dev->cache_dev = cache_dev;
+
+ mutex_init(&backing_dev->info_lock);
+ INIT_LIST_HEAD(&backing_dev->node);
+ INIT_LIST_HEAD(&backing_dev->submit_list);
+ INIT_LIST_HEAD(&backing_dev->complete_list);
+ spin_lock_init(&backing_dev->lock);
+ 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);
+
+ return backing_dev;
+
+destroy_io_cache:
+ kmem_cache_destroy(backing_dev->backing_req_cache);
+free_backing_dev:
+ kfree(backing_dev);
+ return NULL;
+}
+
+static int backing_dev_cache_init(struct pcache_backing_dev *backing_dev,
+ struct pcache_backing_dev_opts *backing_opts,
+ bool new_backing_dev)
+{
+ struct pcache_cache_opts cache_opts = { 0 };
+ int ret;
+
+ backing_dev->cache_segs = backing_opts->cache_segs;
+ cache_opts.cache_info = &backing_dev->backing_dev_info.cache_info;
+ cache_opts.n_segs = backing_opts->cache_segs;
+ cache_opts.n_paral = backing_opts->queues;
+ cache_opts.new_cache = new_backing_dev;
+ cache_opts.data_crc = backing_opts->data_crc;
+ cache_opts.bdev_file = backing_dev->bdev_file;
+ cache_opts.dev_size = backing_dev->dev_size;
+
+ backing_dev->cache = pcache_cache_alloc(backing_dev, &cache_opts);
+ if (!backing_dev->cache) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static void backing_dev_cache_destroy(struct pcache_backing_dev *backing_dev)
+{
+ if (backing_dev->cache)
+ pcache_cache_destroy(backing_dev->cache);
+}
+
+static int backing_dev_sysfs_init(struct pcache_backing_dev *backing_dev)
+{
+ struct device *dev;
+ struct pcache_logic_dev *logic_dev = backing_dev->logic_dev;
+ int ret;
+
+ dev = &backing_dev->device;
+ device_initialize(dev);
+ device_set_pm_not_required(dev);
+ dev->type = &backing_dev_type;
+ dev->parent = &backing_dev->cache_dev->device;
+ dev_set_name(dev, "backing_dev%d", backing_dev->backing_dev_id);
+
+ ret = device_add(dev);
+ if (ret)
+ goto err;
+
+ ret = sysfs_create_link(&disk_to_dev(logic_dev->disk)->kobj,
+ &backing_dev->device.kobj, "pcache");
+ if (ret)
+ goto dev_unregister;
+
+ bd_link_disk_holder(backing_dev->bdev, logic_dev->disk);
+ bd_link_disk_holder(backing_dev->cache_dev->bdev, logic_dev->disk);
+
+ return 0;
+
+dev_unregister:
+ device_unregister(dev);
+err:
+ return ret;
+}
+
+static void backing_dev_sysfs_exit(struct pcache_backing_dev *backing_dev)
+{
+ struct pcache_logic_dev *logic_dev = backing_dev->logic_dev;
+
+ bd_unlink_disk_holder(backing_dev->cache_dev->bdev, logic_dev->disk);
+ bd_unlink_disk_holder(backing_dev->bdev, logic_dev->disk);
+ sysfs_remove_link(&disk_to_dev(logic_dev->disk)->kobj, "pcache");
+ device_unregister(&backing_dev->device);
+}
+
+static int backing_dev_init(struct pcache_backing_dev *backing_dev, struct pcache_backing_dev_opts *backing_opts)
+{
+ struct pcache_cache_dev *cache_dev = backing_dev->cache_dev;
+ bool new_backing;
+ int ret;
+
+ memcpy(backing_dev->backing_dev_info.path, backing_opts->path, PCACHE_PATH_LEN);
+
+ backing_dev->bdev_file = bdev_file_open_by_path(backing_dev->backing_dev_info.path,
+ BLK_OPEN_READ | BLK_OPEN_WRITE, backing_dev, NULL);
+ if (IS_ERR(backing_dev->bdev_file)) {
+ backing_dev_err(backing_dev, "failed to open bdev: %d", (int)PTR_ERR(backing_dev->bdev_file));
+ ret = PTR_ERR(backing_dev->bdev_file);
+ goto err;
+ }
+
+ backing_dev->bdev = file_bdev(backing_dev->bdev_file);
+ backing_dev->dev_size = bdev_nr_sectors(backing_dev->bdev);
+
+ ret = bioset_init(&backing_dev->bioset, 1024, 0, BIOSET_NEED_BVECS);
+ if (ret)
+ goto close_bdev;
+
+ ret = cache_dev_find_backing_info(cache_dev, backing_dev, &new_backing);
+ if (ret)
+ goto bioset_exit;
+
+ if (!new_backing)
+ backing_dev_info_load(backing_dev);
+
+ ret = backing_dev_cache_init(backing_dev, backing_opts, new_backing);
+ if (ret)
+ goto bioset_exit;
+
+ ret = logic_dev_start(backing_dev, backing_opts->queues);
+ if (ret)
+ goto destroy_cache;
+
+ ret = backing_dev_sysfs_init(backing_dev);
+ if (ret)
+ goto logic_dev_stop;
+
+ backing_dev->backing_dev_info.state = PCACHE_BACKING_STATE_RUNNING;
+ backing_dev->backing_dev_info.backing_dev_id = backing_dev->backing_dev_id;
+ backing_dev_info_write(backing_dev);
+
+ cache_dev_add_backing(cache_dev, backing_dev);
+
+ return 0;
+
+logic_dev_stop:
+ logic_dev_stop(backing_dev->logic_dev);
+destroy_cache:
+ backing_dev_cache_destroy(backing_dev);
+bioset_exit:
+ bioset_exit(&backing_dev->bioset);
+close_bdev:
+ fput(backing_dev->bdev_file);
+err:
+ return ret;
+}
+
+static int backing_dev_destroy(struct pcache_backing_dev *backing_dev)
+{
+ backing_dev_sysfs_exit(backing_dev);
+ logic_dev_stop(backing_dev->logic_dev);
+ backing_dev_cache_destroy(backing_dev);
+ bioset_exit(&backing_dev->bioset);
+ fput(backing_dev->bdev_file);
+
+ backing_dev->backing_dev_info.state = PCACHE_BACKING_STATE_NONE;
+ backing_dev_info_write(backing_dev);
+
+ return 0;
+}
+
+int backing_dev_start(struct pcache_cache_dev *cache_dev, struct pcache_backing_dev_opts *backing_opts)
+{
+ struct pcache_backing_dev *backing_dev;
+ int ret;
+
+ /* Check if path starts with "/dev/" */
+ if (strncmp(backing_opts->path, "/dev/", 5) != 0)
+ return -EINVAL;
+
+ backing_dev = backing_dev_alloc(cache_dev);
+ if (!backing_dev)
+ return -ENOMEM;
+
+ ret = backing_dev_init(backing_dev, backing_opts);
+ if (ret)
+ goto destroy_backing_dev;
+
+ return 0;
+
+destroy_backing_dev:
+ backing_dev_free(backing_dev);
+
+ return ret;
+}
+
+int backing_dev_stop(struct pcache_cache_dev *cache_dev, u32 backing_dev_id)
+{
+ struct pcache_backing_dev *backing_dev;
+
+ backing_dev = cache_dev_fetch_backing(cache_dev, backing_dev_id);
+ if (!backing_dev)
+ return -ENOENT;
+
+ backing_dev_destroy(backing_dev);
+ backing_dev_free(backing_dev);
+
+ return 0;
+}
+
+/* pcache_backing_dev_req functions */
+static void end_req(struct kref *ref)
+{
+ struct pcache_backing_dev_req *backing_req = container_of(ref, struct pcache_backing_dev_req, ref);
+ struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
+
+ spin_lock(&backing_dev->complete_lock);
+ list_move_tail(&backing_req->node, &backing_dev->complete_list);
+ spin_unlock(&backing_dev->complete_lock);
+
+ queue_work(backing_dev->task_wq, &backing_dev->req_complete_work);
+}
+
+static void backing_dev_bio_end(struct bio *bio)
+{
+ struct pcache_backing_dev_req *backing_req = bio->bi_private;
+ int ret = bio->bi_status;
+
+ if (ret && !backing_req->ret)
+ backing_req->ret = ret;
+
+ kref_put(&backing_req->ref, end_req);
+ bio_put(bio);
+}
+
+static int map_bio_pages(struct bio *bio, struct request *req, u32 req_off, u32 len)
+{
+ struct bio_vec src_bvec;
+ struct bvec_iter src_iter;
+ size_t mapped = 0, offset = 0;
+ struct bio *src_bio;
+
+ src_bio = req->bio;
+
+next_bio:
+ bio_for_each_segment(src_bvec, src_bio, src_iter) {
+ struct page *page = src_bvec.bv_page;
+ size_t page_off = src_bvec.bv_offset;
+ size_t page_len = src_bvec.bv_len;
+
+ if (offset + page_len <= req_off) {
+ offset += page_len;
+ continue;
+ }
+
+ size_t start = (req_off > offset) ? (req_off - offset) : 0;
+ size_t map_len = min(len - mapped, page_len - start);
+
+ if (bio_add_page(bio, page, map_len, page_off + start) != map_len) {
+ pr_err("Failed to map page to bio\n");
+ break;
+ }
+
+ mapped += map_len;
+ if (mapped >= len)
+ goto out;
+
+ offset += page_len;
+ }
+
+ if (src_bio->bi_next) {
+ src_bio = src_bio->bi_next;
+ goto next_bio;
+ }
+out:
+ return 0;
+}
+
+struct pcache_backing_dev_req *backing_dev_req_create(struct pcache_backing_dev *backing_dev, struct pcache_request *pcache_req,
+ u32 off, u32 len, backing_req_end_fn_t end_req)
+{
+ struct pcache_backing_dev_req *backing_req;
+ u32 mapped_len = 0;
+ struct bio *bio;
+
+ backing_req = kmem_cache_zalloc(backing_dev->backing_req_cache, GFP_ATOMIC);
+ if (!backing_req)
+ return NULL;
+
+ backing_req->backing_dev = backing_dev;
+ INIT_LIST_HEAD(&backing_req->node);
+ kref_init(&backing_req->ref);
+ backing_req->end_req = end_req;
+ backing_req->bio_off = off;
+next_bio:
+ bio = bio_alloc_bioset(backing_dev->bdev,
+ BIO_MAX_VECS,
+ req_op(pcache_req->req),
+ GFP_ATOMIC, &backing_dev->bioset);
+ if (!bio)
+ goto free_backing_req;
+
+ bio->bi_iter.bi_sector = (pcache_req->off + off + mapped_len) >> SECTOR_SHIFT;
+ bio->bi_iter.bi_size = 0;
+ bio->bi_private = backing_req;
+ bio->bi_end_io = backing_dev_bio_end;
+ kref_get(&backing_req->ref);
+
+ if (backing_req->bio)
+ bio->bi_next = backing_req->bio;
+ backing_req->bio = bio;
+
+ map_bio_pages(bio, pcache_req->req, off + mapped_len, len - mapped_len);
+ mapped_len += bio->bi_iter.bi_size;
+ if (mapped_len < len)
+ goto next_bio;
+
+ pcache_req_get(pcache_req);
+ backing_req->upper_req = pcache_req;
+
+ return backing_req;
+
+free_backing_req:
+ while (backing_req->bio) {
+ bio = backing_req->bio;
+ backing_req->bio = bio->bi_next;
+ bio_put(bio);
+ }
+ kmem_cache_free(backing_dev->backing_req_cache, backing_req);
+
+ return NULL;
+}
+
+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;
+ unsigned long flags;
+ 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);
+ while (backing_req->bio) {
+ struct bio *bio = backing_req->bio;
+
+ backing_req->bio = bio->bi_next;
+ submit_bio_noacct(bio);
+ }
+
+ local_irq_save(flags);
+ kref_put(&backing_req->ref, end_req);
+ local_irq_restore(flags);
+ }
+}
+
+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;
+ unsigned long flags;
+ LIST_HEAD(tmp_list);
+
+ spin_lock_irqsave(&backing_dev->complete_lock, flags);
+ list_splice_init(&backing_dev->complete_list, &tmp_list);
+ spin_unlock_irqrestore(&backing_dev->complete_lock, flags);
+
+ 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);
+ }
+}
+
+void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req)
+{
+ struct pcache_backing_dev *backing_dev = backing_req->backing_dev;
+
+ spin_lock(&backing_dev->submit_lock);
+ list_add_tail(&backing_req->node, &backing_dev->submit_list);
+ spin_unlock(&backing_dev->submit_lock);
+
+ queue_work(backing_dev->task_wq, &backing_dev->req_submit_work);
+}
+
+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);
+
+ kmem_cache_free(backing_dev->backing_req_cache, backing_req);
+}
new file mode 100644
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _BACKING_DEV_H
+#define _BACKING_DEV_H
+
+#include <linux/hashtable.h>
+
+#include "pcache_internal.h"
+#include "cache_dev.h"
+
+#define backing_dev_err(backing_dev, fmt, ...) \
+ cache_dev_err(backing_dev->cache_dev, "backing_dev%d: " fmt, \
+ backing_dev->backing_dev_id, ##__VA_ARGS__)
+#define backing_dev_info(backing_dev, fmt, ...) \
+ cache_dev_info(backing_dev->cache_dev, "backing_dev%d: " fmt, \
+ backing_dev->backing_dev_id, ##__VA_ARGS__)
+#define backing_dev_debug(backing_dev, fmt, ...) \
+ cache_dev_debug(backing_dev->cache_dev, "backing_dev%d: " fmt, \
+ backing_dev->backing_dev_id, ##__VA_ARGS__)
+
+#define PCACHE_BACKING_STATE_NONE 0
+#define PCACHE_BACKING_STATE_RUNNING 1
+
+struct pcache_cache_info;
+struct pcache_backing_dev_info {
+ struct pcache_meta_header header;
+ u8 state;
+ u8 res;
+
+ u16 res1;
+
+ u32 backing_dev_id;
+ u64 dev_size; /* nr_sectors */
+
+ char path[PCACHE_PATH_LEN];
+ struct pcache_cache_info cache_info;
+};
+
+struct pcache_backing_dev_req;
+typedef void (*backing_req_end_fn_t)(struct pcache_backing_dev_req *backing_req, int ret);
+
+struct pcache_request;
+struct pcache_backing_dev_req {
+ struct bio *bio;
+ struct pcache_backing_dev *backing_dev;
+
+ void *priv_data;
+ backing_req_end_fn_t end_req;
+
+ struct pcache_request *upper_req;
+ u32 bio_off;
+ struct list_head node;
+ struct kref ref;
+ int ret;
+};
+
+struct pcache_logic_dev;
+struct pcache_backing_dev {
+ u32 backing_dev_id;
+ struct pcache_cache_dev *cache_dev;
+ spinlock_t lock;
+ struct list_head node;
+ struct device device;
+
+ struct pcache_backing_dev_info backing_dev_info;
+ struct pcache_backing_dev_info *backing_dev_info_addr;
+ struct mutex info_lock;
+
+ struct block_device *bdev;
+ struct file *bdev_file;
+
+ struct workqueue_struct *task_wq;
+
+ struct bio_set bioset;
+ struct kmem_cache *backing_req_cache;
+ struct list_head submit_list;
+ spinlock_t submit_lock;
+ struct work_struct req_submit_work;
+
+ struct list_head complete_list;
+ spinlock_t complete_lock;
+ struct work_struct req_complete_work;
+
+ struct pcache_logic_dev *logic_dev;
+ u64 dev_size;
+
+ u32 cache_segs;
+ struct pcache_cache *cache;
+};
+
+struct pcache_backing_dev_opts {
+ char *path;
+ u32 queues;
+ u32 cache_segs;
+ bool data_crc;
+};
+
+int backing_dev_start(struct pcache_cache_dev *cache_dev, struct pcache_backing_dev_opts *backing_opts);
+int backing_dev_stop(struct pcache_cache_dev *cache_dev, u32 backing_dev_id);
+void backing_dev_info_write(struct pcache_backing_dev *backing_dev);
+
+void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req);
+void backing_dev_req_end(struct pcache_backing_dev_req *backing_req);
+struct pcache_backing_dev_req *backing_dev_req_create(struct pcache_backing_dev *backing_dev,
+ struct pcache_request *pcache_req, u32 off, u32 len, backing_req_end_fn_t end_req);
+#endif /* _BACKING_DEV_H */
This patch introduces support for managing backing devices in the pcache framework. A backing device represents a real block device (e.g., /dev/sdX), which is wrapped with caching support and exposed via a logical device (e.g., /dev/pcacheX). Key highlights: - `pcache_backing_dev`: Encapsulates the state of a backing device, including its metadata, bioset, workqueues, cache instance, and associated logic device. - Metadata persistence: Uses `pcache_backing_dev_info` to persist path, cache config (segment count, GC percent), and other info. Supports update and recovery. - Sysfs interface: Exposes `path`, `cache_segs`, `mapped_id`, `cache_used_segs`, and GC control under `/sys/class/.../backing_devX/`. - I/O request handling: Implements a generic `pcache_backing_dev_req` abstraction, which maps and submits bio chains to the underlying device. Completion is handled asynchronously via `workqueue` to enable decoupled upper-layer processing. - Initialization flow: `backing_dev_start()` prepares the backing device by opening the file, initializing bioset/workqueue, loading or creating metadata, instantiating the cache, starting the logic block device, and registering sysfs. - Clean shutdown via `backing_dev_stop()`. This forms the middle layer of pcache, bridging cache logic and the logical block device to actual physical storage. Signed-off-by: Dongsheng Yang <dongsheng.yang@linux.dev> --- drivers/block/pcache/backing_dev.c | 593 +++++++++++++++++++++++++++++ drivers/block/pcache/backing_dev.h | 105 +++++ 2 files changed, 698 insertions(+) create mode 100644 drivers/block/pcache/backing_dev.c create mode 100644 drivers/block/pcache/backing_dev.h