diff options
Diffstat (limited to 'fs/buffer.c')
| -rw-r--r-- | fs/buffer.c | 66 | 
1 files changed, 30 insertions, 36 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 9f6d2e41281..58e2e7b7737 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -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?   */  |