Skip to contentSkip to conversation

fat: move fat_clusters_flush() to write_super()

PATCH In review OGAWA Hirofumi 2 participants 13 messages Nov 7, 2005 – Nov 8, 2005

Subsystems (4)
lore
Share & tools
lore

OGAWA Hirofumi

author maintainer
#0 lore raw

PATCH 1/6 fat: move fat_clusters_flush() to write_super()

It is overkill to update the FS_INFO whenever modifying prev_free/free_clusters, because those are just a hint.

So, this patch uses ->write_super() for updating FS_INFO instead.

---

 fs/fat/fatent.c |    8 ++++++--
 fs/fat/inode.c  |   10 ++++++++--
 fs/fat/misc.c   |    2 --
 3 files changed, 14 insertions(+), 6 deletions(-)
fs/fat/fatent.c renamed from fs/fat/fatent.c~fat_write_super +6 -2
diff -puN fs/fat/fatent.c~fat_write_super fs/fat/fatent.c
@@ -476,6 +476,7 @@ int fat_alloc_clusters(struct inode *ino
 				sbi->prev_free = entry;
 				if (sbi->free_clusters != -1)
 					sbi->free_clusters--;
+				sb->s_dirt = 1;
 
 				cluster[idx_clus] = entry;
 				idx_clus++;
@@ -496,6 +497,7 @@ int fat_alloc_clusters(struct inode *ino
 
 	/* Couldn't allocate the free entries */
 	sbi->free_clusters = 0;
+	sb->s_dirt = 1;
 	err = -ENOSPC;
 
 out:
@@ -509,7 +511,6 @@ out:
 	}
 	for (i = 0; i < nr_bhs; i++)
 		brelse(bhs[i]);
-	fat_clusters_flush(sb);
 
 	if (err && idx_clus)
 		fat_free_clusters(inode, cluster[0]);
@@ -542,8 +543,10 @@ int fat_free_clusters(struct inode *inod
 		}
 
 		ops->ent_put(&fatent, FAT_ENT_FREE);
-		if (sbi->free_clusters != -1)
+		if (sbi->free_clusters != -1) {
 			sbi->free_clusters++;
+			sb->s_dirt = 1;
+		}
 
 		if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
 			if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -605,6 +608,7 @@ int fat_count_free_clusters(struct super
 		} while (fat_ent_next(sbi, &fatent));
 	}
 	sbi->free_clusters = free;
+	sb->s_dirt = 1;
 	fatent_brelse(&fatent);
 out:
 	unlock_fat(sbi);
fs/fat/inode.c renamed from fs/fat/inode.c~fat_write_super +8 -2
diff -puN fs/fat/inode.c~fat_write_super fs/fat/inode.c
@@ -374,12 +374,17 @@ static void fat_clear_inode(struct inode
 	unlock_kernel();
 }
 
-static void fat_put_super(struct super_block *sb)
+static void fat_write_super(struct super_block *sb)
 {
-	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	sb->s_dirt = 0;
 
 	if (!(sb->s_flags & MS_RDONLY))
 		fat_clusters_flush(sb);
+}
+
+static void fat_put_super(struct super_block *sb)
+{
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
 	if (sbi->nls_disk) {
 		unload_nls(sbi->nls_disk);
@@ -546,6 +551,7 @@ static struct super_operations fat_sops 
 	.write_inode	= fat_write_inode,
 	.delete_inode	= fat_delete_inode,
 	.put_super	= fat_put_super,
+	.write_super	= fat_write_super,
 	.statfs		= fat_statfs,
 	.clear_inode	= fat_clear_inode,
 	.remount_fs	= fat_remount,
fs/fat/misc.c renamed from fs/fat/misc.c~fat_write_super +0 -2
diff -puN fs/fat/misc.c~fat_write_super fs/fat/misc.c
@@ -67,8 +67,6 @@ void fat_clusters_flush(struct super_blo
 		if (sbi->prev_free != -1)
 			fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
 		mark_buffer_dirty(bh);
-		if (sb->s_flags & MS_SYNCHRONOUS)
-			sync_dirty_buffer(bh);
 	}
 	brelse(bh);
 }

_

author maintainer
#1 lore raw

PATCH 1/1 fat: use sb_find_get_block() instead of sb_getblk()

We don't need to allocate buffer for checking the buffer is uptodate. This use sb_find_get_block() instead, and if it returns NULL it's not uptodate.

---

 fs/fat/dir.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/fat/dir.c renamed from fs/fat/dir.c~fat_dir_readahead-optimize +2 -2
diff -puN fs/fat/dir.c~fat_dir_readahead-optimize fs/fat/dir.c
@@ -45,8 +45,8 @@ static inline void fat_dir_readahead(str
 	if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO))
 		return;
 
