@@ -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 */
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+ 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 */
5783static 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
143170out_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-
159178static 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