diff options
| author | Linus Torvalds <[email protected]> | 2012-08-25 11:36:43 -0700 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2012-08-25 11:36:43 -0700 | 
| commit | a7e546f175f07630453c44b5afe14dd667dcfec9 (patch) | |
| tree | 352c2577161f0cbe8c3b49bb6f053cfd49ed32b4 /fs/buffer.c | |
| parent | da31ce727e8cc6920de5840e35b4e770c08e86e3 (diff) | |
| parent | 676ce6d5ca3098339c028d44fe0427d1566a4d2d (diff) | |
Merge branch 'for-linus' of git://git.kernel.dk/linux-block
Pull block-related fixes from Jens Axboe:
 - Improvements to the buffered and direct write IO plugging from
   Fengguang.
 - Abstract out the mapping of a bio in a request, and use that to
   provide a blk_bio_map_sg() helper.  Useful for mapping just a bio
   instead of a full request.
 - Regression fix from Hugh, fixing up a patch that went into the
   previous release cycle (and marked stable, too) attempting to prevent
   a loop in __getblk_slow().
 - Updates to discard requests, fixing up the sizing and how we align
   them.  Also a change to disallow merging of discard requests, since
   that doesn't really work properly yet.
 - A few drbd fixes.
 - Documentation updates.
* 'for-linus' of git://git.kernel.dk/linux-block:
  block: replace __getblk_slow misfix by grow_dev_page fix
  drbd: Write all pages of the bitmap after an online resize
  drbd: Finish requests that completed while IO was frozen
  drbd: fix drbd wire compatibility for empty flushes
  Documentation: update tunable options in block/cfq-iosched.txt
  Documentation: update tunable options in block/cfq-iosched.txt
  Documentation: update missing index files in block/00-INDEX
  block: move down direct IO plugging
  block: remove plugging at buffered write time
  block: disable discard request merge temporarily
  bio: Fix potential memory leak in bio_find_or_create_slab()
  block: Don't use static to define "void *p" in show_partition_start()
  block: Add blk_bio_map_sg() helper
  block: Introduce __blk_segment_map_sg() helper
  fs/block-dev.c:fix performance regression in O_DIRECT writes to md block devices
  block: split discard into aligned requests
  block: reorganize rounding of max_discard_sectors
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 9f6d2e41281d..58e2e7b77372 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?   */  |