-	bh = sb_getblk(sb, phys);
-	if (bh && !buffer_uptodate(bh)) {
+	bh = sb_find_get_block(sb, phys);
+	if (bh == NULL || !buffer_uptodate(bh)) {
 		for (sec = 0; sec < sbi->sec_per_clus; sec++)
 			sb_breadahead(sb, phys + sec);
 	}

_

author maintainer
#2 lore raw

PATCH 3/6 fat: add the read/writepages()

---

 fs/fat/inode.c |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)
fs/fat/inode.c renamed from fs/fat/inode.c~fat_read-writepages +16 -1
diff -puN fs/fat/inode.c~fat_read-writepages fs/fat/inode.c
@@ -18,6 +18,7 @@
 #include <linux/seq_file.h>
 #include <linux/msdos_fs.h>
 #include <linux/pagemap.h>
+#include <linux/mpage.h>
 #include <linux/buffer_head.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
@@ -90,9 +91,21 @@ static int fat_writepage(struct page *pa
 	return block_write_full_page(page, fat_get_block, wbc);
 }
 
+static int fat_writepages(struct address_space *mapping,
+			  struct writeback_control *wbc)
+{
+	return mpage_writepages(mapping, wbc, fat_get_block);
+}
+
 static int fat_readpage(struct file *file, struct page *page)
 {
-	return block_read_full_page(page, fat_get_block);
+	return mpage_readpage(page, fat_get_block);
+}
+
+static int fat_readpages(struct file *file, struct address_space *mapping,
+			 struct list_head *pages, unsigned nr_pages)
+{
+	return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
 }
 
 static int fat_prepare_write(struct file *file, struct page *page,
@@ -122,7 +135,9 @@ static sector_t _fat_bmap(struct address
 
 static struct address_space_operations fat_aops = {
 	.readpage	= fat_readpage,
+	.readpages	= fat_readpages,
 	.writepage	= fat_writepage,
+	.writepages	= fat_writepages,
 	.sync_page	= block_sync_page,
 	.prepare_write	= fat_prepare_write,
 	.commit_write	= fat_commit_write,

_

author maintainer
#3 lore raw

PATCH 4/6 fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/

All EXPORT_SYMBOL of fatfs is only for vfat/msdos. _GPL would be proper.

---

 fs/fat/dir.c    |   14 +++++++-------
 fs/fat/fatent.c |    2 +-
 fs/fat/file.c   |    2 +-
 fs/fat/inode.c  |   10 +++++-----
 fs/fat/misc.c   |    6 +++---
 5 files changed, 17 insertions(+), 17 deletions(-)
fs/fat/dir.c renamed from fs/fat/dir.c~fat_export_symbol_gpl +7 -7
diff -puN fs/fat/dir.c~fat_export_symbol_gpl fs/fat/dir.c
@@ -418,7 +418,7 @@ EODir:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_search_long);
+EXPORT_SYMBOL_GPL(fat_search_long);
 
 struct fat_ioctl_filldir_callback {
 	struct dirent __user *dirent;
@@ -780,7 +780,7 @@ int fat_get_dotdot_entry(struct inode *d
 	return -ENOENT;
 }
 
-EXPORT_SYMBOL(fat_get_dotdot_entry);
+EXPORT_SYMBOL_GPL(fat_get_dotdot_entry);
 
 /* See if directory is empty */
 int fat_dir_empty(struct inode *dir)
@@ -803,7 +803,7 @@ int fat_dir_empty(struct inode *dir)
 	return result;
 }
 
-EXPORT_SYMBOL(fat_dir_empty);
+EXPORT_SYMBOL_GPL(fat_dir_empty);
 
 /*
  * fat_subdirs counts the number of sub-directories of dir. It can be run
@@ -849,7 +849,7 @@ int fat_scan(struct inode *dir, const un
 	return -ENOENT;
 }
 
-EXPORT_SYMBOL(fat_scan);
+EXPORT_SYMBOL_GPL(fat_scan);
 
 static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
 {
@@ -936,7 +936,7 @@ int fat_remove_entries(struct inode *dir
 	return 0;
 }
 
-EXPORT_SYMBOL(fat_remove_entries);
+EXPORT_SYMBOL_GPL(fat_remove_entries);
 
 static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
 			      struct buffer_head **bhs, int nr_bhs)
@@ -1048,7 +1048,7 @@ error:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_alloc_new_dir);
+EXPORT_SYMBOL_GPL(fat_alloc_new_dir);
 
 static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
 			       int *nr_cluster, struct msdos_dir_entry **de,
@@ -1264,4 +1264,4 @@ error_remove:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_add_entries);
+EXPORT_SYMBOL_GPL(fat_add_entries);
fs/fat/fatent.c renamed from fs/fat/fatent.c~fat_export_symbol_gpl +1 -1
diff -puN fs/fat/fatent.c~fat_export_symbol_gpl fs/fat/fatent.c
@@ -581,7 +581,7 @@ error:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_free_clusters);
+EXPORT_SYMBOL_GPL(fat_free_clusters);
 
 int fat_count_free_clusters(struct super_block *sb)
 {
fs/fat/file.c renamed from fs/fat/file.c~fat_export_symbol_gpl +1 -1
diff -puN fs/fat/file.c~fat_export_symbol_gpl fs/fat/file.c
@@ -173,7 +173,7 @@ out:
 	return error;
 }
 
-EXPORT_SYMBOL(fat_notify_change);
+EXPORT_SYMBOL_GPL(fat_notify_change);
 
 /* Free all clusters after the skip'th cluster. */
 static int fat_free(struct inode *inode, int skip)
fs/fat/inode.c renamed from fs/fat/inode.c~fat_export_symbol_gpl +5 -5
diff -puN fs/fat/inode.c~fat_export_symbol_gpl fs/fat/inode.c
@@ -197,7 +197,7 @@ void fat_attach(struct inode *inode, lof
 	spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL_GPL(fat_attach);
 
 void fat_detach(struct inode *inode)
 {
@@ -208,7 +208,7 @@ void fat_detach(struct inode *inode)
 	spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL_GPL(fat_detach);
 
 struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
 {
@@ -362,7 +362,7 @@ out:
 	return inode;
 }
 
-EXPORT_SYMBOL(fat_build_inode);
+EXPORT_SYMBOL_GPL(fat_build_inode);
 
 static void fat_delete_inode(struct inode *inode)
 {
@@ -557,7 +557,7 @@ int fat_sync_inode(struct inode *inode)
 	return fat_write_inode(inode, 1);
 }
 
-EXPORT_SYMBOL(fat_sync_inode);
+EXPORT_SYMBOL_GPL(fat_sync_inode);
 
 static int fat_show_options(struct seq_file *m, struct vfsmount *mnt);
 static struct super_operations fat_sops = {
@@ -1365,7 +1365,7 @@ out_fail:
 	return error;
 }
 
-EXPORT_SYMBOL(fat_fill_super);
+EXPORT_SYMBOL_GPL(fat_fill_super);
 
 int __init fat_cache_init(void);
 void fat_cache_destroy(void);
fs/fat/misc.c renamed from fs/fat/misc.c~fat_export_symbol_gpl +3 -3
diff -puN fs/fat/misc.c~fat_export_symbol_gpl fs/fat/misc.c
@@ -33,7 +33,7 @@ void fat_fs_panic(struct super_block *s,
 	}
 }
 
-EXPORT_SYMBOL(fat_fs_panic);
+EXPORT_SYMBOL_GPL(fat_fs_panic);
 
 /* Flushes the number of free clusters on FAT32 */
 /* XXX: Need to write one per FSINFO block.  Currently only writes 1 */
@@ -192,7 +192,7 @@ void fat_date_unix2dos(int unix_date, __
 	*date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
 }
 
-EXPORT_SYMBOL(fat_date_unix2dos);
+EXPORT_SYMBOL_GPL(fat_date_unix2dos);
 
 int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
 {
@@ -220,4 +220,4 @@ int fat_sync_bhs(struct buffer_head **bh
 	return err;
 }
 
-EXPORT_SYMBOL(fat_sync_bhs);
+EXPORT_SYMBOL_GPL(fat_sync_bhs);

_

author maintainer
#4 lore raw

PATCH 5/6 fat: support ->direct_IO()

This patch add to support of ->direct_IO() for mostly read.

The user of this seems to want to use for streaming read. So, current direct I/O has limitation, it can only overwrite.
(For write operation, mainly we need to handle the hole etc..)

---

 fs/fat/cache.c           |   14 ++++++--
 fs/fat/dir.c             |    6 +--
 fs/fat/inode.c           |   82 +++++++++++++++++++++++++++++++++++++++++------
 include/linux/msdos_fs.h |    3 +
 4 files changed, 89 insertions(+), 16 deletions(-)
fs/fat/cache.c renamed from fs/fat/cache.c~fat_direct-io +11 -3
diff -puN fs/fat/cache.c~fat_direct-io fs/fat/cache.c
@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode
 	return dclus;
 }
 
-int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
+int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
+	     unsigned long *mapped_blocks)
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector
 	int cluster, offset;
 
 	*phys = 0;
+	*mapped_blocks = 0;
 	if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) {
-		if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits))
+		if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) {
 			*phys = sector + sbi->dir_start;
+			*mapped_blocks = 1;
+		}
 		return 0;
 	}
 	last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
