From: Steven Cavanagh <[EMAIL PROTECTED]> Added support for fallocate for a msdos fat driver. This allows preallocation of clusters to an inode before writes to reduce file fragmentation
Signed-off-by: Steven.Cavanagh <[EMAIL PROTECTED]> --- fs/fat/cache.c | 9 ++ fs/fat/file.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fat/inode.c | 21 ++++++ include/linux/msdos_fs.h | 5 + 4 files changed, 201 insertions(+), 1 deletions(-) diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 639b3b4..1a69ce4 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -8,6 +8,8 @@ * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. */ +#undef DEBUG + #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/buffer_head.h> @@ -316,6 +318,10 @@ int fat_bmap(struct inode *inode, sector cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); offset = sector & (sbi->sec_per_clus - 1); + + pr_debug("fat_bmap():cluster:%d, offset:%d, last_block:%llu\n", + cluster, offset, last_block); + cluster = fat_bmap_cluster(inode, cluster); if (cluster < 0) return cluster; @@ -324,6 +330,9 @@ int fat_bmap(struct inode *inode, sector *mapped_blocks = sbi->sec_per_clus - offset; if (*mapped_blocks > last_block - sector) *mapped_blocks = last_block - sector; + + pr_debug("fat_bmap():cluster:%d, phys:%llu\n", + cluster, *phys); } return 0; } diff --git a/fs/fat/file.c b/fs/fat/file.c index 69a83b5..9698d42 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -6,6 +6,8 @@ * regular file handling primitives for fat-based filesystems */ +#undef DEBUG + #include <linux/capability.h> #include <linux/module.h> #include <linux/time.h> @@ -15,6 +17,7 @@ #include <linux/buffer_head.h> #include <linux/writeback.h> #include <linux/backing-dev.h> #include <linux/blkdev.h> +#include <linux/falloc.h> int fat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -312,8 +315,172 @@ int fat_getattr(struct vfsmount *mnt, st } EXPORT_SYMBOL_GPL(fat_getattr); +/* + * preallocate space for a file. This implements fat fallocate inode + * operation, which gets called from sys_fallocate system call. User + * space requests len bytes at offset. + */ +long fat_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len) +{ + unsigned int blkbits = inode->i_blkbits; + int ret = 0, err; + unsigned long offset_block, new_blocks; + unsigned long max_blocks, nblocks = 0; + unsigned long mapped_blocks = 0, cluster_offset = 0; + + struct buffer_head bh; + + loff_t newsize = 0; + + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + sector_t phys; + struct timespec now; + + /* preallocation to directories is currently not supported */ + if (S_ISDIR(inode->i_mode)) { + printk(KERN_ERR + "fat_fallocate(): Directory prealloc not supported\n"); + return -ENODEV; + } + + offset_block = offset >> blkbits; + pr_debug("fat_fallocate:offset block:%lu\n", offset_block); + + /* Determine new allocation block */ + new_blocks = (MSDOS_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) + - offset_block; + pr_debug("fat_fallocate:allocate block:%lu\n", new_blocks); + + if ((offset_block + new_blocks) <= + (MSDOS_BLOCK_ALIGN(i_size_read(inode), blkbits) + >> blkbits)) { + printk(KERN_ERR + "fat_fallocate():Blocks already allocated\n"); + return -EIO; + } + if (offset_block > + (MSDOS_BLOCK_ALIGN(i_size_read(inode), blkbits) + >> blkbits)) { + printk(KERN_ERR + "fat_fallocate():Offset error\n"); + return -EIO; + } + while (ret >= 0 && nblocks < new_blocks) { + + /* Allocate a new cluster after all the available + * blocks(sectors) have been allocated. + */ + cluster_offset = + (unsigned long)offset_block & (sbi->sec_per_clus - 1); + if (!cluster_offset) { + + ret = fat_add_cluster(inode); + if (ret) { + pr_debug("msdos_fallocate():Add cluster err\ + inode#%lu, block = %lu, max_blocks = %lu\n", + inode->i_ino, offset_block, new_blocks); + break; + } + } + + /* mapped blocks = 4 blocks/sector - offset into cluster */ + mapped_blocks = sbi->sec_per_clus - cluster_offset; + pr_debug("msdos_fallocate():mapped_blocks:%lu\n", + mapped_blocks); + + /*mapped_blocks and dos_max_blocks should + *be the same because mapped_blocks used to + *be calculated in __fat_get_block() as + * sbi->sec_per_clus - offset(sector offset) + */ + max_blocks = min(mapped_blocks, (new_blocks-nblocks)); + + MSDOS_I(inode)->mmu_private += + max_blocks << sb->s_blocksize_bits; + + pr_debug("msdos_fallocate():MSDOS_I(inode)->mmu_private:%llu\n", + MSDOS_I(inode)->mmu_private); + + pr_debug("msdos_fallocate():mapped_blocks:%lu,max_blocks:%lu\n", + mapped_blocks, max_blocks); + + err = fat_bmap(inode, offset_block, &phys, &mapped_blocks); + if (err) { + printk(KERN_ERR + "msdos_fallocate():fat_bmap() error:%d\n", err); + return err; + } + pr_debug("msdos_fallocate():sector: %lu, phys: %llu\n", + offset_block, phys); + + if (!phys) { + printk(KERN_ERR + "msdos_fallocate():Bad disk sector number\n"); + return -EIO; + } + if (!mapped_blocks) { + printk(KERN_ERR + "msdos_fallocate():Block mapping error\n"); + return -EIO; + } + set_buffer_new(&bh); + map_bh(&bh, sb, phys); + pr_debug("msdos_fallocate():b_size:%d, b_blocknr:%llu\n", + bh.b_size, bh.b_blocknr); + + now = current_fs_time(inode->i_sb); + if (!timespec_equal(&inode->i_ctime, &now)) + inode->i_ctime = now; + + /*Increment the cluster block count*/ + nblocks += mapped_blocks; + offset_block += mapped_blocks; + } + + pr_debug("msdos_fallocate():fat_bmap():nblocks:%lu\n", + nblocks); + mark_inode_dirty(inode); + + /* + * Time to update the file size. + * Update only when preallocation was requested beyond the file size. + */ + if (!(mode & FALLOC_FL_KEEP_SIZE) && + (offset + len) > i_size_read(inode)) { + if (!ret) { + /* + * if no error, we assume preallocation succeeded + * completely + */ + mutex_lock(&inode->i_mutex); + i_size_write(inode, offset + len); + mutex_unlock(&inode->i_mutex); + pr_debug("msdos_fallocate():INODE SIZE:%llu\n", + i_size_read(inode)); + + } else if (ret && nblocks) { + + printk(KERN_ERR + "msdos_fallocate(): Errors, updating inode size\n"); + /* Handle partial allocation scenario */ + mutex_lock(&inode->i_mutex); + newsize = (nblocks << blkbits) + i_size_read(inode); + i_size_write(inode, + MSDOS_BLOCK_ALIGN(newsize, blkbits)); + mutex_unlock(&inode->i_mutex); + pr_debug("msdos_fallocate():INODE SIZE:%llu\n", + i_size_read(inode)); + } + } + + return ret; + +} + const struct inode_operations fat_file_inode_operations = { .truncate = fat_truncate, .setattr = fat_notify_change, .getattr = fat_getattr, + .fallocate = fat_fallocate, }; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 920a576..463ead5 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -10,6 +10,8 @@ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 */ +#undef DEBUG + #include <linux/module.h> #include <linux/init.h> #include <linux/time.h> @@ -38,7 +40,7 @@ static int fat_default_codepage = CONFIG static char fat_default_iocharset[] = CONFIG_FAT_DEFAULT_IOCHARSET; -static int fat_add_cluster(struct inode *inode) +int fat_add_cluster(struct inode *inode) { int err, cluster; @@ -64,6 +66,11 @@ static inline int __fat_get_block(struct int err, offset; err = fat_bmap(inode, iblock, &phys, &mapped_blocks); + + pr_debug( + "__fat_get_block():phys: %llu, mapped_blocks:%lu,sector: %llu\n", + phys, mapped_blocks, iblock); + if (err) return err; if (phys) { @@ -90,10 +97,18 @@ static inline int __fat_get_block(struct /* available blocks on this cluster */ mapped_blocks = sbi->sec_per_clus - offset; + pr_debug( + "__fat_get_block(): max_blocks: %lu, mapped_blocks:%lu\n", + *max_blocks, mapped_blocks); + *max_blocks = min(mapped_blocks, *max_blocks); MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; err = fat_bmap(inode, iblock, &phys, &mapped_blocks); + pr_debug( + "__fat_get_block():phys: %llu, mapped_blocks:%lu,sector: %llu\n", + phys, mapped_blocks, iblock); + if (err) return err; @@ -116,6 +131,10 @@ static int fat_get_block(struct inode *i if (err) return err; bh_result->b_size = max_blocks << sb->s_blocksize_bits; + + printk(KERN_INFO "fat_get_block():bh_result->b_size:%d\n", + bh_result->b_size); + return 0; } diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index f950921..034a2a4 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -109,6 +109,8 @@ #define VFAT_SFN_DISPLAY_WINNT 0x0004 /* #define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ #define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ +#define MSDOS_BLOCK_ALIGN(size, blkbits) ALIGN((size), (1 << (blkbits))) + struct fat_boot_sector { __u8 ignored[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case @@ -350,6 +352,9 @@ extern int fat_add_entries(struct inode struct fat_slot_info *sinfo); extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo); +/* fat/inode.c */ +extern int fat_add_cluster(struct inode *inode); + /* fat/fatent.c */ struct fat_entry { int entry; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/