set_blocksize: Allow to set a larger block size than PAGE_SIZE set_blocksize is changed to allow to specify a blocksize larger than a page. If that occurs then we switch the device to use compound pages. Signed-off-by: Christoph Lameter --- fs/block_dev.c | 86 +++++++++++++++++++++++++++++++++++++++++++----- fs/buffer.c | 2 - fs/inode.c | 3 + include/linux/pagemap.h | 17 +++++++++ 4 files changed, 98 insertions(+), 10 deletions(-) Index: linux-2.6.22-rc4-mm2/fs/block_dev.c =================================================================== --- linux-2.6.22-rc4-mm2.orig/fs/block_dev.c 2007-06-18 19:32:36.000000000 -0700 +++ linux-2.6.22-rc4-mm2/fs/block_dev.c 2007-06-18 20:09:55.000000000 -0700 @@ -65,24 +65,92 @@ static void kill_bdev(struct block_devic return; invalidate_bh_lrus(); truncate_inode_pages(bdev->bd_inode->i_mapping, 0); -} +} -int set_blocksize(struct block_device *bdev, int size) +#ifdef CONFIG_LARGE_BLOCKSIZE + +static int page_cache_min_order = 0; +static int page_cache_max_order = 4; /* 64k */ + +static int __init setup_page_cache_min_order(char *str) +{ + get_option (&str, &page_cache_min_order); + return 1; +} + +__setup("page_cache_min_order=", setup_page_cache_min_order); + +static int __init setup_page_cache_max_order(char *str) +{ + get_option (&str, &page_cache_max_order); + return 1; +} + +__setup("page_cache_max_order=", setup_page_cache_max_order); + +/* + * Convert a blkbits setting to the proper page cache order + */ +int page_cache_blkbits_to_order(int shift) +{ + int order; + + if (shift < 9) + return -EINVAL; + + if (shift > PAGE_SHIFT + page_cache_max_order) + return -EINVAL; + + order = shift - PAGE_SHIFT; + if (order < page_cache_min_order) + order = page_cache_min_order; + + /* + * If we would create too many buffers per page then + * we need to reduce the order + */ + while ((PAGE_SIZE << order) / size > MAX_BUF_PER_PAGE) + order--; + + return order; +} + +int page_cache_blocksize_to_order(int size) { - /* Size must be a power of two, and between 512 and PAGE_SIZE */ - if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size)) + if (!is_power_of_2(size)) return -EINVAL; + return blockbits_to_order(ilog2(size - 1) + 1); +} +#else + +#define page_cache_min_order 0 +#define page_cache_max_order 0 + +#endif + +int set_blocksize(struct block_device *bdev, int size) +{ + int order = page_cache_blocksize_to_order(size); + + if (order < 0) + return order; + /* Size cannot be smaller than the size supported by the device */ if (size < bdev_hardsect_size(bdev)) return -EINVAL; /* Don't change the size if it is same as current */ if (bdev->bd_block_size != size) { + int bits = blksize_bits(size); + struct address_space *mapping = + bdev->bd_inode->i_mapping; + sync_blockdev(bdev); - bdev->bd_block_size = size; - bdev->bd_inode->i_blkbits = blksize_bits(size); kill_bdev(bdev); + bdev->bd_block_size = size; + bdev->bd_inode->i_blkbits = bits; + mapping_setup(mapping, GFP_NOFS, order); } return 0; } @@ -93,8 +161,10 @@ int sb_set_blocksize(struct super_block { if (set_blocksize(sb->s_bdev, size)) return 0; - /* If we get here, we know size is power of two - * and it's value is between 512 and PAGE_SIZE */ + /* + * If we get here, we know size is power of two + * and it's value is valid for the page cache + */ sb->s_blocksize = size; sb->s_blocksize_bits = blksize_bits(size); return sb->s_blocksize; Index: linux-2.6.22-rc4-mm2/fs/buffer.c =================================================================== --- linux-2.6.22-rc4-mm2.orig/fs/buffer.c 2007-06-18 19:32:36.000000000 -0700 +++ linux-2.6.22-rc4-mm2/fs/buffer.c 2007-06-18 19:45:46.000000000 -0700 @@ -1097,7 +1097,7 @@ __getblk_slow(struct block_device *bdev, { /* Size must be multiple of hard sectorsize */ if (unlikely(size & (bdev_hardsect_size(bdev)-1) || - (size < 512 || size > PAGE_SIZE))) { + page_cache_blocksize_to_order(size) < 0 )) { printk(KERN_ERR "getblk(): invalid block size %d requested\n", size); printk(KERN_ERR "hardsect size: %d\n", Index: linux-2.6.22-rc4-mm2/fs/inode.c =================================================================== --- linux-2.6.22-rc4-mm2.orig/fs/inode.c 2007-06-18 19:32:36.000000000 -0700 +++ linux-2.6.22-rc4-mm2/fs/inode.c 2007-06-18 20:12:21.000000000 -0700 @@ -145,7 +145,8 @@ static struct inode *alloc_inode(struct mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags = 0; - mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE); + mapping_setup(mapping, GFP_HIGHUSER_PAGECACHE, + page_cache_blkbits_to_order(inode->i_blkbits)); mapping->assoc_mapping = NULL; mapping->backing_dev_info = &default_backing_dev_info; Index: linux-2.6.22-rc4-mm2/include/linux/pagemap.h =================================================================== --- linux-2.6.22-rc4-mm2.orig/include/linux/pagemap.h 2007-06-18 20:10:02.000000000 -0700 +++ linux-2.6.22-rc4-mm2/include/linux/pagemap.h 2007-06-18 20:14:06.000000000 -0700 @@ -86,6 +86,14 @@ static inline void mapping_setup(struct * with multiple page sizes. */ #ifdef CONFIG_LARGE_BLOCKSIZE +/* + * Determine page order from the blkbits in the inode structure + */ +extern int page_cache_blkbits_to_order(int shift); + +/* + * Determine page order from a given blocksize + */ extern int page_cache_blocksize_to_order(unsigned long size); static inline int mapping_order(struct address_space *a) @@ -117,6 +125,15 @@ static inline unsigned int page_cache_of /* * Kernel configured for a fixed PAGE_SIZEd page cache */ +static inline int page_cache_blkbits_to_order(int shift) +{ + if (shift < 9) + return -EINVAL; + if (shift > PAGE_SHIFT) + return -EINVAL; + return 0; +} + static inline int page_cache_blocksize_to_order(unsigned long size) { if (size >= 512 && size <= PAGE_SIZE)