Skip to content

Commit c34549f

Browse files
author
Ming Lei
committed
brd: use page reference to protect page lifetime
JIRA: https://issues.redhat.com/browse/RHEL-116171 commit 2a06145 Author: Yu Kuai <yukuai3@huawei.com> Date: Mon Aug 11 14:56:28 2025 +0800 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/eb41cab3-5946-4fe3-a1be-843dd6fca159@kernel.dk/ Signed-off-by: Yu Kuai <yukuai3@huawei.com> Link: https://lore.kernel.org/r/20250811065628.1829339-1-yukuai1@huaweicloud.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Ming Lei <ming.lei@redhat.com>
1 parent 14c3cf9 commit c34549f

File tree

1 file changed

+48
-27
lines changed

1 file changed

+48
-27
lines changed

drivers/block/brd.c

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,45 +44,74 @@ 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+
page = xas_load(&xas);
57+
if (xas_retry(&xas, page)) {
58+
xas_reset(&xas);
59+
goto repeat;
60+
}
61+
62+
if (!page)
63+
goto out;
64+
65+
if (!get_page_unless_zero(page)) {
66+
xas_reset(&xas);
67+
goto repeat;
68+
}
69+
70+
if (unlikely(page != xas_reload(&xas))) {
71+
put_page(page);
72+
xas_reset(&xas);
73+
goto repeat;
74+
}
75+
out:
76+
rcu_read_unlock();
77+
78+
return page;
5279
}
5380

5481
/*
5582
* Insert a new page for a given sector, if one does not already exist.
83+
* The returned page will grab reference.
5684
*/
5785
static struct page *brd_insert_page(struct brd_device *brd, sector_t sector,
5886
blk_opf_t opf)
59-
__releases(rcu)
60-
__acquires(rcu)
6187
{
6288
gfp_t gfp = (opf & REQ_NOWAIT) ? GFP_NOWAIT : GFP_NOIO;
6389
struct page *page, *ret;
6490

65-
rcu_read_unlock();
6691
page = alloc_page(gfp | __GFP_ZERO | __GFP_HIGHMEM);
67-
if (!page) {
68-
rcu_read_lock();
92+
if (!page)
6993
return ERR_PTR(-ENOMEM);
70-
}
7194

7295
xa_lock(&brd->brd_pages);
7396
ret = __xa_cmpxchg(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT, NULL,
7497
page, gfp);
75-
rcu_read_lock();
76-
if (ret) {
98+
if (!ret) {
99+
brd->brd_nr_pages++;
100+
get_page(page);
101+
xa_unlock(&brd->brd_pages);
102+
return page;
103+
}
104+
105+
if (!xa_is_err(ret)) {
106+
get_page(ret);
77107
xa_unlock(&brd->brd_pages);
78-
__free_page(page);
79-
if (xa_is_err(ret))
80-
return ERR_PTR(xa_err(ret));
108+
put_page(page);
81109
return ret;
82110
}
83-
brd->brd_nr_pages++;
111+
84112
xa_unlock(&brd->brd_pages);
85-
return page;
113+
put_page(page);
114+
return ERR_PTR(xa_err(ret));
86115
}
87116

88117
/*
@@ -95,7 +124,7 @@ static void brd_free_pages(struct brd_device *brd)
95124
pgoff_t idx;
96125

97126
xa_for_each(&brd->brd_pages, idx, page) {
98-
__free_page(page);
127+
put_page(page);
99128
cond_resched();
100129
}
101130

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

118147
bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset);
119148

120-
rcu_read_lock();
121149
page = brd_lookup_page(brd, sector);
122150
if (!page && op_is_write(opf)) {
123151
page = brd_insert_page(brd, sector, opf);
@@ -135,27 +163,20 @@ static bool brd_rw_bvec(struct brd_device *brd, struct bio *bio)
135163
memset(kaddr, 0, bv.bv_len);
136164
}
137165
kunmap_local(kaddr);
138-
rcu_read_unlock();
139166

140167
bio_advance_iter_single(bio, &bio->bi_iter, bv.bv_len);
168+
if (page)
169+
put_page(page);
141170
return true;
142171

143172
out_error:
144-
rcu_read_unlock();
145173
if (PTR_ERR(page) == -ENOMEM && (opf & REQ_NOWAIT))
146174
bio_wouldblock_error(bio);
147175
else
148176
bio_io_error(bio);
149177
return false;
150178
}
151179

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-
159180
static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size)
160181
{
161182
sector_t aligned_sector = round_up(sector, PAGE_SECTORS);
@@ -170,7 +191,7 @@ static void brd_do_discard(struct brd_device *brd, sector_t sector, u32 size)
170191
while (aligned_sector < aligned_end && aligned_sector < rd_size * 2) {
171192
page = __xa_erase(&brd->brd_pages, aligned_sector >> PAGE_SECTORS_SHIFT);
172193
if (page) {
173-
call_rcu(&page->rcu_head, brd_free_one_page);
194+
put_page(page);
174195
brd->brd_nr_pages--;
175196
}
176197
aligned_sector += PAGE_SECTORS;

0 commit comments

Comments
 (0)