dm-buffered: remove async memcpy Remove the async memcpy - it makes the target too complicated (and buggy). Signed-off-by: Mikulas Patocka --- drivers/md/dm-buffered-target.c | 159 +++++----------------------------------- 1 file changed, 21 insertions(+), 138 deletions(-) Index: linux-2.6/drivers/md/dm-buffered-target.c =================================================================== --- linux-2.6.orig/drivers/md/dm-buffered-target.c +++ linux-2.6/drivers/md/dm-buffered-target.c @@ -12,7 +12,6 @@ */ #include -#include #include #include @@ -37,8 +36,6 @@ struct buffered_c { sector_t start; sector_t block_mask; unsigned int block_shift; - unsigned int async:1; - unsigned int async_set:1; unsigned int buffer_size; unsigned int buffer_size_set:1; unsigned int buffer_size_default:1; @@ -53,13 +50,6 @@ struct buffered_c { atomic_t stats[S_END]; }; -/* buffer async_memcpy context */ -struct async_c { - struct async_submit_ctl submit; - struct bio *bio; - atomic_t buffer_refcount; -}; - /* Convert sector to bufio block number */ static sector_t _to_block(struct buffered_c *bc, sector_t sector) { @@ -72,68 +62,12 @@ static sector_t _sector_mod(struct buffe return sector & bc->block_mask; } -/* Use unutilized @bio->bi_next as endio reference counter and take out @ref_count references */ -static void _bio_set_references(struct bio *bio, unsigned int ref_count) -{ - atomic_set((atomic_t *)&bio->bi_next, ref_count); -} - -/* Use unutilized @bio->bi_next as endio reference counter */ -static void _bio_get(struct bio *bio) -{ - atomic_inc((atomic_t *)&bio->bi_next); -} - -/* Perform bio endio completion when unreferenced using bio clone's unutilized bi_next member */ -static void _bio_put(struct bio *bio) -{ - if (atomic_dec_and_test((atomic_t *)&bio->bi_next)) - bio_endio(bio); -} - -/* Set asynchronous memcpy bufio buffer release reference count */ -static void _buffer_get(struct dm_buffer *bp, int ref_count) -{ - struct async_c *ac = dm_bufio_get_aux_data(bp); - - atomic_set(&ac->buffer_refcount, ref_count); -} - -/* Ensure @bp doesn't get released too soon */ -static void _buffer_put(struct dm_buffer *bp) -{ - struct async_c *ac = dm_bufio_get_aux_data(bp); - - if (atomic_dec_and_test(&ac->buffer_refcount)) - dm_bufio_release(bp); -} - /* Flush any dirty buffers of @bc out */ static int _buffered_flush(struct buffered_c *bc) { return dm_bufio_write_dirty_buffers(bc->bufio) ?: dm_bufio_issue_flush(bc->bufio); } -/* async_memcpy() completion callback */ -static void _complete_memcpy(void *context) -{ - struct dm_buffer *bp = context; - struct async_c *ac = dm_bufio_get_aux_data(bp); - - _bio_put(ac->bio); - _buffer_put(bp); -} - -/* Initialize per bufio buffer async_memcpy() context */ -static struct async_submit_ctl *_init_async_memcpy(struct dm_buffer *bp, struct bio *bio) -{ - struct async_c *ac = dm_bufio_get_aux_data(bp); - - ac->bio = bio; - init_async_submit(&ac->submit, 0, NULL, _complete_memcpy, bp, NULL); - return &ac->submit; -} - /* Return total number of blocks for @ti */ static sector_t _buffered_size(struct dm_target *ti) { @@ -144,20 +78,14 @@ static sector_t _buffered_size(struct dm static void _memcpy(struct buffered_c *bc, struct dm_buffer *bp, struct page *dst, struct page *src, - loff_t dst_offset, loff_t src_offset, size_t len, - struct async_submit_ctl *ctl) + loff_t dst_offset, loff_t src_offset, size_t len) { - if (bc->async) { - async_memcpy(dst, src, dst_offset, src_offset, len, ctl); - } else { - void *d = kmap_local_page(dst); - void *s = kmap_local_page(src); - - memcpy(d + dst_offset, s + src_offset, len); - kunmap_local(d); - kunmap_local(s); - _complete_memcpy(bp); - } + void *d = kmap_local_page(dst); + void *s = kmap_local_page(src); + + memcpy(d + dst_offset, s + src_offset, len); + kunmap_local(d); + kunmap_local(s); } /* @@ -183,7 +111,6 @@ static void _io(struct buffered_c *bc, s * in case the segment is split across 2 buffers. */ if (len < total_len) { - _bio_get(bio); atomic_inc(&bc->stats[S_BUFFER_SPLITS]); } @@ -204,23 +131,14 @@ static void _io(struct buffered_c *bc, s if (!bio->bi_status) bio->bi_status = PTR_ERR(buffer); - _bio_put(bio); /* Continue with any split bio payload */ - } else if (write) { - /* - * Take out 2 references to be savely handling any single aysnchronous - * write copy to avoid race between copying the data and setting the - * partial buffer dirty otherwise leading to premature buffer releases. - */ /* (Superfluous) function consistency check example */ WARN_ON_ONCE(block != dm_bufio_get_block_number(bp)); /* Superfluous call to cover the API example */ buffer = dm_bufio_get_block_data(bp); - WARN_ON_ONCE(IS_ERR(buffer)); - _buffer_get(bp, 2); _memcpy(bc, bp, virt_to_page(buffer), bvec->bv_page, - buffer_offset, bvec_offset, len, _init_async_memcpy(bp, bio)); + buffer_offset, bvec_offset, len); /* Superfluous conditional to show both functions dirtying buffers. */ if (!buffer_offset && len == block_size) @@ -228,18 +146,13 @@ static void _io(struct buffered_c *bc, s else dm_bufio_mark_partial_buffer_dirty(bp, buffer_offset, buffer_offset + len); - - /* - * Now release the safeguard reference after - * having dirtied the (partial) buffer. - */ - _buffer_put(bp); + dm_bufio_release(bp); } else { /* (Superfluous) function consistency check example */ WARN_ON(block != dm_bufio_get_block_number(bp)); - _buffer_get(bp, 1); _memcpy(bc, bp, bvec->bv_page, virt_to_page(buffer), - bvec_offset, buffer_offset, len, _init_async_memcpy(bp, bio)); + bvec_offset, buffer_offset, len); + dm_bufio_release(bp); } /* Process any additional buffer even in case of I/O error */ @@ -321,12 +234,6 @@ static void __process_bio(struct buffere switch (bio_op(bio)) { case REQ_OP_READ: case REQ_OP_WRITE: - /* - * Take references per segment out and add an - * additional one to prevent an endio race. - */ - _bio_set_references(bio, bio_segments(bio) + 1); - if (bio->bi_opf & REQ_PREFLUSH) { /* Flush any writes out on request. */ atomic_inc(&bc->stats[S_PREFLUSHS]); bio->bi_status = errno_to_blk_status(_buffered_flush(bc)); @@ -353,27 +260,22 @@ static void __process_bio(struct buffere * Try forgetting buffers on discard (least we can do * with lag of discard passdown in dm-bufio). */ - _bio_set_references(bio, 1); if (bc->discard) _discard_blocks(bc, bio); break; case REQ_OP_WRITE_ZEROES: /* FIXME: not tested, as none came from upstack back in2 2019. */ - _bio_set_references(bio, 1); _write_zeroes(bc, bio); break; default: - _bio_set_references(bio, 1); /* Return error for unsupported operation */ bio->bi_status = errno_to_blk_status(-EOPNOTSUPP); } goto out; err: - /* On error, reset refecount to 1 for _bio_put() to endio */ - _bio_set_references(bio, 1); out: - _bio_put(bio); /* Release (additional) reference */ + bio_endio(bio); } /* Process I/O on a bio prefetching buffers on a single @bio in a worker. */ @@ -461,9 +363,6 @@ static void buffered_dtr(struct dm_targe * : full pathname of the buffered device * : offset in sectors into the device * [] : optional size of bufio buffers in bytes - * [] - * : select to (not) perform asynchronous memory copies - * between bvec pages and buffers * [] * : select to (not) perform discards * [] @@ -515,25 +414,11 @@ static int buffered_ctr(struct dm_target } if (argc > 3) { - bc->async_set = 1; - - if (!strcasecmp(argv[3], "no_async_memcpy")) { - bc->async = 0; - } else if (!strcasecmp(argv[3], "async_memcpy")) { - bc->async = 1; - } else { - ti->error = "Invalid async memcpy parameter"; - r = -EINVAL; - goto bad; - } - } - - if (argc > 4) { bc->discard_set = 1; - if (!strcasecmp(argv[4], "no_discard")) { + if (!strcasecmp(argv[3], "no_discard")) { bc->discard = 0; - } else if (!strcasecmp(argv[4], "discard")) { + } else if (!strcasecmp(argv[3], "discard")) { bc->discard = 1; } else { ti->error = "Invalid discard parameter"; @@ -542,12 +427,12 @@ static int buffered_ctr(struct dm_target } } - if (argc > 5) { + if (argc > 4) { bc->discard_passdown_set = 1; - if (!strcasecmp(argv[5], "no_discard_passdown")) { + if (!strcasecmp(argv[4], "no_discard_passdown")) { bc->discard_passdown = 0; - } else if (!strcasecmp(argv[5], "discard_passdown")) { + } else if (!strcasecmp(argv[4], "discard_passdown")) { bc->discard_passdown = 1; } else { ti->error = "Invalid discard passdown parameter"; @@ -556,12 +441,12 @@ static int buffered_ctr(struct dm_target } } - if (argc > 6) { + if (argc > 5) { bc->write_zeroes_set = 1; - if (!strcasecmp(argv[6], "no_write_zeroes")) { + if (!strcasecmp(argv[5], "no_write_zeroes")) { bc->write_zeroes = 0; - } else if (!strcasecmp(argv[6], "write_zeroes")) { + } else if (!strcasecmp(argv[5], "write_zeroes")) { bc->write_zeroes = 1; } else { ti->error = "Invalid write zeroes parameter"; @@ -591,7 +476,7 @@ static int buffered_ctr(struct dm_target bc->buffer_size = bc->buffer_size ?: DEFAULT_BUFFERED_BLOCK_SIZE; bc->bufio = dm_bufio_client_create(bc->dev->bdev, bc->buffer_size, - 1, sizeof(struct async_c), NULL, NULL, 0); + 1, 0, NULL, NULL, 0); if (IS_ERR(bc->bufio)) { ti->error = "Couldn't create bufio client"; r = PTR_ERR(bc->bufio); @@ -691,8 +576,6 @@ static void buffered_status(struct dm_ta DMEMIT(" -"); if (bc->buffer_size_set) DMEMIT(" %u", bc->buffer_size); - if (bc->async_set) - DMEMIT(" %s", bc->async ? "async_memcpy" : "no_async_memcpy"); if (bc->discard_set) DMEMIT(" %s", bc->discard ? "discard" : "no_discard"); if (bc->discard_passdown_set)