Skip to content

Commit f6e9abf

Browse files
YuKuai-huaweikawasaki
authored andcommitted
brd: use page reference to protect page lifetime
As discussed [1], hold rcu for copying data from/to page is too heavy. it's better to protect page with rcu around for page lookup and then grab a reference to prevent page to be freed by discard. [1] https://lore.kernel.org/all/[email protected]/ Signed-off-by: Yu Kuai <[email protected]>
1 parent ef18525 commit f6e9abf

1 file changed

Lines changed: 46 additions & 27 deletions

File tree

drivers/block/brd.c

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,45 +44,72 @@ struct brd_device {
4444
};
4545

4646
/*
47-
* Look up and return a brd's page for a given sector.
47+
* Look up and return a brd's page with reference grabbed for a given sector.
4848
*/
4949
static struct page *brd_lookup_page(struct brd_device *brd, sector_t sector)
5050
{
51-
return xa_load(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT);
51+
struct page *page;
52+
XA_STATE(xas, &brd->brd_pages, sector >> PAGE_SECTORS_SHIFT);
53+
54+
rcu_read_lock();
55+
repeat:
56+
xas_reset(&xas);
57+
page = xas_load(&xas);
58+
if (xas_retry(&xas, page))
59+
goto repeat;
60+
61+
if (!page || xa_is_value(page)) {
62+
page = NULL;
63+
goto out;
64+
}
65+
66+
if (!get_page_unless_zero(page))
67+
goto repeat;
68+
69+
if (unlikely(page != xas_reload(&xas))) {
70+
put_page(page);
71+
goto repeat;
72+
}
73+
out:
74+
rcu_read_unlock();
75+
76+
return page;
5277
}
5378

5479
/*
5580
* Insert a new page for a given sector, if one does not already exist.
81+
* The returned page will grab reference.
5682
*/
5783
static struct page *brd_insert_page(struct brd_device *brd, sector_t sector,
5884
blk_opf_t opf)
59-
__releases(rcu)
60-
__acquires(rcu)
6185
{
6286
gfp_t gfp = (opf & REQ_NOWAIT) ? GFP_NOWAIT : GFP_NOIO;
6387
struct page *page, *ret;
6488

65-
rcu_read_unlock();
6689
page = alloc_page(gfp | __GFP_ZERO | __GFP_HIGHMEM);
67-
if (!page) {
68-
rcu_read_lock();
90+
if (!page)
6991
return ERR_PTR(-ENOMEM);
70-
}
7192

7293
xa_lock(&brd->brd_pages);
7394
ret = __xa_cmpxchg(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT, NULL,
7495
page, gfp);
75-
rcu_read_lock();
76-
if (ret) {
96+
if (!ret) {
97+
brd->brd_nr_pages++;
98+
get_page(page);
7799
xa_unlock(&brd->brd_pages);
78-
__free_page(page);
79-
if (xa_is_err(ret))
80-
return ERR_PTR(xa_err(ret));
100+
return page;
101+
}
102+
103+
if (!xa_is_err(ret)) {
104+
get_page(ret);
105+
xa_unlock(&brd->brd_pages);
106+
put_page(page);
81107
return ret;
82108
}
83-
brd->brd_nr_pages++;
109+
84110
xa_unlock(&brd->brd_pages);
85-
return page;
111+
put_page(page);
112+
return ERR_PTR(xa_err(ret));
86113
}
87114

88115
/*
@@ -95,7 +122,7 @@ static void brd_free_pages(struct brd_device *brd)
95122
pgoff_t idx;
96123

97124
xa_for_each(&brd->brd_pages, idx, page) {
98-
__free_page(page);
125+
put_page(page);
99126
cond_resched();
100127
}
101128

@@ -117,7 +144,6 @@ static bool brd_rw_bvec(struct brd_device *brd, struct bio *bio)
117144

118145
bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset);
119146

120-
rcu_read_lock();
121147
page = brd_lookup_page(brd, sector);
122148
if (!page && op_is_write(opf)) {
123149
page = brd_insert_page(brd, sector, opf);
@@ -135,27 +161,20 @@ static bool brd_rw_bvec(struct brd_device *brd, struct bio *bio)
135161
memset(kaddr, 0, bv.bv_len);
136162
}
137163
kunmap_local(kaddr);
138-
rcu_read_unlock();
139164

140165
bio_advance_iter_single(bio, &bio->bi_iter, bv.bv_len);
166+
if (page)
167+
put_page(page);
141168
return true;
142169

143170
out_error:
144-
rcu_read_unlock();
145171
if (PTR_ERR(page) == -ENOMEM && (opf & REQ_NOWAIT))
146172
bio_wouldblock_error(bio);
147173
else
148174
bio_io_error(bio);
149175
return false;
150176
}
151177

152-
static void brd_free_one_page(struct rcu_head *head)
153-
{
154-
struct page *page = container_of(head, struct page, rcu_head);
155-
156-
__free_page(page);
157-
}
158-
159178
static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size)
160179
{
161180
sector_t aligned_sector = round_up(sector, PAGE_SECTORS);
@@ -170,7 +189,7 @@ static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size)
170189
while (aligned_sector < aligned_end && aligned_sector < rd_size * 2) {
171190
page = __xa_erase(&brd->brd_pages, aligned_sector >> PAGE_SECTORS_SHIFT);
172191
if (page) {
173-
call_rcu(&page->rcu_head, brd_free_one_page);
192+
put_page(page);
174193
brd->brd_nr_pages--;
175194
}
176195
aligned_sector += PAGE_SECTORS;

0 commit comments

Comments
 (0)