2005-11-07 17:32:47

by OGAWA Hirofumi

[permalink] [raw]
Subject: [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.

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

fs/fat/fatent.c | 8 ++++++--
fs/fat/inode.c | 10 ++++++++--
fs/fat/misc.c | 2 --
3 files changed, 14 insertions(+), 6 deletions(-)

diff -puN fs/fat/fatent.c~fat_write_super fs/fat/fatent.c
--- linux-2.6.14/fs/fat/fatent.c~fat_write_super 2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/fatent.c 2005-11-07 03:46:35.000000000 +0900
@@ -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);
diff -puN fs/fat/inode.c~fat_write_super fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_write_super 2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c 2005-11-07 03:55:16.000000000 +0900
@@ -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,
diff -puN fs/fat/misc.c~fat_write_super fs/fat/misc.c
--- linux-2.6.14/fs/fat/misc.c~fat_write_super 2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/misc.c 2005-11-07 03:46:35.000000000 +0900
@@ -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);
}
_


2005-11-07 17:37:58

by OGAWA Hirofumi

[permalink] [raw]
Subject: [PATCH 3/6] fat: add the read/writepages()


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

fs/fat/inode.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

diff -puN fs/fat/inode.c~fat_read-writepages fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_read-writepages 2005-11-07 03:58:49.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c 2005-11-07 03:58:49.000000000 +0900
@@ -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,
_

2005-11-07 17:39:43

by OGAWA Hirofumi

[permalink] [raw]
Subject: [PATCH 4/6] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/

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

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

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(-)

diff -puN fs/fat/dir.c~fat_export_symbol_gpl fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_export_symbol_gpl 2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c 2005-11-07 03:58:53.000000000 +0900
@@ -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);
diff -puN fs/fat/fatent.c~fat_export_symbol_gpl fs/fat/fatent.c
--- linux-2.6.14/fs/fat/fatent.c~fat_export_symbol_gpl 2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/fatent.c 2005-11-07 03:58:53.000000000 +0900
@@ -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)
{
diff -puN fs/fat/file.c~fat_export_symbol_gpl fs/fat/file.c
--- linux-2.6.14/fs/fat/file.c~fat_export_symbol_gpl 2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c 2005-11-07 03:58:53.000000000 +0900
@@ -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)
diff -puN fs/fat/inode.c~fat_export_symbol_gpl fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_export_symbol_gpl 2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c 2005-11-07 03:58:53.000000000 +0900
@@ -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);
diff -puN fs/fat/misc.c~fat_export_symbol_gpl fs/fat/misc.c
--- linux-2.6.14/fs/fat/misc.c~fat_export_symbol_gpl 2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/misc.c 2005-11-07 03:58:53.000000000 +0900
@@ -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);
_

2005-11-07 17:40:11

by OGAWA Hirofumi

[permalink] [raw]
Subject: [PATCH] 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.

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

fs/fat/dir.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff -puN fs/fat/dir.c~fat_dir_readahead-optimize fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_dir_readahead-optimize 2005-11-07 03:58:44.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c 2005-11-07 03:58:44.000000000 +0900
@@ -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);
}
_

2005-11-07 17:41:49

by OGAWA Hirofumi

[permalink] [raw]
Subject: [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..)

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

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(-)

diff -puN fs/fat/cache.c~fat_direct-io fs/fat/cache.c
--- linux-2.6.14/fs/fat/cache.c~fat_direct-io 2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/cache.c 2005-11-07 03:58:58.000000000 +0900
@@ -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;
}
diff -puN fs/fat/dir.c~fat_direct-io fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_direct-io 2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c 2005-11-07 03:58:58.000000000 +0900
@@ -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 */

diff -puN fs/fat/inode.c~fat_direct-io fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_direct-io 2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c 2005-11-07 03:58:58.000000000 +0900
@@ -23,6 +23,7 @@
#include <linux/mount.h>
#include <linux/vfs.h>
#include <linux/parser.h>
+#include <linux/uio.h>
#include <asm/unaligned.h>

#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
@@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode
return err;
}

-static int fat_get_block(struct inode *inode, sector_t iblock,
- struct buffer_head *bh_result, int create)
+static int __fat_get_blocks(struct inode *inode, sector_t iblock,
+ unsigned long *max_blocks,
+ struct buffer_head *bh_result, int create)
{
struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
sector_t phys;
- int err;
+ unsigned long mapped_blocks;
+ int err, offset;

- err = fat_bmap(inode, iblock, &phys);
+ err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
if (err)
return err;
if (phys) {
map_bh(bh_result, sb, phys);
+ *max_blocks = min(mapped_blocks, *max_blocks);
return 0;
}
if (!create)
return 0;
+
if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
return -EIO;
}
- if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) {
+
+ offset = (unsigned long)iblock & (sbi->sec_per_clus - 1);
+ if (!offset) {
+ /* TODO: multiple cluster allocation would be desirable. */
err = fat_add_cluster(inode);
if (err)
return err;
}
- MSDOS_I(inode)->mmu_private += sb->s_blocksize;
- err = fat_bmap(inode, iblock, &phys);
+ /* available blocks on this cluster */
+ mapped_blocks = sbi->sec_per_clus - offset;
+
+ *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);
if (err)
return err;
- if (!phys)
- BUG();
+ BUG_ON(!phys);
+ BUG_ON(*max_blocks != mapped_blocks);
set_buffer_new(bh_result);
map_bh(bh_result, sb, phys);
return 0;
}

