Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754463Ab2FJJUk (ORCPT ); Sun, 10 Jun 2012 05:20:40 -0400 Received: from mail-wi0-f172.google.com ([209.85.212.172]:58491 "EHLO mail-wi0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753131Ab2FJJUe (ORCPT ); Sun, 10 Jun 2012 05:20:34 -0400 Message-ID: <4FD4655D.6080602@gmail.com> Date: Sun, 10 Jun 2012 11:14:05 +0200 From: Marco Stornelli User-Agent: Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20120421 Thunderbird/12.0 MIME-Version: 1.0 To: Linux FS Devel CC: Linux Kernel Subject: [PATCH 04/17] pramfs: file operations Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11155 Lines: 454 From: Marco Stornelli File operations. Signed-off-by: Marco Stornelli --- diff -Nurp linux-3.5-rc2-orig/fs/pramfs/file.c linux-3.5-rc2/fs/pramfs/file.c --- linux-3.5-rc2-orig/fs/pramfs/file.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.5-rc2/fs/pramfs/file.c 2012-06-10 10:08:28.000000000 +0200 @@ -0,0 +1,439 @@ +/* + * BRIEF DESCRIPTION + * + * File operations for files. + * + * Copyright 2009-2011 Marco Stornelli + * 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 +#include +#include +#include +#include +#include +#include +#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; + unsigned int num_blocks; + size_t length = iov_length(iov, nr_segs); + loff_t size; + + /* + * If we are in the write path we are under i_mutex but no lock held + * in the read, so we need to be sync with truncate to avoid race + * conditions. + */ + if (rw == READ) + rcu_read_lock(); + + size = i_size_read(inode); + + if (length < 0) { + retval = -EINVAL; + goto out; + } + if ((rw == READ) && (offset + length > size)) + length = size - offset; + if (!length) + goto out; + + /* find starting block number to access */ + blocknr = offset >> sb->s_blocksize_bits; + /* find starting offset within starting block */ + blockoff = offset & (sb->s_blocksize - 1); + /* find number of blocks to access */ + num_blocks = (blockoff + length + sb->s_blocksize - 1) >> + 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) { + retval = -ENOMEM; + goto out; + } + } + + 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; + /* + * Check for the flag EOFBLOCKS is still valid after the extending + * write. + */ + if (rw == WRITE && (offset + length >= size)) + check_eof_blocks(inode, size + retval); + fail: + kfree(tmp); + out: + if (rw == READ) + rcu_read_unlock(); + return retval; +} + +static int pram_check_flags(int flags) +{ + if (!(flags & O_DIRECT)) + return -EINVAL; + + return 0; +} + +static long pram_fallocate(struct file *file, int mode, loff_t offset, + loff_t len) +{ + struct inode *inode = file->f_path.dentry->d_inode; + long ret = 0; + unsigned long blocknr, blockoff; + int num_blocks, blocksize_mask; + struct pram_inode *pi; + loff_t new_size; + + /* We only support the FALLOC_FL_KEEP_SIZE mode */ + if (mode & ~FALLOC_FL_KEEP_SIZE) + return -EOPNOTSUPP; + + if (S_ISDIR(inode->i_mode)) + return -ENODEV; + + mutex_lock(&inode->i_mutex); + + new_size = len + offset; + if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) { + ret = inode_newsize_ok(inode, new_size); + if (ret) + goto out; + } + + blocksize_mask = (1 << inode->i_sb->s_blocksize_bits) - 1; + blocknr = offset >> inode->i_sb->s_blocksize_bits; + blockoff = offset & blocksize_mask; + num_blocks = (blockoff + len + blocksize_mask) >> + inode->i_sb->s_blocksize_bits; + ret = pram_alloc_blocks(inode, blocknr, num_blocks); + if (ret) + goto out; + + if (mode & FALLOC_FL_KEEP_SIZE) { + pi = pram_get_inode(inode->i_sb, inode->i_ino); + if (!pi) { + ret = -EACCES; + goto out; + } + pram_memunlock_inode(inode->i_sb, pi); + pi->i_flags |= cpu_to_be32(PRAM_EOFBLOCKS_FL); + pram_memlock_inode(inode->i_sb, pi); + } + + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size > inode->i_size) + inode->i_size = new_size; + ret = pram_update_inode(inode); + out: + mutex_unlock(&inode->i_mutex); + return ret; +} + +loff_t pram_llseek(struct file *file, loff_t offset, int origin) +{ + struct inode *inode = file->f_path.dentry->d_inode; + int retval; + + if (origin != SEEK_DATA && origin != SEEK_HOLE) + return generic_file_llseek(file, offset, origin); + + mutex_lock(&inode->i_mutex); + switch (origin) { + case SEEK_DATA: + retval = pram_find_region(inode, &offset, 0); + if (retval) { + mutex_unlock(&inode->i_mutex); + return retval; + } + break; + case SEEK_HOLE: + retval = pram_find_region(inode, &offset, 1); + if (retval) { + mutex_unlock(&inode->i_mutex); + return retval; + } + break; + } + + if ((offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) || + offset > inode->i_sb->s_maxbytes) { + mutex_unlock(&inode->i_mutex); + return -EINVAL; + } + + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; + } + + mutex_unlock(&inode->i_mutex); + return offset; +} + +const struct file_operations pram_file_operations = { + .llseek = pram_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_readonly_mmap, + .open = pram_open_file, + .fsync = noop_fsync, + .check_flags = pram_check_flags, + .unlocked_ioctl = pram_ioctl, + .splice_read = generic_file_splice_read, + .fallocate = pram_fallocate, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; + +#ifdef CONFIG_PRAMFS_XIP +const struct file_operations pram_xip_file_operations = { + .llseek = pram_llseek, + .read = pram_xip_file_read, + .write = xip_file_write, + .mmap = pram_xip_file_mmap, + .open = generic_file_open, + .fsync = noop_fsync, + .unlocked_ioctl = pram_ioctl, + .fallocate = pram_fallocate, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; +#endif + +const 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, + .get_acl = pram_get_acl, +}; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/