@@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector
 	cluster = fat_bmap_cluster(inode, cluster);
 	if (cluster < 0)
 		return cluster;
-	else if (cluster)
+	else if (cluster) {
 		*phys = fat_clus_to_blknr(sbi, cluster) + offset;
+		*mapped_blocks = sbi->sec_per_clus - offset;
+		if (*mapped_blocks > last_block - sector)
+			*mapped_blocks = last_block - sector;
+	}
 	return 0;
 }
fs/fat/dir.c renamed from fs/fat/dir.c~fat_direct-io +3 -3
diff -puN fs/fat/dir.c~fat_direct-io fs/fat/dir.c
@@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *
 {
 	struct super_block *sb = dir->i_sb;
 	sector_t phys, iblock;
-	int offset;
-	int err;
+	unsigned long mapped_blocks;
+	int err, offset;
 
 next:
 	if (*bh)
@@ -77,7 +77,7 @@ next:
 
 	*bh = NULL;
 	iblock = *pos >> sb->s_blocksize_bits;
-	err = fat_bmap(dir, iblock, &phys);
+	err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
 	if (err || !phys)
 		return -1;	/* beyond EOF or error */
 
fs/fat/inode.c renamed from fs/fat/inode.c~fat_direct-io +73 -9

134 lines not on this page · raw message

include/linux/msdos_fs.h renamed from include/linux/msdos_fs.h~fat_direct-io +2 -1
diff -puN include/linux/msdos_fs.h~fat_direct-io include/linux/msdos_fs.h
@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *d
 extern void fat_cache_inval_inode(struct inode *inode);
 extern int fat_get_cluster(struct inode *inode, int cluster,
 			   int *fclus, int *dclus);
-extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys);
+extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
+		    unsigned long *mapped_blocks);
 
 /* fat/dir.c */
 extern struct file_operations fat_dir_operations;

