There's a race: one CPU writes a 1k block into a ramdisk page which isn't in the blockdev pagecache yet. It memsets the locked page to zeroes. While this is happening, another CPU comes in and tries to write a different 1k block to the "disk". But it doesn't lock the page so it races with the memset and can have its data scribbled over. Fix this up by locking the page even if it already existed in pagecache. Locking a pagecache page in a make_request_fn sounds deadlocky but it is not, because: a) ramdisk_writepage() does nothing but a set_bit(), and cannot recut onto the same page. b) Any higher-level code which holds a page lock is supposed to be allocating its memory with GFP_NOFS, and in 2.6 kernels that's equivalent to GFP_NOIO. (The distinction between GFP_NOIO and GFP_NOFS basically disappeared with the buffer_head LRU, although it was reused for writes to swap). --- 25-akpm/drivers/block/rd.c | 30 ++++++++++-------------------- 1 files changed, 10 insertions(+), 20 deletions(-) diff -puN drivers/block/rd.c~ramdisk-lock-io-pages drivers/block/rd.c --- 25/drivers/block/rd.c~ramdisk-lock-io-pages 2004-05-18 02:29:35.354052736 -0700 +++ 25-akpm/drivers/block/rd.c 2004-05-18 02:30:10.593695496 -0700 @@ -176,33 +176,24 @@ static int rd_blkdev_pagecache_IO(int rw do { int count; - struct page * page; - char * src, * dst; - int unlock = 0; + struct page *page; + char *src; + char *dst; count = PAGE_CACHE_SIZE - offset; if (count > size) count = size; size -= count; - page = find_get_page(mapping, index); + page = grab_cache_page(mapping, index); if (!page) { - page = grab_cache_page(mapping, index); err = -ENOMEM; - if (!page) - goto out; - err = 0; - - if (!PageUptodate(page)) { - void *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr, 0, PAGE_CACHE_SIZE); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - SetPageUptodate(page); - } + goto out; + } - unlock = 1; + if (!PageUptodate(page)) { + clear_highpage(page); + SetPageUptodate(page); } index++; @@ -227,8 +218,7 @@ static int rd_blkdev_pagecache_IO(int rw } else { set_page_dirty(page); } - if (unlock) - unlock_page(page); + unlock_page(page); put_page(page); } while (size); _