Commit f8cc9647 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ramdisk: lock blockdev pages during "IO".

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 recur 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).
parent 5be8e6a2
...@@ -176,33 +176,24 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector, ...@@ -176,33 +176,24 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector,
do { do {
int count; int count;
struct page * page; struct page *page;
char * src, * dst; char *src;
int unlock = 0; char *dst;
count = PAGE_CACHE_SIZE - offset; count = PAGE_CACHE_SIZE - offset;
if (count > size) if (count > size)
count = size; count = size;
size -= count; size -= count;
page = find_get_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) { if (!page) {
page = grab_cache_page(mapping, index);
err = -ENOMEM; err = -ENOMEM;
if (!page) goto out;
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);
}
unlock = 1; if (!PageUptodate(page)) {
clear_highpage(page);
SetPageUptodate(page);
} }
index++; index++;
...@@ -227,8 +218,7 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector, ...@@ -227,8 +218,7 @@ static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector,
} else { } else {
set_page_dirty(page); set_page_dirty(page);
} }
if (unlock) unlock_page(page);
unlock_page(page);
put_page(page); put_page(page);
} while (size); } while (size);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment