Skip to content

Commit de2b957

Browse files
yangdongshengkawasaki
authored andcommitted
dm-pcache: add backing device management
This patch introduces *backing_dev.{c,h}*, a self-contained layer that handles all interaction with the *backing block device* where cache write-back and cache-miss reads are serviced. Isolating this logic keeps the core dm-pcache code free of low-level bio plumbing. * Device setup / teardown - Opens the target with `dm_get_device()`, stores `bdev`, file and size, and initialises a dedicated `bioset`. - Gracefully releases resources via `backing_dev_stop()`. * Request object (`struct pcache_backing_dev_req`) - Two request flavours: - REQ-type – cloned from an upper `struct bio` issued to dm-pcache; trimmed and re-targeted to the backing LBA. - KMEM-type – maps an arbitrary kernel memory buffer into a freshly built. - Private completion callback (`end_req`) propagates status to the upper layer and handles resource recycling. * Submission & completion path - Lock-protected submit queue + worker (`req_submit_work`) let pcache push many requests asynchronously, at the same time, allow caller to submit backing_dev_req in atomic context. - End-io handler moves finished requests to a completion list processed by `req_complete_work`, ensuring callbacks run in process context. - Direct-submit option for non-atomic context. * Flush - `backing_dev_flush()` issues a flush to persist backing-device data. Signed-off-by: Dongsheng Yang <[email protected]>
1 parent 8802f83 commit de2b957

2 files changed

Lines changed: 438 additions & 0 deletions

File tree

drivers/md/dm-pcache/backing_dev.c

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
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

Comments
 (0)