On 01/12/2020 08:14, SelvaKumar S wrote: > +static inline int bio_check_copy_eod(struct bio *bio, sector_t start, > + sector_t nr_sectors, sector_t maxsector) > +{ > + if (nr_sectors && maxsector && (nr_sectors > maxsector || > + start > maxsector - nr_sectors)) { Nit: I don't like the line break here, maybe: if (nr_sectors && maxsector && (nr_sectors > maxsector || start > maxsector - nr_sectors)) { > + handle_bad_sector(bio, maxsector); > + return -EIO; > + } > + return 0; > +} > + > /* > * Check whether this bio extends beyond the end of the device or partition. > * This may well happen - the kernel calls bread() without checking the size of > @@ -737,6 +748,75 @@ static inline int bio_check_eod(struct bio *bio, sector_t maxsector) > return 0; > } > > +/* > + * Check for copy limits and remap source ranges if needed. > + */ > +static inline int blk_check_copy(struct bio *bio) That function is a bit big to be marked as inline, isn't it? > +{ > + struct hd_struct *p = NULL; > + struct request_queue *q = bio->bi_disk->queue; > + struct blk_copy_payload *payload; > + unsigned short nr_range; > + int ret = -EIO; > + int i, copy_len = 0; > + > + rcu_read_lock(); > + > + if (bio->bi_partno) { > + p = __disk_get_part(bio->bi_disk, bio->bi_partno); > + if (unlikely(!p)) > + goto out; > + if (unlikely(bio_check_ro(bio, p))) > + goto out; > + } else { > + if (unlikely(bio_check_ro(bio, &bio->bi_disk->part0))) > + goto out; > + } > + > + payload = bio_data(bio); > + nr_range = payload->copy_range; > + > + /* cannot handle copy crossing nr_ranges limit */ > + if (payload->copy_range > q->limits.max_copy_nr_ranges) > + goto out; > + > + for (i = 0; i < nr_range; i++) { > + copy_len += payload->range[i].len; > + if (p) { > + if (bio_check_copy_eod(bio, payload->range[i].src, > + payload->range[i].len, part_nr_sects_read(p))) > + goto out; > + payload->range[i].src += p->start_sect; > + } else { > + if (unlikely(bio_check_copy_eod(bio, payload->range[i].src, > + payload->range[i].len, > + get_capacity(bio->bi_disk)))) > + goto out; > + } > + } > + > + /* cannot handle copy more than copy limits */ > + if (copy_len > q->limits.max_copy_sectors) > + goto out; > + > + if (p) { > + if (unlikely(bio_check_copy_eod(bio, bio->bi_iter.bi_sector, copy_len, > + part_nr_sects_read(p)))) > + goto out; > + } else { > + if (unlikely(bio_check_copy_eod(bio, bio->bi_iter.bi_sector, copy_len, > + get_capacity(bio->bi_disk)))) > + goto out; > + > + } > + All these if (p) {} else {} branches make this function a bit hard to follow. > + if (p) > + bio->bi_partno = 0; > + ret = 0; > +out: > + rcu_read_unlock(); > + return ret; > +} > /* > * Remap block n of partition p to block n+start(p) of the disk. > */ > diff --git a/block/blk-lib.c b/block/blk-lib.c > index e90614fd8d6a..db4947f7014d 100644 > --- a/block/blk-lib.c > +++ b/block/blk-lib.c > @@ -150,6 +150,122 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, > } > EXPORT_SYMBOL(blkdev_issue_discard); > > +int __blkdev_issue_copy(struct block_device *bdev, sector_t dest, > + sector_t nr_srcs, struct range_entry *rlist, gfp_t gfp_mask, > + int flags, struct bio **biop) > +{ > + struct request_queue *q = bdev_get_queue(bdev); > + struct bio *bio; > + struct blk_copy_payload *payload; > + unsigned int op; I don't think op is needed. > + sector_t bs_mask; > + sector_t src_sects, len = 0, total_len = 0; > + int i, ret, total_size; > + > + if (!q) > + return -ENXIO; > + > + if (!nr_srcs) > + return -EINVAL; > + > + if (bdev_read_only(bdev)) > + return -EPERM; > + > + if (!blk_queue_copy(q)) > + return -EOPNOTSUPP; > + op = REQ_OP_COPY; > + > + bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1; > + if (dest & bs_mask) > + return -EINVAL; > + > + payload = kmalloc(sizeof(struct blk_copy_payload) + > + nr_srcs * sizeof(struct range_entry), > + GFP_ATOMIC | __GFP_NOWARN); Please check if the use of struct_size() is possible. Probably even assign total_size here so you don't need to do the size calculation twice. > + if (!payload) > + return -ENOMEM; > + > + bio = bio_alloc(gfp_mask, 1); > + bio->bi_iter.bi_sector = dest; > + bio_set_dev(bio, bdev); > + bio_set_op_attrs(bio, op, REQ_NOMERGE); bio_set_op_attrs() is deprecated, please don't use it. bio->bi_opf = REQ_OP_COPY | REQ_NOMERGE; > + > + payload->dest = dest; > + > + for (i = 0; i < nr_srcs; i++) { > + /* copy payload provided are in bytes */ > + src_sects = rlist[i].src; > + if (src_sects & bs_mask) > + return -EINVAL; > + src_sects = src_sects >> SECTOR_SHIFT; > + > + if (len & bs_mask) > + return -EINVAL; > + len = rlist[i].len >> SECTOR_SHIFT; > + if (len > q->limits.max_copy_range_sectors) > + return -EINVAL; > + > + total_len += len; > + > + WARN_ON_ONCE((src_sects << 9) > UINT_MAX); > + > + payload->range[i].src = src_sects; > + payload->range[i].len = len; > + } > + > + /* storing # of source ranges */ > + payload->copy_range = i; > + /* storing copy len so far */ > + payload->copy_size = total_len; > + > + total_size = sizeof(struct blk_copy_payload) + nr_srcs * sizeof(struct range_entry); See above. > + ret = bio_add_page(bio, virt_to_page(payload), total_size, > + offset_in_page(payload)); > + if (ret != total_size) { > + kfree(payload); > + return -ENOMEM; > + } > + > + *biop = bio; > + return 0; > +} > +EXPORT_SYMBOL(__blkdev_issue_copy); > + > +/** > + * blkdev_issue_copy - queue a copy > + * @bdev: blockdev to issue copy for > + * @dest: dest sector > + * @nr_srcs: number of source ranges to copy > + * @rlist: list of range entries > + * @gfp_mask: memory allocation flags (for bio_alloc) > + * @flags: BLKDEV_COPY_* flags to control behaviour //TODO > + * > + * Description: > + * Issue a copy request for dest sector with source in rlist > + */ > +int blkdev_issue_copy(struct block_device *bdev, sector_t dest, > + int nr_srcs, struct range_entry *rlist, > + gfp_t gfp_mask, unsigned long flags) > +{ > + struct bio *bio = NULL; > + int ret; > + > + ret = __blkdev_issue_copy(bdev, dest, nr_srcs, rlist, gfp_mask, flags, > + &bio); > + if (!ret && bio) { > + ret = submit_bio_wait(bio); > + if (ret == -EOPNOTSUPP) > + ret = 0; > + > + kfree(page_address(bio_first_bvec_all(bio)->bv_page) + > + bio_first_bvec_all(bio)->bv_offset); > + bio_put(bio); > + } > + > + return ret; What happens with bio here if __blkdev_issue_copy() returns say -ENOMEM because bio_add_page() fails? Also please handle failure not success. Sth along the lines of ret = __blkdev_issue_copy(bdev, dest, nr_srcs, rlist, gfp_mask, flags, &bio); if (ret) ... ret = submit_bio_wait(); if (ret) ... ... return ret; } > +} > +EXPORT_SYMBOL(blkdev_issue_copy);