|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +#include <linux/blkdev.h> |
| 3 | + |
| 4 | +#include "../dm-core.h" |
| 5 | +#include "pcache_internal.h" |
| 6 | +#include "cache_dev.h" |
| 7 | +#include "backing_dev.h" |
| 8 | +#include "cache.h" |
| 9 | +#include "dm_pcache.h" |
| 10 | + |
| 11 | +static void backing_dev_exit(struct pcache_backing_dev *backing_dev) |
| 12 | +{ |
| 13 | + mempool_exit(&backing_dev->req_pool); |
| 14 | + kmem_cache_destroy(backing_dev->backing_req_cache); |
| 15 | +} |
| 16 | + |
| 17 | +static void req_submit_fn(struct work_struct *work); |
| 18 | +static void req_complete_fn(struct work_struct *work); |
| 19 | +static int backing_dev_init(struct dm_pcache *pcache) |
| 20 | +{ |
| 21 | + struct pcache_backing_dev *backing_dev = &pcache->backing_dev; |
| 22 | + int ret; |
| 23 | + |
| 24 | + backing_dev->backing_req_cache = KMEM_CACHE(pcache_backing_dev_req, 0); |
| 25 | + if (!backing_dev->backing_req_cache) { |
| 26 | + ret = -ENOMEM; |
| 27 | + goto err; |
| 28 | + } |
| 29 | + |
| 30 | + ret = mempool_init_slab_pool(&backing_dev->req_pool, 128, backing_dev->backing_req_cache); |
| 31 | + if (ret) |
| 32 | + goto cache_destroy; |
| 33 | + |
| 34 | + INIT_LIST_HEAD(&backing_dev->submit_list); |
| 35 | + INIT_LIST_HEAD(&backing_dev->complete_list); |
| 36 | + spin_lock_init(&backing_dev->submit_lock); |
| 37 | + spin_lock_init(&backing_dev->complete_lock); |
| 38 | + INIT_WORK(&backing_dev->req_submit_work, req_submit_fn); |
| 39 | + INIT_WORK(&backing_dev->req_complete_work, req_complete_fn); |
| 40 | + |
| 41 | + return 0; |
| 42 | +cache_destroy: |
| 43 | + kmem_cache_destroy(backing_dev->backing_req_cache); |
| 44 | +err: |
| 45 | + return ret; |
| 46 | +} |
| 47 | + |
| 48 | +int backing_dev_start(struct dm_pcache *pcache) |
| 49 | +{ |
| 50 | + struct pcache_backing_dev *backing_dev = &pcache->backing_dev; |
| 51 | + int ret; |
| 52 | + |
| 53 | + ret = backing_dev_init(pcache); |
| 54 | + if (ret) |
| 55 | + return ret; |
| 56 | + |
| 57 | + backing_dev->dev_size = bdev_nr_sectors(backing_dev->dm_dev->bdev); |
| 58 | + |
| 59 | + return 0; |
| 60 | +} |
| 61 | + |
| 62 | +void backing_dev_stop(struct dm_pcache *pcache) |
| 63 | +{ |
| 64 | + struct pcache_backing_dev *backing_dev = &pcache->backing_dev; |
| 65 | + |
| 66 | + flush_work(&backing_dev->req_submit_work); |
| 67 | + flush_work(&backing_dev->req_complete_work); |
| 68 | + |
| 69 | + /* There should be no inflight backing_dev_request */ |
| 70 | + BUG_ON(!list_empty(&backing_dev->submit_list)); |
| 71 | + BUG_ON(!list_empty(&backing_dev->complete_list)); |
| 72 | + |
| 73 | + backing_dev_exit(backing_dev); |
| 74 | +} |
| 75 | + |
| 76 | +/* pcache_backing_dev_req functions */ |
| 77 | +void backing_dev_req_end(struct pcache_backing_dev_req *backing_req) |
| 78 | +{ |
| 79 | + struct pcache_backing_dev *backing_dev = backing_req->backing_dev; |
| 80 | + |
| 81 | + if (backing_req->end_req) |
| 82 | + backing_req->end_req(backing_req, backing_req->ret); |
| 83 | + |
| 84 | + switch (backing_req->type) { |
| 85 | + case BACKING_DEV_REQ_TYPE_REQ: |
| 86 | + if (backing_req->req.upper_req) |
| 87 | + pcache_req_put(backing_req->req.upper_req, backing_req->ret); |
| 88 | + break; |
| 89 | + case BACKING_DEV_REQ_TYPE_KMEM: |
| 90 | + if (backing_req->kmem.bvecs != backing_req->kmem.inline_bvecs) |
| 91 | + kfree(backing_req->kmem.bvecs); |
| 92 | + break; |
| 93 | + default: |
| 94 | + BUG(); |
| 95 | + } |
| 96 | + |
| 97 | + mempool_free(backing_req, &backing_dev->req_pool); |
| 98 | +} |
| 99 | + |
| 100 | +static void req_complete_fn(struct work_struct *work) |
| 101 | +{ |
| 102 | + struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_complete_work); |
| 103 | + struct pcache_backing_dev_req *backing_req; |
| 104 | + LIST_HEAD(tmp_list); |
| 105 | + |
| 106 | + spin_lock_irq(&backing_dev->complete_lock); |
| 107 | + list_splice_init(&backing_dev->complete_list, &tmp_list); |
| 108 | + spin_unlock_irq(&backing_dev->complete_lock); |
| 109 | + |
| 110 | + while (!list_empty(&tmp_list)) { |
| 111 | + backing_req = list_first_entry(&tmp_list, |
| 112 | + struct pcache_backing_dev_req, node); |
| 113 | + list_del_init(&backing_req->node); |
| 114 | + backing_dev_req_end(backing_req); |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +static void backing_dev_bio_end(struct bio *bio) |
| 119 | +{ |
| 120 | + struct pcache_backing_dev_req *backing_req = bio->bi_private; |
| 121 | + struct pcache_backing_dev *backing_dev = backing_req->backing_dev; |
| 122 | + unsigned long flags; |
| 123 | + |
| 124 | + backing_req->ret = bio->bi_status; |
| 125 | + |
| 126 | + spin_lock_irqsave(&backing_dev->complete_lock, flags); |
| 127 | + list_move_tail(&backing_req->node, &backing_dev->complete_list); |
| 128 | + queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_complete_work); |
| 129 | + spin_unlock_irqrestore(&backing_dev->complete_lock, flags); |
| 130 | +} |
| 131 | + |
| 132 | +static void req_submit_fn(struct work_struct *work) |
| 133 | +{ |
| 134 | + struct pcache_backing_dev *backing_dev = container_of(work, struct pcache_backing_dev, req_submit_work); |
| 135 | + struct pcache_backing_dev_req *backing_req; |
| 136 | + LIST_HEAD(tmp_list); |
| 137 | + |
| 138 | + spin_lock(&backing_dev->submit_lock); |
| 139 | + list_splice_init(&backing_dev->submit_list, &tmp_list); |
| 140 | + spin_unlock(&backing_dev->submit_lock); |
| 141 | + |
| 142 | + while (!list_empty(&tmp_list)) { |
| 143 | + backing_req = list_first_entry(&tmp_list, |
| 144 | + struct pcache_backing_dev_req, node); |
| 145 | + list_del_init(&backing_req->node); |
| 146 | + submit_bio_noacct(&backing_req->bio); |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req, bool direct) |
| 151 | +{ |
| 152 | + struct pcache_backing_dev *backing_dev = backing_req->backing_dev; |
| 153 | + |
| 154 | + if (direct) { |
| 155 | + submit_bio_noacct(&backing_req->bio); |
| 156 | + return; |
| 157 | + } |
| 158 | + |
| 159 | + spin_lock(&backing_dev->submit_lock); |
| 160 | + list_add_tail(&backing_req->node, &backing_dev->submit_list); |
| 161 | + queue_work(BACKING_DEV_TO_PCACHE(backing_dev)->task_wq, &backing_dev->req_submit_work); |
| 162 | + spin_unlock(&backing_dev->submit_lock); |
| 163 | +} |
| 164 | + |
| 165 | +static void bio_map(struct bio *bio, void *base, size_t size) |
| 166 | +{ |
| 167 | + struct page *page; |
| 168 | + unsigned int offset; |
| 169 | + unsigned int len; |
| 170 | + |
| 171 | + if (!is_vmalloc_addr(base)) { |
| 172 | + page = virt_to_page(base); |
| 173 | + offset = offset_in_page(base); |
| 174 | + |
| 175 | + BUG_ON(!bio_add_page(bio, page, size, offset)); |
| 176 | + return; |
| 177 | + } |
| 178 | + |
| 179 | + flush_kernel_vmap_range(base, size); |
| 180 | + while (size) { |
| 181 | + page = vmalloc_to_page(base); |
| 182 | + offset = offset_in_page(base); |
| 183 | + len = min_t(size_t, PAGE_SIZE - offset, size); |
| 184 | + |
| 185 | + BUG_ON(!bio_add_page(bio, page, len, offset)); |
| 186 | + size -= len; |
| 187 | + base += len; |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +static struct pcache_backing_dev_req *req_type_req_alloc(struct pcache_backing_dev *backing_dev, |
| 192 | + struct pcache_backing_dev_req_opts *opts) |
| 193 | +{ |
| 194 | + struct pcache_request *pcache_req = opts->req.upper_req; |
| 195 | + struct pcache_backing_dev_req *backing_req; |
| 196 | + struct bio *orig = pcache_req->bio; |
| 197 | + int ret; |
| 198 | + |
| 199 | + backing_req = mempool_alloc(&backing_dev->req_pool, GFP_NOIO); |
| 200 | + if (!backing_req) |
| 201 | + return NULL; |
| 202 | + |
| 203 | + memset(backing_req, 0, sizeof(struct pcache_backing_dev_req)); |
| 204 | + |
| 205 | + ret = bio_init_clone(backing_dev->dm_dev->bdev, &backing_req->bio, orig, GFP_NOIO); |
| 206 | + if (ret) |
| 207 | + goto err_free_req; |
| 208 | + |
| 209 | + backing_req->type = BACKING_DEV_REQ_TYPE_REQ; |
| 210 | + backing_req->backing_dev = backing_dev; |
| 211 | + |
| 212 | + return backing_req; |
| 213 | + |
| 214 | +err_free_req: |
| 215 | + mempool_free(backing_req, &backing_dev->req_pool); |
| 216 | + return NULL; |
| 217 | +} |
| 218 | + |
| 219 | +static u32 get_n_vecs(void *data, u32 len) |
| 220 | +{ |
| 221 | + if (!is_vmalloc_addr(data)) |
| 222 | + return 1; |
| 223 | + |
| 224 | + return DIV_ROUND_UP(len, PAGE_SIZE); |
| 225 | +} |
| 226 | + |
| 227 | +static struct pcache_backing_dev_req *kmem_type_req_alloc(struct pcache_backing_dev *backing_dev, |
| 228 | + struct pcache_backing_dev_req_opts *opts) |
| 229 | +{ |
| 230 | + struct pcache_backing_dev_req *backing_req; |
| 231 | + u32 n_vecs = get_n_vecs(opts->kmem.data, opts->kmem.len); |
| 232 | + |
| 233 | + backing_req = mempool_alloc(&backing_dev->req_pool, GFP_NOIO); |
| 234 | + if (!backing_req) |
| 235 | + return NULL; |
| 236 | + |
| 237 | + memset(backing_req, 0, sizeof(struct pcache_backing_dev_req)); |
| 238 | + |
| 239 | + if (n_vecs > BACKING_DEV_REQ_INLINE_BVECS) { |
| 240 | + backing_req->kmem.bvecs = kmalloc_array(n_vecs, sizeof(struct bio_vec), GFP_NOIO); |
| 241 | + if (!backing_req->kmem.bvecs) |
| 242 | + goto err_free_req; |
| 243 | + } else { |
| 244 | + backing_req->kmem.bvecs = backing_req->kmem.inline_bvecs; |
| 245 | + } |
| 246 | + |
| 247 | + backing_req->kmem.n_vecs = n_vecs; |
| 248 | + backing_req->type = BACKING_DEV_REQ_TYPE_KMEM; |
| 249 | + backing_req->backing_dev = backing_dev; |
| 250 | + |
| 251 | + return backing_req; |
| 252 | + |
| 253 | +err_free_req: |
| 254 | + mempool_free(backing_req, &backing_dev->req_pool); |
| 255 | + return NULL; |
| 256 | +} |
| 257 | + |
| 258 | +struct pcache_backing_dev_req *backing_dev_req_alloc(struct pcache_backing_dev *backing_dev, |
| 259 | + struct pcache_backing_dev_req_opts *opts) |
| 260 | +{ |
| 261 | + if (opts->type == BACKING_DEV_REQ_TYPE_REQ) |
| 262 | + return req_type_req_alloc(backing_dev, opts); |
| 263 | + |
| 264 | + if (opts->type == BACKING_DEV_REQ_TYPE_KMEM) |
| 265 | + return kmem_type_req_alloc(backing_dev, opts); |
| 266 | + |
| 267 | + return NULL; |
| 268 | +} |
| 269 | + |
| 270 | +static void req_type_req_init(struct pcache_backing_dev_req *backing_req, |
| 271 | + struct pcache_backing_dev_req_opts *opts) |
| 272 | +{ |
| 273 | + struct pcache_request *pcache_req = opts->req.upper_req; |
| 274 | + struct bio *clone; |
| 275 | + u32 off = opts->req.req_off; |
| 276 | + u32 len = opts->req.len; |
| 277 | + |
| 278 | + clone = &backing_req->bio; |
| 279 | + BUG_ON(off & SECTOR_MASK); |
| 280 | + BUG_ON(len & SECTOR_MASK); |
| 281 | + bio_trim(clone, off >> SECTOR_SHIFT, len >> SECTOR_SHIFT); |
| 282 | + |
| 283 | + clone->bi_iter.bi_sector = (pcache_req->off + off) >> SECTOR_SHIFT; |
| 284 | + clone->bi_private = backing_req; |
| 285 | + clone->bi_end_io = backing_dev_bio_end; |
| 286 | + |
| 287 | + INIT_LIST_HEAD(&backing_req->node); |
| 288 | + backing_req->end_req = opts->end_fn; |
| 289 | + |
| 290 | + pcache_req_get(pcache_req); |
| 291 | + backing_req->req.upper_req = pcache_req; |
| 292 | + backing_req->req.bio_off = off; |
| 293 | +} |
| 294 | + |
| 295 | +static void kmem_type_req_init(struct pcache_backing_dev_req *backing_req, |
| 296 | + struct pcache_backing_dev_req_opts *opts) |
| 297 | +{ |
| 298 | + struct pcache_backing_dev *backing_dev = backing_req->backing_dev; |
| 299 | + struct bio *backing_bio; |
| 300 | + |
| 301 | + bio_init(&backing_req->bio, backing_dev->dm_dev->bdev, backing_req->kmem.bvecs, |
| 302 | + backing_req->kmem.n_vecs, opts->kmem.opf); |
| 303 | + |
| 304 | + backing_bio = &backing_req->bio; |
| 305 | + bio_map(backing_bio, opts->kmem.data, opts->kmem.len); |
| 306 | + |
| 307 | + backing_bio->bi_iter.bi_sector = (opts->kmem.backing_off) >> SECTOR_SHIFT; |
| 308 | + backing_bio->bi_private = backing_req; |
| 309 | + backing_bio->bi_end_io = backing_dev_bio_end; |
| 310 | + |
| 311 | + INIT_LIST_HEAD(&backing_req->node); |
| 312 | + backing_req->end_req = opts->end_fn; |
| 313 | + backing_req->priv_data = opts->priv_data; |
| 314 | +} |
| 315 | + |
| 316 | +void backing_dev_req_init(struct pcache_backing_dev_req *backing_req, |
| 317 | + struct pcache_backing_dev_req_opts *opts) |
| 318 | +{ |
| 319 | + if (opts->type == BACKING_DEV_REQ_TYPE_REQ) |
| 320 | + return req_type_req_init(backing_req, opts); |
| 321 | + |
| 322 | + if (opts->type == BACKING_DEV_REQ_TYPE_KMEM) |
| 323 | + return kmem_type_req_init(backing_req, opts); |
| 324 | + |
| 325 | + BUG(); |
| 326 | +} |
| 327 | + |
| 328 | +struct pcache_backing_dev_req *backing_dev_req_create(struct pcache_backing_dev *backing_dev, |
| 329 | + struct pcache_backing_dev_req_opts *opts) |
| 330 | +{ |
| 331 | + struct pcache_backing_dev_req *backing_req; |
| 332 | + |
| 333 | + backing_req = backing_dev_req_alloc(backing_dev, opts); |
| 334 | + if (!backing_req) |
| 335 | + return NULL; |
| 336 | + |
| 337 | + backing_dev_req_init(backing_req, opts); |
| 338 | + |
| 339 | + return backing_req; |
| 340 | +} |
| 341 | + |
| 342 | +void backing_dev_flush(struct pcache_backing_dev *backing_dev) |
| 343 | +{ |
| 344 | + blkdev_issue_flush(backing_dev->dm_dev->bdev); |
| 345 | +} |
0 commit comments