pramfs: file operations
PATCH 4/17 pramfs: file operations
From: Marco Stornelli <[email protected]>
File operations.
Signed-off-by Marco Stornelli <[email protected]>
---
fs/pramfs/file.c new file +326 -0
@@ -0,0 +1,326 @@ +/* +* BRIEF DESCRIPTION +* +* File operations for files. +* +* Copyright 2009-2010 Marco Stornelli <[email protected]> +* Copyright 2003 Sony Corporation +* Copyright 2003 Matsushita Electric Industrial Co., Ltd. +* 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam +* This file is licensed under the terms of the GNU General Public +* License version 2. This program is licensed "as is" without any +* warranty of any kind, whether express or implied. +*/ + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/uio.h> +#include <linux/mm.h> +#include <linux/uaccess.h> +#include "pram.h" +#include "acl.h" +#include "xip.h" +#include "xattr.h" + +/* +* The following functions are helper routines to copy to/from +* user space and iter over io vectors (mainly for readv/writev). +* They are used in the direct IO path. +*/ +static size_t __pram_iov_copy_from (char *vaddr, +const struct iovec *iov,size_t base,size_t bytes) +{ +size_t copied =0 , left =0 ; + +while (bytes) { +char __user *buf = iov->iov_base + base; +int copy =min (bytes, iov->iov_len - base); + + base =0 ; + left =__copy_from_user (vaddr, buf, copy); + copied += copy; + bytes -= copy; + vaddr += copy; + iov++; + +if (unlikely (left)) +break ; + } +return copied - left; +} + +static size_t __pram_iov_copy_to (char *vaddr, +const struct iovec *iov,size_t base,size_t bytes) +{ +size_t copied =0 , left =0 ; + +while (bytes) { +char __user *buf = iov->iov_base + base; +int copy =min (bytes, iov->iov_len - base); + + base =0 ; + left =__copy_to_user (buf, vaddr, copy); + copied += copy; + bytes -= copy; + vaddr += copy; + iov++; + +if (unlikely (left)) +break ; + } +return copied - left; +} + +static size_t pram_iov_copy_from (void *to,struct iov_iter *i,size_t bytes) +{ +size_t copied; + +if (likely (i->nr_segs ==1 )) { +int left; +char __user *buf = i->iov->iov_base + i->iov_offset; + left =__copy_from_user (to, buf, bytes); + copied = bytes - left; + }else { + copied =__pram_iov_copy_from (to, i->iov, i->iov_offset, bytes); + } + +return copied; +} + +static size_t pram_iov_copy_to (void *from,struct iov_iter *i,size_t bytes) +{ +size_t copied; + +if (likely (i->nr_segs ==1 )) { +int left; +char __user *buf = i->iov->iov_base + i->iov_offset; + left =__copy_to_user (buf, from, bytes); + copied = bytes - left; + }else { + copied =__pram_iov_copy_to (from, i->iov, i->iov_offset, bytes); + } + +return copied; +} + +static size_t __pram_clear_user (const struct iovec *iov,size_t base,size_t bytes) +{ +size_t claened =0 , left =0 ; + +while (bytes) { +char __user *buf = iov->iov_base + base; +int clear =min (bytes, iov->iov_len - base); + + base =0 ; + left =__clear_user (buf, clear); + claened += clear; + bytes -= clear; + iov++; + +if (unlikely (left)) +break ; + } +return claened - left; +} + +static size_t pram_clear_user (struct iov_iter *i,size_t bytes) +{ +size_t clear; + +if (likely (i->nr_segs ==1 )) { +int left; +char __user *buf = i->iov->iov_base + i->iov_offset; + left =__clear_user (buf, bytes); + clear = bytes - left; + }else { + clear =__pram_clear_user (i->iov, i->iov_offset, bytes); + } + +return clear; +} + +static int pram_open_file (struct inode *inode,struct file *filp) +{ + filp->f_flags |=O_DIRECT ; +return generic_file_open (inode, filp); +} + +ssize_t pram_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; +struct super_block *sb = inode->i_sb; +int progress =0 , hole =0 , alloc_once =1 ; +ssize_t retval =0 ; +void *tmp =NULL ; +unsigned long blocknr, blockoff, blocknr_start; +struct iov_iter iter; +int num_blocks, blocksize_mask; +size_t length =iov_length (iov, nr_segs); + +if (length <0 ) +return -EINVAL ; +if ((rw ==READ ) && (offset + length > inode->i_size)) + length = inode->i_size - offset; +if (!length) + goto out; + + blocksize_mask = (1 << sb->s_blocksize_bits) -1 ; +/* find starting block number to access */ + blocknr = offset >> sb->s_blocksize_bits; +/* find starting offset within starting block */ + blockoff = offset & blocksize_mask; +/* find number of blocks to access */ + num_blocks = (blockoff + length + blocksize_mask) >> + sb->s_blocksize_bits; + blocknr_start = blocknr; + +if (rw ==WRITE ) { +/* prepare a temporary buffer to hold a user data block +for writing. */ + tmp =kmalloc (sb->s_blocksize,GFP_KERNEL ); +if (!tmp) +return -ENOMEM ; + } + +iov_iter_init (&iter, iov, nr_segs, length,0 ); + +while (length) { +int count; +u8 *bp =NULL ; +u64 block =pram_find_data_block (inode, blocknr); +if (!block) { +if (alloc_once && rw ==WRITE ) { +/* +* Allocate the data blocks starting from blocknr +* to the end. +*/ + retval =pram_alloc_blocks (inode, blocknr, + num_blocks - (blocknr - + blocknr_start)); +if (retval) + goto fail; +/* retry....*/ + block =pram_find_data_block (inode, blocknr); +BUG_ON (!block); + alloc_once =0 ; + }else if (unlikely (rw ==READ )) { +/* We are falling in a hole */ + hole =1 ; + goto hole; + } + } + bp = (u8 *)pram_get_block (sb, block); +if (!bp) { + retval = -EACCES ; + goto fail; + } + hole: + ++blocknr; + + count = blockoff + length > sb->s_blocksize ? + sb->s_blocksize - blockoff : length; + +if (rw ==READ ) { +if (unlikely (hole)) { + retval =pram_clear_user (&iter, count); +if (retval != count) { + retval = -EFAULT ; + goto fail; + } + }else { + retval =pram_iov_copy_to (&bp[blockoff], &iter, + count); +if (retval != count) { + retval = -EFAULT ; + goto fail; + } + } + }else { + retval =pram_iov_copy_from (tmp, &iter, count); +if (retval != count) { + retval = -EFAULT ; + goto fail; + } + +pram_memunlock_block (inode->i_sb, bp); +memcpy (&bp[blockoff], tmp, count); +pram_memlock_block (inode->i_sb, bp); + } + + progress += count; +iov_iter_advance (&iter, count); + length -= count; + blockoff =0 ; + hole =0 ; + } + + retval = progress; + fail: +kfree (tmp); + out: +return retval; +} + +int pram_mmap (struct file *file,struct vm_area_struct *vma) +{ +/* Only private mappings */ +if (vma->vm_flags &VM_SHARED ) +return -EINVAL ; +return generic_file_mmap (file, vma); +} + +static int pram_check_flags (int flags) +{ +if (!(flags &O_DIRECT )) +return -EINVAL ; + +return 0 ; +} + +struct file_operations pram_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = pram_mmap, + .open = pram_open_file, + .fsync = noop_fsync, + .check_flags = pram_check_flags, + .unlocked_ioctl = pram_ioctl, + .splice_read = generic_file_splice_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; + +#ifdef CONFIG_PRAMFS_XIP +struct file_operations pram_xip_file_operations = { + .llseek = generic_file_llseek, + .read = xip_file_read, + .write = xip_file_write, + .mmap = xip_file_mmap, + .open = generic_file_open, + .fsync = noop_fsync, + .unlocked_ioctl = pram_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; +#endif + +struct inode_operations pram_file_inode_operations = { +#ifdef CONFIG_PRAMFS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = pram_listxattr, + .removexattr = generic_removexattr, +#endif + .setattr = pram_notify_change, + .check_acl = pram_check_acl, + .fallocate = pram_fallocate, +};