| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| #ifndef _BACKING_DEV_H |
| #define _BACKING_DEV_H |
| |
| #include <linux/device-mapper.h> |
| |
| #include "pcache_internal.h" |
| |
| struct pcache_backing_dev_req; |
| typedef void (*backing_req_end_fn_t)(struct pcache_backing_dev_req *backing_req, int ret); |
| |
| #define BACKING_DEV_REQ_TYPE_REQ 1 |
| #define BACKING_DEV_REQ_TYPE_KMEM 2 |
| |
| #define BACKING_DEV_REQ_INLINE_BVECS 4 |
| |
| struct pcache_request; |
| struct pcache_backing_dev_req { |
| u8 type; |
| struct bio bio; |
| struct pcache_backing_dev *backing_dev; |
| |
| void *priv_data; |
| backing_req_end_fn_t end_req; |
| |
| struct list_head node; |
| int ret; |
| |
| union { |
| struct { |
| struct pcache_request *upper_req; |
| u32 bio_off; |
| } req; |
| struct { |
| struct bio_vec inline_bvecs[BACKING_DEV_REQ_INLINE_BVECS]; |
| struct bio_vec *bvecs; |
| u32 n_vecs; |
| } kmem; |
| }; |
| }; |
| |
| struct pcache_backing_dev { |
| struct pcache_cache *cache; |
| |
| struct dm_dev *dm_dev; |
| mempool_t req_pool; |
| mempool_t bvec_pool; |
| |
| 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; |
| |
| atomic_t inflight_reqs; |
| wait_queue_head_t inflight_wq; |
| |
| u64 dev_size; |
| }; |
| |
| struct dm_pcache; |
| int backing_dev_start(struct dm_pcache *pcache); |
| void backing_dev_stop(struct dm_pcache *pcache); |
| |
| struct pcache_backing_dev_req_opts { |
| u32 type; |
| union { |
| struct { |
| struct pcache_request *upper_req; |
| u32 req_off; |
| u32 len; |
| } req; |
| struct { |
| void *data; |
| blk_opf_t opf; |
| u32 len; |
| u64 backing_off; |
| } kmem; |
| }; |
| |
| gfp_t gfp_mask; |
| backing_req_end_fn_t end_fn; |
| void *priv_data; |
| }; |
| |
| static inline u32 backing_dev_req_coalesced_max_len(const void *data, u32 len) |
| { |
| const void *p = data; |
| u32 done = 0, in_page, to_advance; |
| struct page *first_page, *next_page; |
| |
| if (!is_vmalloc_addr(data)) |
| return len; |
| |
| first_page = vmalloc_to_page(p); |
| advance: |
| in_page = PAGE_SIZE - offset_in_page(p); |
| to_advance = min_t(u32, in_page, len - done); |
| |
| done += to_advance; |
| p += to_advance; |
| |
| if (done == len) |
| return done; |
| |
| next_page = vmalloc_to_page(p); |
| if (zone_device_pages_have_same_pgmap(first_page, next_page)) |
| goto advance; |
| |
| return done; |
| } |
| |
| void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req, bool direct); |
| 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_backing_dev_req_opts *opts); |
| struct pcache_backing_dev_req *backing_dev_req_alloc(struct pcache_backing_dev *backing_dev, |
| struct pcache_backing_dev_req_opts *opts); |
| void backing_dev_req_init(struct pcache_backing_dev_req *backing_req, |
| struct pcache_backing_dev_req_opts *opts); |
| void backing_dev_flush(struct pcache_backing_dev *backing_dev); |
| |
| int pcache_backing_init(void); |
| void pcache_backing_exit(void); |
| #endif /* _BACKING_DEV_H */ |