mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
				synced 2025-11-04 07:44:51 +10:00 
			
		
		
		
	[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: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
		
							parent
							
								
									7c709d00d6
								
							
						
					
					
						commit
						e5174baaea
					
				@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
 | 
			
		||||
	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_t sector, sector_t *phys)
 | 
			
		||||
	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_t sector, sector_t *phys)
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos,
 | 
			
		||||
{
 | 
			
		||||
	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 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 *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 *file, struct page *page,
 | 
			
		||||
	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 fat_aops = {
 | 
			
		||||
	.sync_page	= block_sync_page,
 | 
			
		||||
	.prepare_write	= fat_prepare_write,
 | 
			
		||||
	.commit_write	= fat_commit_write,
 | 
			
		||||
	.direct_IO	= fat_direct_IO,
 | 
			
		||||
	.bmap		= _fat_bmap
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 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;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user