Message ID | 20220207141348.4235-6-nj.shetty@samsung.com |
---|---|
State | Superseded |
Headers | show |
Series | Add Copy offload support | expand |
Hi Nitesh, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on axboe-block/for-next] [also build test WARNING on next-20220207] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Nitesh-Shetty/block-make-bio_map_kern-non-static/20220207-231407 base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next config: nios2-randconfig-r001-20220207 (https://download.01.org/0day-ci/archive/20220208/202202081132.axCkiVgv-lkp@intel.com/config) compiler: nios2-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/a7bb30870db803af4ad955a968992222bcfb478f git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Nitesh-Shetty/block-make-bio_map_kern-non-static/20220207-231407 git checkout a7bb30870db803af4ad955a968992222bcfb478f # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=nios2 SHELL=/bin/bash If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): block/blk-lib.c:185:5: warning: no previous prototype for 'blk_copy_offload' [-Wmissing-prototypes] 185 | int blk_copy_offload(struct block_device *src_bdev, int nr_srcs, | ^~~~~~~~~~~~~~~~ >> block/blk-lib.c:275:5: warning: no previous prototype for 'blk_submit_rw_buf' [-Wmissing-prototypes] 275 | int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len, | ^~~~~~~~~~~~~~~~~ vim +/blk_submit_rw_buf +275 block/blk-lib.c 180 181 /* 182 * blk_copy_offload - Use device's native copy offload feature 183 * Go through user provide payload, prepare new payload based on device's copy offload limits. 184 */ > 185 int blk_copy_offload(struct block_device *src_bdev, int nr_srcs, 186 struct range_entry *rlist, struct block_device *dst_bdev, gfp_t gfp_mask) 187 { 188 struct request_queue *sq = bdev_get_queue(src_bdev); 189 struct request_queue *dq = bdev_get_queue(dst_bdev); 190 struct bio *read_bio, *write_bio; 191 struct copy_ctx *ctx; 192 struct cio *cio; 193 struct page *token; 194 sector_t src_blk, copy_len, dst_blk; 195 sector_t remaining, max_copy_len = LONG_MAX; 196 int ri = 0, ret = 0; 197 198 cio = kzalloc(sizeof(struct cio), GFP_KERNEL); 199 if (!cio) 200 return -ENOMEM; 201 atomic_set(&cio->refcount, 0); 202 cio->rlist = rlist; 203 204 max_copy_len = min3(max_copy_len, (sector_t)sq->limits.max_copy_sectors, 205 (sector_t)dq->limits.max_copy_sectors); 206 max_copy_len = min3(max_copy_len, (sector_t)sq->limits.max_copy_range_sectors, 207 (sector_t)dq->limits.max_copy_range_sectors) << SECTOR_SHIFT; 208 209 for (ri = 0; ri < nr_srcs; ri++) { 210 cio->rlist[ri].comp_len = rlist[ri].len; 211 for (remaining = rlist[ri].len, src_blk = rlist[ri].src, dst_blk = rlist[ri].dst; 212 remaining > 0; 213 remaining -= copy_len, src_blk += copy_len, dst_blk += copy_len) { 214 copy_len = min(remaining, max_copy_len); 215 216 token = alloc_page(gfp_mask); 217 if (unlikely(!token)) { 218 ret = -ENOMEM; 219 goto err_token; 220 } 221 222 read_bio = bio_alloc(src_bdev, 1, REQ_OP_READ | REQ_COPY | REQ_NOMERGE, 223 gfp_mask); 224 if (!read_bio) { 225 ret = -ENOMEM; 226 goto err_read_bio; 227 } 228 read_bio->bi_iter.bi_sector = src_blk >> SECTOR_SHIFT; 229 read_bio->bi_iter.bi_size = copy_len; 230 __bio_add_page(read_bio, token, PAGE_SIZE, 0); 231 ret = submit_bio_wait(read_bio); 232 if (ret) { 233 bio_put(read_bio); 234 goto err_read_bio; 235 } 236 bio_put(read_bio); 237 ctx = kzalloc(sizeof(struct copy_ctx), gfp_mask); 238 if (!ctx) { 239 ret = -ENOMEM; 240 goto err_read_bio; 241 } 242 ctx->cio = cio; 243 ctx->range_idx = ri; 244 ctx->start_sec = rlist[ri].src; 245 246 write_bio = bio_alloc(dst_bdev, 1, REQ_OP_WRITE | REQ_COPY | REQ_NOMERGE, 247 gfp_mask); 248 if (!write_bio) { 249 ret = -ENOMEM; 250 goto err_read_bio; 251 } 252 253 write_bio->bi_iter.bi_sector = dst_blk >> SECTOR_SHIFT; 254 write_bio->bi_iter.bi_size = copy_len; 255 __bio_add_page(write_bio, token, PAGE_SIZE, 0); 256 write_bio->bi_end_io = bio_copy_end_io; 257 write_bio->bi_private = ctx; 258 atomic_inc(&cio->refcount); 259 submit_bio(write_bio); 260 } 261 } 262 263 /* Wait for completion of all IO's*/ 264 return cio_await_completion(cio); 265 266 err_read_bio: 267 __free_page(token); 268 err_token: 269 rlist[ri].comp_len = min_t(sector_t, rlist[ri].comp_len, (rlist[ri].len - remaining)); 270 271 cio->io_err = ret; 272 return cio_await_completion(cio); 273 } 274 > 275 int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len, 276 sector_t sector, unsigned int op, gfp_t gfp_mask) 277 { 278 struct request_queue *q = bdev_get_queue(bdev); 279 struct bio *bio, *parent = NULL; 280 sector_t max_hw_len = min_t(unsigned int, queue_max_hw_sectors(q), 281 queue_max_segments(q) << (PAGE_SHIFT - SECTOR_SHIFT)) << SECTOR_SHIFT; 282 sector_t len, remaining; 283 int ret; 284 285 for (remaining = buf_len; remaining > 0; remaining -= len) { 286 len = min_t(int, max_hw_len, remaining); 287 retry: 288 bio = bio_map_kern(q, buf, len, gfp_mask); 289 if (IS_ERR(bio)) { 290 len >>= 1; 291 if (len) 292 goto retry; 293 return PTR_ERR(bio); 294 } 295 296 bio->bi_iter.bi_sector = sector >> SECTOR_SHIFT; 297 bio->bi_opf = op; 298 bio_set_dev(bio, bdev); 299 bio->bi_end_io = NULL; 300 bio->bi_private = NULL; 301 302 if (parent) { 303 bio_chain(parent, bio); 304 submit_bio(parent); 305 } 306 parent = bio; 307 sector += len; 308 buf = (char *) buf + len; 309 } 310 ret = submit_bio_wait(bio); 311 bio_put(bio); 312 313 return ret; 314 } 315 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On Mon, 7 Feb 2022, Nitesh Shetty wrote: > + goto retry; > + return PTR_ERR(bio); > + } > + > + bio->bi_iter.bi_sector = sector >> SECTOR_SHIFT; > + bio->bi_opf = op; > + bio_set_dev(bio, bdev); > @@ -346,6 +463,8 @@ int blkdev_issue_copy(struct block_device *src_bdev, int nr, > > if (blk_check_copy_offload(src_q, dest_q)) > ret = blk_copy_offload(src_bdev, nr, rlist, dest_bdev, gfp_mask); > + else > + ret = blk_copy_emulate(src_bdev, nr, rlist, dest_bdev, gfp_mask); > > return ret; > } The emulation is not reliable because a device mapper device may be reconfigured and it may lose the copy capability between the calls to blk_check_copy_offload and blk_copy_offload. You should call blk_copy_emulate if blk_copy_offload returns an error. Mikulas
On Wed, Feb 16, 2022 at 08:32:45AM -0500, Mikulas Patocka wrote: > > > On Mon, 7 Feb 2022, Nitesh Shetty wrote: > > > + goto retry; > > + return PTR_ERR(bio); > > + } > > + > > + bio->bi_iter.bi_sector = sector >> SECTOR_SHIFT; > > + bio->bi_opf = op; > > + bio_set_dev(bio, bdev); > > @@ -346,6 +463,8 @@ int blkdev_issue_copy(struct block_device *src_bdev, int nr, > > > > if (blk_check_copy_offload(src_q, dest_q)) > > ret = blk_copy_offload(src_bdev, nr, rlist, dest_bdev, gfp_mask); > > + else > > + ret = blk_copy_emulate(src_bdev, nr, rlist, dest_bdev, gfp_mask); > > > > return ret; > > } > > The emulation is not reliable because a device mapper device may be > reconfigured and it may lose the copy capability between the calls to > blk_check_copy_offload and blk_copy_offload. > > You should call blk_copy_emulate if blk_copy_offload returns an error. > > Mikulas > > I agree, it was in our todo list to fallback to emulation for partial copy offload failures. In next version we will add this. -- Nitesh Shetty
diff --git a/block/blk-lib.c b/block/blk-lib.c index 3ae2c27b566e..05c8cd02fffc 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -272,6 +272,65 @@ int blk_copy_offload(struct block_device *src_bdev, int nr_srcs, return cio_await_completion(cio); } +int blk_submit_rw_buf(struct block_device *bdev, void *buf, sector_t buf_len, + sector_t sector, unsigned int op, gfp_t gfp_mask) +{ + struct request_queue *q = bdev_get_queue(bdev); + struct bio *bio, *parent = NULL; + sector_t max_hw_len = min_t(unsigned int, queue_max_hw_sectors(q), + queue_max_segments(q) << (PAGE_SHIFT - SECTOR_SHIFT)) << SECTOR_SHIFT; + sector_t len, remaining; + int ret; + + for (remaining = buf_len; remaining > 0; remaining -= len) { + len = min_t(int, max_hw_len, remaining); +retry: + bio = bio_map_kern(q, buf, len, gfp_mask); + if (IS_ERR(bio)) { + len >>= 1; + if (len) + goto retry; + return PTR_ERR(bio); + } + + bio->bi_iter.bi_sector = sector >> SECTOR_SHIFT; + bio->bi_opf = op; + bio_set_dev(bio, bdev); + bio->bi_end_io = NULL; + bio->bi_private = NULL; + + if (parent) { + bio_chain(parent, bio); + submit_bio(parent); + } + parent = bio; + sector += len; + buf = (char *) buf + len; + } + ret = submit_bio_wait(bio); + bio_put(bio); + + return ret; +} + +static void *blk_alloc_buf(sector_t req_size, sector_t *alloc_size, gfp_t gfp_mask) +{ + int min_size = PAGE_SIZE; + void *buf; + + while (req_size >= min_size) { + buf = kvmalloc(req_size, gfp_mask); + if (buf) { + *alloc_size = req_size; + return buf; + } + /* retry half the requested size */ + req_size >>= 1; + } + + return NULL; +} + static inline int blk_copy_sanity_check(struct block_device *src_bdev, struct block_device *dst_bdev, struct range_entry *rlist, int nr) { @@ -297,6 +356,64 @@ static inline int blk_copy_sanity_check(struct block_device *src_bdev, return 0; } +static inline sector_t blk_copy_max_range(struct range_entry *rlist, int nr, sector_t *max_len) +{ + int i; + sector_t len = 0; + + *max_len = 0; + for (i = 0; i < nr; i++) { + *max_len = max(*max_len, rlist[i].len); + len += rlist[i].len; + } + + return len; +} + +/* + * If native copy offload feature is absent, this function tries to emulate, + * by copying data from source to a temporary buffer and from buffer to + * destination device. + */ +static int blk_copy_emulate(struct block_device *src_bdev, int nr, + struct range_entry *rlist, struct block_device *dest_bdev, gfp_t gfp_mask) +{ + void *buf = NULL; + int ret, nr_i = 0; + sector_t src, dst, copy_len, buf_len, read_len, copied_len, max_len = 0, remaining = 0; + + copy_len = blk_copy_max_range(rlist, nr, &max_len); + buf = blk_alloc_buf(max_len, &buf_len, gfp_mask); + if (!buf) + return -ENOMEM; + + for (copied_len = 0; copied_len < copy_len; copied_len += read_len) { + if (!remaining) { + rlist[nr_i].comp_len = 0; + src = rlist[nr_i].src; + dst = rlist[nr_i].dst; + remaining = rlist[nr_i++].len; + } + + read_len = min_t(sector_t, remaining, buf_len); + ret = blk_submit_rw_buf(src_bdev, buf, read_len, src, REQ_OP_READ, gfp_mask); + if (ret) + goto out; + src += read_len; + remaining -= read_len; + ret = blk_submit_rw_buf(dest_bdev, buf, read_len, dst, REQ_OP_WRITE, + gfp_mask); + if (ret) + goto out; + else + rlist[nr_i - 1].comp_len += read_len; + dst += read_len; + } +out: + kvfree(buf); + return ret; +} + static inline bool blk_check_copy_offload(struct request_queue *src_q, struct request_queue *dest_q) { @@ -346,6 +463,8 @@ int blkdev_issue_copy(struct block_device *src_bdev, int nr, if (blk_check_copy_offload(src_q, dest_q)) ret = blk_copy_offload(src_bdev, nr, rlist, dest_bdev, gfp_mask); + else + ret = blk_copy_emulate(src_bdev, nr, rlist, dest_bdev, gfp_mask); return ret; }
For the devices which does not support copy, copy emulation is added. Copy-emulation is implemented by reading from source ranges into memory and writing to the corresponding destination synchronously. TODO: Optimise emulation. Signed-off-by: Nitesh Shetty <nj.shetty@samsung.com> --- block/blk-lib.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+)