Commit e5174baa authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds

[PATCH] 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: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7c709d00
...@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) ...@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
return dclus; 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 super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb); struct msdos_sb_info *sbi = MSDOS_SB(sb);
...@@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) ...@@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
int cluster, offset; int cluster, offset;
*phys = 0; *phys = 0;
*mapped_blocks = 0;
if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) { 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; *phys = sector + sbi->dir_start;
*mapped_blocks = 1;
}
return 0; return 0;
} }
last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
...@@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) ...@@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
cluster = fat_bmap_cluster(inode, cluster); cluster = fat_bmap_cluster(inode, cluster);
if (cluster < 0) if (cluster < 0)
return cluster; return cluster;
else if (cluster) else if (cluster) {
*phys = fat_clus_to_blknr(sbi, cluster) + offset; *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; return 0;
} }
...@@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos, ...@@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos,
{ {
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
sector_t phys, iblock; sector_t phys, iblock;
int offset; unsigned long mapped_blocks;
int err; int err, offset;
next: next:
if (*bh) if (*bh)
...@@ -77,7 +77,7 @@ static int fat__get_entry(struct inode *dir, loff_t *pos, ...@@ -77,7 +77,7 @@ static int fat__get_entry(struct inode *dir, loff_t *pos,
*bh = NULL; *bh = NULL;
iblock = *pos >> sb->s_blocksize_bits; iblock = *pos >> sb->s_blocksize_bits;
err = fat_bmap(dir, iblock, &phys); err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
if (err || !phys) if (err || !phys)
return -1; /* beyond EOF or error */ return -1; /* beyond EOF or error */
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/uio.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#ifndef CONFIG_FAT_DEFAULT_IOCHARSET #ifndef CONFIG_FAT_DEFAULT_IOCHARSET
...@@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode *inode) ...@@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode *inode)
return err; return err;
} }
static int fat_get_block(struct inode *inode, sector_t iblock, static int __fat_get_blocks(struct inode *inode, sector_t iblock,
unsigned long *max_blocks,
struct buffer_head *bh_result, int create) struct buffer_head *bh_result, int create)
{ {
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
sector_t phys; 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) if (err)
return err; return err;
if (phys) { if (phys) {
map_bh(bh_result, sb, phys); map_bh(bh_result, sb, phys);
*max_blocks = min(mapped_blocks, *max_blocks);
return 0; return 0;
} }
if (!create) if (!create)
return 0; return 0;
if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
return -EIO; 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); err = fat_add_cluster(inode);
if (err) if (err)
return err; return err;
} }
MSDOS_I(inode)->mmu_private += sb->s_blocksize; /* available blocks on this cluster */
err = fat_bmap(inode, iblock, &phys); 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) if (err)
return err; return err;
if (!phys) BUG_ON(!phys);
BUG(); BUG_ON(*max_blocks != mapped_blocks);
set_buffer_new(bh_result); set_buffer_new(bh_result);
map_bh(bh_result, sb, phys); map_bh(bh_result, sb, phys);
return 0; 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) static int fat_writepage(struct page *page, struct writeback_control *wbc)
{ {
return block_write_full_page(page, fat_get_block, wbc); return block_write_full_page(page, fat_get_block, wbc);
...@@ -128,6 +163,34 @@ static int fat_commit_write(struct file *file, struct page *page, ...@@ -128,6 +163,34 @@ static int fat_commit_write(struct file *file, struct page *page,
return err; 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) static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
{ {
return generic_block_bmap(mapping, block, fat_get_block); return generic_block_bmap(mapping, block, fat_get_block);
...@@ -141,6 +204,7 @@ static struct address_space_operations fat_aops = { ...@@ -141,6 +204,7 @@ static struct address_space_operations fat_aops = {
.sync_page = block_sync_page, .sync_page = block_sync_page,
.prepare_write = fat_prepare_write, .prepare_write = fat_prepare_write,
.commit_write = fat_commit_write, .commit_write = fat_commit_write,
.direct_IO = fat_direct_IO,
.bmap = _fat_bmap .bmap = _fat_bmap
}; };
......
...@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len) ...@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
extern void fat_cache_inval_inode(struct inode *inode); extern void fat_cache_inval_inode(struct inode *inode);
extern int fat_get_cluster(struct inode *inode, int cluster, extern int fat_get_cluster(struct inode *inode, int cluster,
int *fclus, int *dclus); 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 */ /* fat/dir.c */
extern struct file_operations fat_dir_operations; extern struct file_operations fat_dir_operations;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment