@@ -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 */
4949static 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 */
5785static 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
143172out_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-
159180static 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