_

author maintainer
#5 lore raw

PATCH 6/6 export/change sync_page_range/_nolock()

This exports/changes the sync_page_range/_nolock(). The fatfs needs sync_page_range/_nolock() for expanding truncate, and changes "size_t count" to "loff_t count".

---

 include/linux/writeback.h |    4 +++-
 mm/filemap.c              |    8 ++++----
 2 files changed, 7 insertions(+), 5 deletions(-)
include/linux/writeback.h renamed from include/linux/writeback.h~export-sync_page_range_nolock +3 -1
diff -puN include/linux/writeback.h~export-sync_page_range_nolock include/linux/writeback.h
@@ -108,7 +108,9 @@ void balance_dirty_pages_ratelimited(str
 int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0);
 int do_writepages(struct address_space *mapping, struct writeback_control *wbc);
 int sync_page_range(struct inode *inode, struct address_space *mapping,
-			loff_t pos, size_t count);
+			loff_t pos, loff_t count);
+int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
+			   loff_t pos, loff_t count);
 
 /* pdflush.c */
 extern int nr_pdflush_threads;	/* Global so it can be exported to sysctl
mm/filemap.c renamed from mm/filemap.c~export-sync_page_range_nolock +4 -4
diff -puN mm/filemap.c~export-sync_page_range_nolock mm/filemap.c
@@ -280,7 +280,7 @@ static int wait_on_page_writeback_range(
  * it is otherwise livelockable.
  */
 int sync_page_range(struct inode *inode, struct address_space *mapping,
-			loff_t pos, size_t count)
+			loff_t pos, loff_t count)
 {
 	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
 	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
@@ -305,9 +305,8 @@ EXPORT_SYMBOL(sync_page_range);
  * as it forces O_SYNC writers to different parts of the same file
  * to be serialised right until io completion.
  */
-static int sync_page_range_nolock(struct inode *inode,
-				  struct address_space *mapping,
-				  loff_t pos, size_t count)
+int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
+			   loff_t pos, loff_t count)
 {
 	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
 	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
@@ -322,6 +321,7 @@ static int sync_page_range_nolock(struct
 		ret = wait_on_page_writeback_range(mapping, start, end);
 	return ret;
 }
+EXPORT_SYMBOL(sync_page_range_nolock);
 
 /**
  * filemap_fdatawait - walk the list of under-writeback pages of the given

_

author maintainer
#6 lore raw

[PATCH 7/6] fat: Support a truncate() for expanding size

---

 fs/fat/file.c |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 3 deletions(-)
fs/fat/file.c renamed from fs/fat/file.c~fat_support-expand-truncate +64 -3
diff -puN fs/fat/file.c~fat_support-expand-truncate fs/fat/file.c
@@ -11,6 +11,7 @@
 #include <linux/msdos_fs.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
 		      unsigned int cmd, unsigned long arg)
@@ -124,6 +125,60 @@ struct file_operations fat_file_operatio
 	.sendfile	= generic_file_sendfile,
 };
 
+static int fat_cont_expand(struct inode *inode, loff_t size)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page;
+	unsigned long index, limit;
+	loff_t count, pos, start;
+	unsigned int from, to;
+	int err;
+
+	err = -EFBIG;
+	limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+	if (limit != RLIM_INFINITY && size > (loff_t)limit) {
+		send_sig(SIGXFSZ, current, 0);
+		goto out;
+	}
+	if (size > inode->i_sb->s_maxbytes)
+		goto out;
+
+	start = inode->i_size;
+	count = size - inode->i_size;
+	/* calculate the stuff of last page */
+	pos = size - 1;
+	index = pos >> PAGE_CACHE_SHIFT;
+	from = to = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
+
+	err = -ENOMEM;
+	page = grab_cache_page(mapping, index);
+	if (!page)
+		goto out;
+	err = mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (unlikely(err)) {
+		/*
+		 * ->prepare_write() may have instantiated a few blocks
+		 * outside i_size.  Trim these off again.
+		 */
+		unlock_page(page);
+		page_cache_release(page);
+		vmtruncate(inode, inode->i_size);
+		goto out;
+	}
+
+	err = mapping->a_ops->commit_write(NULL, page, from, to);
+
+	unlock_page(page);
+	page_cache_release(page);
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	if (!err && IS_SYNC(inode))
+		err = sync_page_range_nolock(inode, mapping, start, count);
+out:
+	return err;
+}
+
 int fat_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
