[PATCH] fat: Editions to support fat_fallocate()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux