diff -urN linux-2.4.4.tmp/drivers/block/loop.c linux-2.4.4.SuSE/drivers/block/loop.c --- linux-2.4.4.tmp/drivers/block/loop.c Thu Apr 12 04:05:14 2001 +++ linux-2.4.4.SuSE/drivers/block/loop.c Tue May 8 16:29:06 2001 @@ -86,10 +86,12 @@ static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf, char *loop_buf, int size, int real_block) { - if (cmd == READ) - memcpy(loop_buf, raw_buf, size); - else - memcpy(raw_buf, loop_buf, size); + if (raw_buf != loop_buf) { + if (cmd == READ) + memcpy(loop_buf, raw_buf, size); + else + memcpy(raw_buf, loop_buf, size); + } return 0; } @@ -117,6 +119,7 @@ static int none_status(struct loop_device *lo, struct loop_info *info) { + lo->lo_flags |= LO_FLAGS_BH_REMAP; return 0; } @@ -313,9 +316,13 @@ return ret; } +static void loop_end_io_transfer(struct buffer_head *bh, int uptodate); static void loop_put_buffer(struct buffer_head *bh) { - if (bh) { + /* + * check b_end_io, may just be a remapped bh and not an allocated one + */ + if (bh && bh->b_end_io == loop_end_io_transfer) { __free_page(bh->b_page); kmem_cache_free(bh_cachep, bh); } @@ -385,6 +392,14 @@ { struct buffer_head *bh; + /* + * for xfer_funcs that can operate on the same bh, do that + */ + if (lo->lo_flags & LO_FLAGS_BH_REMAP) { + bh = rbh; + goto out_bh; + } + do { bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER); if (bh) @@ -397,9 +412,6 @@ bh->b_size = rbh->b_size; bh->b_dev = rbh->b_rdev; - spin_lock_irq(&lo->lo_lock); - bh->b_rdev = lo->lo_device; - spin_unlock_irq(&lo->lo_lock); bh->b_state = (1 << BH_Req) | (1 << BH_Mapped) | (1 << BH_Lock); /* @@ -418,9 +430,15 @@ bh->b_data = page_address(bh->b_page); bh->b_end_io = loop_end_io_transfer; - bh->b_rsector = rbh->b_rsector + (lo->lo_offset >> 9); + bh->b_private = rbh; init_waitqueue_head(&bh->b_wait); +out_bh: + bh->b_rsector = rbh->b_rsector + (lo->lo_offset >> 9); + spin_lock_irq(&lo->lo_lock); + bh->b_rdev = lo->lo_device; + spin_unlock_irq(&lo->lo_lock); + return bh; } @@ -475,8 +493,7 @@ * piggy old buffer on original, and submit for I/O */ bh = loop_get_buffer(lo, rbh); - bh->b_private = rbh; - IV = loop_get_iv(lo, bh->b_rsector); + IV = loop_get_iv(lo, rbh->b_rsector); if (rw == WRITE) { set_bit(BH_Dirty, &bh->b_state); if (lo_do_transfer(lo, WRITE, bh->b_data, rbh->b_data, @@ -600,7 +617,7 @@ error = -EBUSY; if (lo->lo_state != Lo_unbound) goto out; - + error = -EBADF; file = fget(arg); if (!file) @@ -620,7 +637,6 @@ * If we can't read - sorry. If we only can't write - well, * it's going to be read-only. */ - error = -EINVAL; if (!aops->readpage) goto out_putf; diff -urN linux-2.4.4.tmp/include/linux/loop.h linux-2.4.4.SuSE/include/linux/loop.h --- linux-2.4.4.tmp/include/linux/loop.h Wed Mar 7 04:35:36 2001 +++ linux-2.4.4.SuSE/include/linux/loop.h Tue May 8 16:29:06 2001 @@ -77,6 +77,7 @@ */ #define LO_FLAGS_DO_BMAP 1 #define LO_FLAGS_READ_ONLY 2 +#define LO_FLAGS_BH_REMAP 4 /* * Note that this structure gets the wrong offsets when directly used