|
@@ -914,7 +914,7 @@ link_dev_buffers(struct page *page, struct buffer_head *head)
|
|
|
/*
|
|
|
* Initialise the state of a blockdev page's buffers.
|
|
|
*/
|
|
|
-static void
|
|
|
+static sector_t
|
|
|
init_page_buffers(struct page *page, struct block_device *bdev,
|
|
|
sector_t block, int size)
|
|
|
{
|
|
@@ -936,33 +936,41 @@ init_page_buffers(struct page *page, struct block_device *bdev,
|
|
|
block++;
|
|
|
bh = bh->b_this_page;
|
|
|
} while (bh != head);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Caller needs to validate requested block against end of device.
|
|
|
+ */
|
|
|
+ return end_block;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Create the page-cache page that contains the requested block.
|
|
|
*
|
|
|
- * This is user purely for blockdev mappings.
|
|
|
+ * This is used purely for blockdev mappings.
|
|
|
*/
|
|
|
-static struct page *
|
|
|
+static int
|
|
|
grow_dev_page(struct block_device *bdev, sector_t block,
|
|
|
- pgoff_t index, int size)
|
|
|
+ pgoff_t index, int size, int sizebits)
|
|
|
{
|
|
|
struct inode *inode = bdev->bd_inode;
|
|
|
struct page *page;
|
|
|
struct buffer_head *bh;
|
|
|
+ sector_t end_block;
|
|
|
+ int ret = 0; /* Will call free_more_memory() */
|
|
|
|
|
|
page = find_or_create_page(inode->i_mapping, index,
|
|
|
(mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE);
|
|
|
if (!page)
|
|
|
- return NULL;
|
|
|
+ return ret;
|
|
|
|
|
|
BUG_ON(!PageLocked(page));
|
|
|
|
|
|
if (page_has_buffers(page)) {
|
|
|
bh = page_buffers(page);
|
|
|
if (bh->b_size == size) {
|
|
|
- init_page_buffers(page, bdev, block, size);
|
|
|
- return page;
|
|
|
+ end_block = init_page_buffers(page, bdev,
|
|
|
+ index << sizebits, size);
|
|
|
+ goto done;
|
|
|
}
|
|
|
if (!try_to_free_buffers(page))
|
|
|
goto failed;
|
|
@@ -982,14 +990,14 @@ grow_dev_page(struct block_device *bdev, sector_t block,
|
|
|
*/
|
|
|
spin_lock(&inode->i_mapping->private_lock);
|
|
|
link_dev_buffers(page, bh);
|
|
|
- init_page_buffers(page, bdev, block, size);
|
|
|
+ end_block = init_page_buffers(page, bdev, index << sizebits, size);
|
|
|
spin_unlock(&inode->i_mapping->private_lock);
|
|
|
- return page;
|
|
|
-
|
|
|
+done:
|
|
|
+ ret = (block < end_block) ? 1 : -ENXIO;
|
|
|
failed:
|
|
|
unlock_page(page);
|
|
|
page_cache_release(page);
|
|
|
- return NULL;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -999,7 +1007,6 @@ failed:
|
|
|
static int
|
|
|
grow_buffers(struct block_device *bdev, sector_t block, int size)
|
|
|
{
|
|
|
- struct page *page;
|
|
|
pgoff_t index;
|
|
|
int sizebits;
|
|
|
|
|
@@ -1023,22 +1030,14 @@ grow_buffers(struct block_device *bdev, sector_t block, int size)
|
|
|
bdevname(bdev, b));
|
|
|
return -EIO;
|
|
|
}
|
|
|
- block = index << sizebits;
|
|
|
+
|
|
|
/* Create a page with the proper size buffers.. */
|
|
|
- page = grow_dev_page(bdev, block, index, size);
|
|
|
- if (!page)
|
|
|
- return 0;
|
|
|
- unlock_page(page);
|
|
|
- page_cache_release(page);
|
|
|
- return 1;
|
|
|
+ return grow_dev_page(bdev, block, index, size, sizebits);
|
|
|
}
|
|
|
|
|
|
static struct buffer_head *
|
|
|
__getblk_slow(struct block_device *bdev, sector_t block, int size)
|
|
|
{
|
|
|
- int ret;
|
|
|
- struct buffer_head *bh;
|
|
|
-
|
|
|
/* Size must be multiple of hard sectorsize */
|
|
|
if (unlikely(size & (bdev_logical_block_size(bdev)-1) ||
|
|
|
(size < 512 || size > PAGE_SIZE))) {
|
|
@@ -1051,21 +1050,20 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-retry:
|
|
|
- bh = __find_get_block(bdev, block, size);
|
|
|
- if (bh)
|
|
|
- return bh;
|
|
|
+ for (;;) {
|
|
|
+ struct buffer_head *bh;
|
|
|
+ int ret;
|
|
|
|
|
|
- ret = grow_buffers(bdev, block, size);
|
|
|
- if (ret == 0) {
|
|
|
- free_more_memory();
|
|
|
- goto retry;
|
|
|
- } else if (ret > 0) {
|
|
|
bh = __find_get_block(bdev, block, size);
|
|
|
if (bh)
|
|
|
return bh;
|
|
|
+
|
|
|
+ ret = grow_buffers(bdev, block, size);
|
|
|
+ if (ret < 0)
|
|
|
+ return NULL;
|
|
|
+ if (ret == 0)
|
|
|
+ free_more_memory();
|
|
|
}
|
|
|
- return NULL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1321,10 +1319,6 @@ EXPORT_SYMBOL(__find_get_block);
|
|
|
* which corresponds to the passed block_device, block and size. The
|
|
|
* returned buffer has its reference count incremented.
|
|
|
*
|
|
|
- * __getblk() cannot fail - it just keeps trying. If you pass it an
|
|
|
- * illegal block number, __getblk() will happily return a buffer_head
|
|
|
- * which represents the non-existent block. Very weird.
|
|
|
- *
|
|
|
* __getblk() will lock up the machine if grow_dev_page's try_to_free_buffers()
|
|
|
* attempt is failing. FIXME, perhaps?
|
|
|
*/
|