+static int fat_get_blocks(struct inode *inode, sector_t iblock,
+ unsigned long max_blocks,
+ struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ int err;
+
+ err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+ if (err)
+ return err;
+ bh_result->b_size = max_blocks << sb->s_blocksize_bits;
+ return 0;
+}
+
+static int fat_get_block(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create)
+{
+ unsigned long max_blocks = 1;
+ return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+}
+
static int fat_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page, fat_get_block, wbc);
@@ -128,6 +163,34 @@ static int fat_commit_write(struct file
return err;
}

+static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
+ const struct iovec *iov,
+ loff_t offset, unsigned long nr_segs)
+{
+ struct file *file = iocb->ki_filp;
+ struct inode *inode = file->f_mapping->host;
+
+ if (rw == WRITE) {
+ /*
+ * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(),
+ * so we need to update the ->mmu_private to block boundary.
+ *
+ * But we must fill the remaining area or hole by nul for
+ * updating ->mmu_private.
+ */
+ loff_t size = offset + iov_length(iov, nr_segs);
+ if (MSDOS_I(inode)->mmu_private < size)
+ return -EINVAL;
+ }
+
+ /*
+ * FAT need to use the DIO_LOCKING for avoiding the race
+ * condition of fat_get_block() and ->truncate().
+ */
+ return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+ offset, nr_segs, fat_get_blocks, NULL);
+}
+
static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping, block, fat_get_block);
@@ -141,6 +204,7 @@ static struct address_space_operations f
.sync_page = block_sync_page,
.prepare_write = fat_prepare_write,
.commit_write = fat_commit_write,
+ .direct_IO = fat_direct_IO,
.bmap = _fat_bmap
};

diff -puN include/linux/msdos_fs.h~fat_direct-io include/linux/msdos_fs.h
--- linux-2.6.14/include/linux/msdos_fs.h~fat_direct-io 2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/msdos_fs.h 2005-11-07 03:58:58.000000000 +0900
@@ -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;
_

2005-11-07 17:42:46

by OGAWA Hirofumi

[permalink] [raw]
Subject: [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".

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

include/linux/writeback.h | 4 +++-
mm/filemap.c | 8 ++++----
2 files changed, 7 insertions(+), 5 deletions(-)

diff -puN include/linux/writeback.h~export-sync_page_range_nolock include/linux/writeback.h
--- linux-2.6.14/include/linux/writeback.h~export-sync_page_range_nolock 2005-11-07 03:59:03.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/writeback.h 2005-11-07 03:59:03.000000000 +0900
@@ -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
diff -puN mm/filemap.c~export-sync_page_range_nolock mm/filemap.c
--- linux-2.6.14/mm/filemap.c~export-sync_page_range_nolock 2005-11-07 03:59:03.000000000 +0900
+++ linux-2.6.14-hirofumi/mm/filemap.c 2005-11-07 03:59:03.000000000 +0900
@@ -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
_

2005-11-07 17:46:20

by OGAWA Hirofumi

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


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

fs/fat/file.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 64 insertions(+), 3 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 03:59:07.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c 2005-11-07 03:59:07.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,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;
}
}

_

2005-11-07 17:51:44

by OGAWA Hirofumi

[permalink] [raw]
Subject: 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.
--
OGAWA Hirofumi <[email protected]>

2005-11-08 01:06:09

by Andrew Morton

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

OGAWA Hirofumi <[email protected]> wrote:
>
> +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?

2005-11-08 03:20:09

by OGAWA Hirofumi

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

Andrew Morton <[email protected]> writes:

> OGAWA Hirofumi <[email protected]> wrote:
>>
>> +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.
--
OGAWA Hirofumi <[email protected]>

2005-11-08 04:19:48

by Andrew Morton

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

OGAWA Hirofumi <[email protected]> wrote:
>
> Andrew Morton <[email protected]> writes:
>
> > OGAWA Hirofumi <[email protected]> wrote:
> >>
> >> +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??

> 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?

--- devel/fs/buffer.c~a 2005-11-07 20:17:49.000000000 -0800
+++ devel-akpm/fs/buffer.c 2005-11-07 20:18:59.000000000 -0800
@@ -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.
_

2005-11-08 05:22:54

by OGAWA Hirofumi

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

Andrew Morton <[email protected]> writes:

>> The generic_cont_expand() is too generic.
>
> But can it be fixed??

Oh, probably we can...

>> 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?
>

[...]

> -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;
}
--
OGAWA Hirofumi <[email protected]>

2005-11-08 20:40:45

by OGAWA Hirofumi

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

OGAWA Hirofumi <[email protected]> writes:

>> -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.

How about this patch? If ok, please replace

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

with this.

Thanks.
--
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 *);
_