@@ -132,11 +187,17 @@ int fat_notify_change(struct dentry *den
 
 	lock_kernel();
 
-	/* FAT cannot truncate to a longer file */
+	/*
+	 * Expand the file. Since inode_setattr() updates ->i_size
+	 * before calling the ->truncate(), but FAT needs to fill the
+	 * hole before it.
+	 */
 	if (attr->ia_valid & ATTR_SIZE) {
 		if (attr->ia_size > inode->i_size) {
-			error = -EPERM;
-			goto out;
+			error = fat_cont_expand(inode, attr->ia_size);
+			if (error || attr->ia_valid == ATTR_SIZE)
+				goto out;
+			attr->ia_valid &= ~ATTR_SIZE;
 		}
 	}
 

_

author maintainer
#7 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

Sorry for broken index.

[PATCH 1/7] fat: move fat_clusters_flush() to write_super() [PATCH 2/7] fat: use sb_find_get_block() instead of sb_getblk() [PATCH 3/7] fat: add the read/writepages()
[PATCH 4/7] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/ [PATCH 5/7] fat: support ->direct_IO()
[PATCH 6/7] export/change sync_page_range/_nolock() [PATCH 7/7] fat: Support a truncate() for expanding size

The above is right index. If I need to resend, please tell me.

Note for reviewers, [6/7] patch is not a part of fatfs.

Thanks.

Signature · 1 line
OGAWA Hirofumi <[email protected]>
#8 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

In reply to OGAWA Hirofumi
Quoted 2 lines · OGAWA Hirofumi

+static int fat_cont_expand(struct inode *inode, loff_t size)

Is it not possible to extend generic_cont_expand() so that fatfs can use it?

author maintainer
#9 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

Quoted 5 lines · Andrew Morton
Quoted 2 lines · OGAWA Hirofumi

+static int fat_cont_expand(struct inode *inode, loff_t size)

Is it not possible to extend generic_cont_expand() so that fatfs can use it?

The generic_cont_expand() is too generic.

If "size" is block boundary, generic_cont_expand() expands the ->i_size to "size + 1", after it, the caller of it will truncate to "size" by vmtruncate().

This sequence is not need if ->prepare_write() is cont_prepare_write(). The cont_prepare_write() will just fill the blocks with zero until "size" if blocks is not allocated yet.

FAT is using cont_parepare_write(), so for avoiding the above extra work, is using own version.

Probably, this version is generic only for cont_parepare_write().

Thanks.

Signature · 1 line
OGAWA Hirofumi <[email protected]>
#10 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

Quoted 10 lines · OGAWA Hirofumi
Quoted 5 lines · Andrew Morton
Quoted 2 lines · OGAWA Hirofumi

+static int fat_cont_expand(struct inode *inode, loff_t size)

Is it not possible to extend generic_cont_expand() so that fatfs can use it?

The generic_cont_expand() is too generic.

But can it be fixed??

Quoted 3 lines

If "size" is block boundary, generic_cont_expand() expands the ->i_size to "size + 1", after it, the caller of it will truncate to "size" by vmtruncate().

Something like this?

fs/buffer.c renamed from fs/buffer.c~a +12 -3
@@ -2160,7 +2160,7 @@ int block_read_full_page(struct page *pa
  * truncates.  Uses prepare/commit_write to allow the filesystem to
  * deal with the hole.  
  */
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size, int dont_do_that)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
@@ -2182,9 +2182,8 @@ int generic_cont_expand(struct inode *in
 	** skip the prepare.  make sure we never send an offset for the start
 	** of a block
 	*/
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+	if (!dont_do_that &&  (offset & (inode->i_sb->s_blocksize - 1)) == 0)
 		offset++;
-	}
 	index = size >> PAGE_CACHE_SHIFT;
 	err = -ENOMEM;
 	page = grab_cache_page(mapping, index);
@@ -2202,6 +2201,16 @@ out:
 	return err;
 }
 
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+	return __generic_cont_expand(inode, size, 0);
+}
+
+int generic_cont_expand_dont_do_that(struct inode *inode, loff_t size)
+{
+	return __generic_cont_expand(inode, size, 1);
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.

_

author maintainer
#11 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

Quoted 3 lines · Andrew Morton
Quoted 1 line

The generic_cont_expand() is too generic.

But can it be fixed??

Oh, probably we can...

Quoted 6 lines
Quoted 3 lines

If "size" is block boundary, generic_cont_expand() expands the ->i_size to "size + 1", after it, the caller of it will truncate to "size" by vmtruncate().

Something like this?

[...]

fs/buffer.c int generic_cont_expand(struct inode *in · line 2186

Quoted 12 lines
fs/buffer.c renamed from fs/buffer.c~a +2 -2
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size, int dont_do_that)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
@@ -2182,9 +2182,8 @@ int generic_cont_expand(struct inode *in
 	** skip the prepare.  make sure we never send an offset for the start
 	** of a block
 	*/
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+	if (!dont_do_that &&  (offset & (inode->i_sb->s_blocksize - 1)) == 0)
 		offset++;

Yes. But, if size is the page boundary, index is different.

Probably something like the below. And I'd like to do vmtruncate() if ->prepare_write() returns a error. The sync_page_range_nolock() can do by caller, so not necessary.

Hmm, I'll rethink this at tonight (10 hours later), the result may be same after all though.

Thanks.

if (!dont_do_that) {
	offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */

	/* ugh.  in prepare/commit_write, if from==to==start of block, we 
	** skip the prepare.  make sure we never send an offset for the start
	** of a block
	*/
	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
		offset++;
	}
	index = size >> PAGE_CACHE_SHIFT;
} else {
	/* calculate the stuff of last page */
	loff_t pos = size - 1;
	index = pos >> PAGE_CACHE_SHIFT;
	offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
}
Signature · 1 line
OGAWA Hirofumi <[email protected]>
author maintainer
#12 lore raw

Re: [PATCH 7/6] fat: Support a truncate() for expanding size

Quoted 15 lines · OGAWA Hirofumi

fs/buffer.c int generic_cont_expand(struct inode *in · line 2186

Quoted 12 lines
fs/buffer.c renamed from fs/buffer.c~a +2 -2
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size, int dont_do_that)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
@@ -2182,9 +2182,8 @@ int generic_cont_expand(struct inode *in
 	** skip the prepare.  make sure we never send an offset for the start
 	** of a block
 	*/
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+	if (!dont_do_that &&  (offset & (inode->i_sb->s_blocksize - 1)) == 0)
 		offset++;

Yes. But, if size is the page boundary, index is different.

Quoted 6 lines

Probably something like the below. And I'd like to do vmtruncate() if ->prepare_write() returns a error. The sync_page_range_nolock() can do by caller, so not necessary.

Hmm, I'll rethink this at tonight (10 hours later), the result may be same after all though.

How about this patch? If ok, please replace

fat-support-a-truncate-for-expanding-size.patch

with this.

Thanks.

Signature · 202 lines
OGAWA Hirofumi <[email protected]>



[PATCH] fat: Support a truncate() for expanding size (generic_cont_expand)

This patch changes generic_cont_expand(), in order to share the code
with fatfs.

  - Use vmtruncate() if ->prepare_write() returns a error.

Even if ->prepare_write() returns an error, it may already have added some
blocks. So, this truncates blocks outside of ->i_size by vmtruncate().

  - Add generic_cont_expand_simple().

The generic_cont_expand_simple() assumes that ->prepare_write() can handle
the block boundary. With this, we don't need to care the extra byte.


And for expanding a file size by truncate(), fatfs uses the
added generic_cont_expand_simple().

Signed-off-by: OGAWA Hirofumi <[email protected]>
---

 fs/buffer.c                 |   60 +++++++++++++++++++++++++++++++++-----------
 fs/fat/file.c               |   31 ++++++++++++++++++++--
 include/linux/buffer_head.h |    3 +-
 3 files changed, 76 insertions(+), 18 deletions(-)

diff -puN fs/fat/file.c~fat_support-expand-truncate fs/fat/file.c
--- linux-2.6.14/fs/fat/file.c~fat_support-expand-truncate	2005-11-07 23:03:02.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c	2005-11-09 04:00:53.000000000 +0900
@@ -11,6 +11,7 @@
 #include <linux/msdos_fs.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
 		      unsigned int cmd, unsigned long arg)
@@ -124,6 +125,24 @@ struct file_operations fat_file_operatio
 	.sendfile	= generic_file_sendfile,
 };
 
