diff -urN 2.3.15-pre2-pagecache/fs/buffer.c 2.3.15-pre2-pagecache-devel/fs/buffer.c --- 2.3.15-pre2-pagecache/fs/buffer.c Sun Aug 22 14:36:09 1999 +++ 2.3.15-pre2-pagecache-devel/fs/buffer.c Sun Aug 22 15:32:57 1999 @@ -793,6 +793,10 @@ repeat: bh = get_hash_table(dev, block, size); if (bh) { + /* we may have found a orhpaned-hashed buffer, and so it + may have a end_io handler different than the io_sync one. */ + bh->b_end_io = end_buffer_io_sync; + mb(); if (!buffer_dirty(bh)) { bh->b_flushtime = 0; } @@ -1492,8 +1496,14 @@ bh_old = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size); - if (!bh_old) + if (!bh_old || !buffer_uptodate(bh_old)) { + if (bh_old) + { + trash_buffer(bh_old); + __remove_from_queues(bh_old); + atomic_dec(&bh_old->b_count); + } ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); err = -EIO; @@ -1676,8 +1686,14 @@ bh_old = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size); - if (!bh_old) + if (!bh_old || !buffer_uptodate(bh_old)) { + if (bh_old) + { + trash_buffer(bh_old); + __remove_from_queues(bh_old); + atomic_dec(&bh_old->b_count); + } ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); err = -EIO; @@ -2118,22 +2134,46 @@ bh_old = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size); - if (bh_old) + if (bh_old && buffer_uptodate(bh_old)) { memcpy(bh->b_data, bh_old->b_data, bh->b_size); + set_bit(BH_Uptodate, &bh->b_state); /* move the protected bit from the old buffer to the new buffer. */ if (atomic_expose_buffer(bh_old)) atomic_protect_buffer(bh); if (atomic_set_buffer_clean(bh_old)) + { + /* the buffer will do IO + so it must have a valid + end_io handler before + setting it dirty. */ + bh->b_end_io = end_buffer_io_sync; + /* even if to refile the buffer + will run a spin_lock(), here + is more robust to enforce + a SMP memory barrier by hand + because a buffer is writable + only in function of the + dirty bit and not in + function of where it's + placed in the lru lists. */ + mb(); mark_buffer_dirty(bh, 0); + } /* now trash the old regular buffer */ trash_buffer(bh_old); __remove_from_queues(bh_old); atomic_dec(&bh_old->b_count); continue; + } + if (bh_old) + { + trash_buffer(bh_old); + __remove_from_queues(bh_old); + atomic_dec(&bh_old->b_count); } } }