Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

brd: avoid extra xarray lookups on first write

The xarray can return the previous entry at a location. Use this
fact to simplify the brd code when there is no existing page at
a location. This also slighly improves the handling of racy
discards as we now always have a page under RCU protection by the
time we are ready to copy the data.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Yu Kuai <yukuai3@huawei.com>
Link: https://lore.kernel.org/r/20250507060700.3929430-1-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Christoph Hellwig and committed by
Jens Axboe
bbcacab2 1e332795

+33 -43
+33 -43
drivers/block/brd.c
··· 54 54 /* 55 55 * Insert a new page for a given sector, if one does not already exist. 56 56 */ 57 - static int brd_insert_page(struct brd_device *brd, sector_t sector, gfp_t gfp) 57 + static struct page *brd_insert_page(struct brd_device *brd, sector_t sector, 58 + blk_opf_t opf) 59 + __releases(rcu) 60 + __acquires(rcu) 58 61 { 59 - pgoff_t idx = sector >> PAGE_SECTORS_SHIFT; 60 - struct page *page; 61 - int ret = 0; 62 + gfp_t gfp = (opf & REQ_NOWAIT) ? GFP_NOWAIT : GFP_NOIO; 63 + struct page *page, *ret; 62 64 63 - page = brd_lookup_page(brd, sector); 64 - if (page) 65 - return 0; 66 - 65 + rcu_read_unlock(); 67 66 page = alloc_page(gfp | __GFP_ZERO | __GFP_HIGHMEM); 67 + rcu_read_lock(); 68 68 if (!page) 69 - return -ENOMEM; 69 + return ERR_PTR(-ENOMEM); 70 70 71 71 xa_lock(&brd->brd_pages); 72 - ret = __xa_insert(&brd->brd_pages, idx, page, gfp); 73 - if (!ret) 74 - brd->brd_nr_pages++; 75 - xa_unlock(&brd->brd_pages); 76 - 77 - if (ret < 0) { 72 + ret = __xa_cmpxchg(&brd->brd_pages, sector >> PAGE_SECTORS_SHIFT, NULL, 73 + page, gfp); 74 + if (ret) { 75 + xa_unlock(&brd->brd_pages); 78 76 __free_page(page); 79 - if (ret == -EBUSY) 80 - ret = 0; 77 + if (xa_is_err(ret)) 78 + return ERR_PTR(xa_err(ret)); 79 + return ret; 81 80 } 82 - return ret; 81 + brd->brd_nr_pages++; 82 + xa_unlock(&brd->brd_pages); 83 + return page; 83 84 } 84 85 85 86 /* ··· 115 114 116 115 bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset); 117 116 118 - if (op_is_write(opf)) { 119 - int err; 120 - 121 - /* 122 - * Must use NOIO because we don't want to recurse back into the 123 - * block or filesystem layers from page reclaim. 124 - */ 125 - err = brd_insert_page(brd, sector, 126 - (opf & REQ_NOWAIT) ? GFP_NOWAIT : GFP_NOIO); 127 - if (err) { 128 - if (err == -ENOMEM && (opf & REQ_NOWAIT)) 129 - bio_wouldblock_error(bio); 130 - else 131 - bio_io_error(bio); 132 - return false; 133 - } 134 - } 135 - 136 117 rcu_read_lock(); 137 118 page = brd_lookup_page(brd, sector); 119 + if (!page && op_is_write(opf)) { 120 + page = brd_insert_page(brd, sector, opf); 121 + if (IS_ERR(page)) 122 + goto out_error; 123 + } 138 124 139 125 kaddr = bvec_kmap_local(&bv); 140 126 if (op_is_write(opf)) { 141 - /* 142 - * Page can be removed by concurrent discard, it's fine to skip 143 - * the write and user will read zero data if page does not 144 - * exist. 145 - */ 146 - if (page) 147 - memcpy_to_page(page, offset, kaddr, bv.bv_len); 127 + memcpy_to_page(page, offset, kaddr, bv.bv_len); 148 128 } else { 149 129 if (page) 150 130 memcpy_from_page(kaddr, page, offset, bv.bv_len); ··· 137 155 138 156 bio_advance_iter_single(bio, &bio->bi_iter, bv.bv_len); 139 157 return true; 158 + 159 + out_error: 160 + rcu_read_unlock(); 161 + if (PTR_ERR(page) == -ENOMEM && (opf & REQ_NOWAIT)) 162 + bio_wouldblock_error(bio); 163 + else 164 + bio_io_error(bio); 165 + return false; 140 166 } 141 167 142 168 static void brd_free_one_page(struct rcu_head *head)