+static int fat_cont_expand(struct inode *inode, loff_t size)
+{
+	struct address_space *mapping = inode->i_mapping;
+	loff_t start = inode->i_size, count = size - inode->i_size;
+	int err;
+
+	err = generic_cont_expand_simple(inode, size);
+	if (err)
+		goto out;
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	if (IS_SYNC(inode))
+		err = sync_page_range_nolock(inode, mapping, start, count);
+out:
+	return err;
+}
+
 int fat_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
@@ -132,11 +151,17 @@ int fat_notify_change(struct dentry *den
 
 	lock_kernel();
 
-	/* FAT cannot truncate to a longer file */
+	/*
+	 * Expand the file. Since inode_setattr() updates ->i_size
+	 * before calling the ->truncate(), but FAT needs to fill the
+	 * hole before it.
+	 */
 	if (attr->ia_valid & ATTR_SIZE) {
 		if (attr->ia_size > inode->i_size) {
-			error = -EPERM;
-			goto out;
+			error = fat_cont_expand(inode, attr->ia_size);
+			if (error || attr->ia_valid == ATTR_SIZE)
+				goto out;
+			attr->ia_valid &= ~ATTR_SIZE;
 		}
 	}
 
diff -puN fs/buffer.c~fat_support-expand-truncate fs/buffer.c
--- linux-2.6.14/fs/buffer.c~fat_support-expand-truncate	2005-11-09 03:35:10.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/buffer.c	2005-11-09 04:33:29.000000000 +0900
@@ -2160,11 +2160,12 @@ int block_read_full_page(struct page *pa
  * truncates.  Uses prepare/commit_write to allow the filesystem to
  * deal with the hole.  
  */
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size,
+				 pgoff_t index, unsigned int offset)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
-	unsigned long index, offset, limit;
+	unsigned long limit;
 	int err;
 
 	err = -EFBIG;
@@ -2176,24 +2177,24 @@ int generic_cont_expand(struct inode *in
 	if (size > inode->i_sb->s_maxbytes)
 		goto out;
 
-	offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
-
-	/* ugh.  in prepare/commit_write, if from==to==start of block, we 
-	** skip the prepare.  make sure we never send an offset for the start
-	** of a block
-	*/
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
-		offset++;
-	}
-	index = size >> PAGE_CACHE_SHIFT;
 	err = -ENOMEM;
 	page = grab_cache_page(mapping, index);
 	if (!page)
 		goto out;
 	err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
-	if (!err) {
-		err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+	if (err) {
+		/*
+		 * ->prepare_write() may have instantiated a few blocks
+		 * outside i_size.  Trim these off again.
+		 */
+		unlock_page(page);
+		page_cache_release(page);
+		vmtruncate(inode, inode->i_size);
+		goto out;
 	}
+
+	err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+
 	unlock_page(page);
 	page_cache_release(page);
 	if (err > 0)
@@ -2202,6 +2203,36 @@ out:
 	return err;
 }
 
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+
+	offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
+
+	/* ugh.  in prepare/commit_write, if from==to==start of block, we
+	** skip the prepare.  make sure we never send an offset for the start
+	** of a block
+	*/
+	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+		/* caller must handle this extra byte. */
+		offset++;
+	}
+	index = size >> PAGE_CACHE_SHIFT;
+
+	return __generic_cont_expand(inode, size, index, offset);
+}
+
+int generic_cont_expand_simple(struct inode *inode, loff_t size)
+{
+	loff_t pos = size - 1;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
+
+	/* prepare/commit_write can handle even if from==to==start of block. */
+	return __generic_cont_expand(inode, size, index, offset);
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
@@ -3145,6 +3176,7 @@ EXPORT_SYMBOL(fsync_bdev);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_commit_write);
 EXPORT_SYMBOL(generic_cont_expand);
+EXPORT_SYMBOL(generic_cont_expand_simple);
 EXPORT_SYMBOL(init_buffer);
 EXPORT_SYMBOL(invalidate_bdev);
 EXPORT_SYMBOL(ll_rw_block);
diff -puN include/linux/buffer_head.h~fat_support-expand-truncate include/linux/buffer_head.h
--- linux-2.6.14/include/linux/buffer_head.h~fat_support-expand-truncate	2005-11-09 03:37:26.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/buffer_head.h	2005-11-09 03:37:36.000000000 +0900
@@ -197,7 +197,8 @@ int block_read_full_page(struct page*, g
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
 int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
 				loff_t *);
-int generic_cont_expand(struct inode *inode, loff_t size) ;
+int generic_cont_expand(struct inode *inode, loff_t size);
+int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
 int block_sync_page(struct page *);
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
_