fat: move fat_clusters_flush() to write_super()
Subsystems (4)
OGAWA Hirofumi
maintainerPATCH 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
@@ -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
@@ -374,12 +374,17 @@ static void fat_clear_inode(struct inodeunlock_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
@@ -67,8 +67,6 @@ void fat_clusters_flush(struct super_bloif (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); }
_
OGAWA Hirofumi
maintainerPATCH 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
@@ -45,8 +45,8 @@ static inline void fat_dir_readahead(strif ((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); }
_
OGAWA Hirofumi
maintainerPATCH 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
@@ -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 *pareturn 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 addressstatic 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,
_
OGAWA Hirofumi
maintainerPATCH 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
@@ -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 *dreturn -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 unreturn -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 *dirreturn 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
@@ -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
@@ -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
@@ -197,7 +197,7 @@ void fat_attach(struct inode *inode, lofspin_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
@@ -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 **bhreturn err; } -EXPORT_SYMBOL (fat_sync_bhs); +EXPORT_SYMBOL_GPL (fat_sync_bhs);
_
OGAWA Hirofumi
maintainerPATCH 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
@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inodereturn 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, sectorint 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
@@ -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
@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *dextern 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;
_
OGAWA Hirofumi
maintainerPATCH 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
@@ -108,7 +108,9 @@ void balance_dirty_pages_ratelimited(strint 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
@@ -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
_
OGAWA Hirofumi
maintainer[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
@@ -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 *denlock_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 ; } }
_
OGAWA Hirofumi
maintainerRe: [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]>
Re: [PATCH 7/6] fat: Support a truncate() for expanding size
In reply to OGAWA HirofumiQuoted 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?
OGAWA Hirofumi
maintainerRe: [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]>
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" byvmtruncate().
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.
_
OGAWA Hirofumi
maintainerRe: [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" byvmtruncate().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]>
OGAWA Hirofumi
maintainerRe: [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. Thesync_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 *); _