]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
btrfs_file_write -- first pass
authorChris Mason <chris.mason@oracle.com>
Thu, 29 Mar 2007 15:56:46 +0000 (11:56 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Thu, 29 Mar 2007 15:56:46 +0000 (11:56 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/super.c

index 151c1002d74e4bf872f2776c37ceaffe47cbc06f..7914b31f5bcd42de681020d23bab4429a85678c2 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/smp_lock.h>
 #include <linux/backing-dev.h>
 #include <linux/mpage.h>
+#include <linux/swap.h>
+#include <linux/writeback.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -755,7 +757,7 @@ printk("btrfs sync_fs\n");
        return 0;
 }
 
-static int btrfs_get_block(struct inode *inode, sector_t iblock,
+static int btrfs_get_block_lock(struct inode *inode, sector_t iblock,
                           struct buffer_head *result, int create)
 {
        int ret;
@@ -772,7 +774,6 @@ static int btrfs_get_block(struct inode *inode, sector_t iblock,
        struct btrfs_disk_key *found_key;
 
        btrfs_init_path(&path);
-       mutex_lock(&root->fs_info->fs_mutex);
        if (create)
                trans = btrfs_start_transaction(root, 1);
 
@@ -848,6 +849,16 @@ allocate:
 out:
        if (trans)
                btrfs_end_transaction(trans, root);
+       return err;
+}
+
+static int btrfs_get_block(struct inode *inode, sector_t iblock,
+                          struct buffer_head *result, int create)
+{
+       int err;
+       struct btrfs_root *root = btrfs_sb(inode->i_sb);
+       mutex_lock(&root->fs_info->fs_mutex);
+       err = btrfs_get_block_lock(inode, iblock, result, create);
        mutex_unlock(&root->fs_info->fs_mutex);
        return err;
 }
@@ -855,8 +866,15 @@ out:
 static int btrfs_prepare_write(struct file *file, struct page *page,
                               unsigned from, unsigned to)
 {
+       WARN_ON(1);
        return nobh_prepare_write(page, from, to, btrfs_get_block);
 }
+static int btrfs_commit_write(struct file *file, struct page *page,
+                              unsigned from, unsigned to)
+{
+       WARN_ON(1);
+       return nobh_commit_write(file, page, from, to);
+}
 
 static void btrfs_write_super(struct super_block *sb)
 {
@@ -903,6 +921,196 @@ static void btrfs_truncate(struct inode *inode)
        mark_inode_dirty(inode);
 }
 
+static int btrfs_copy_from_user(loff_t pos, int num_pages, int write_bytes,
+                               struct page **prepared_pages,
+                               const char __user * buf)
+{
+       long page_fault = 0;
+       int i;
+       int offset = pos & (PAGE_CACHE_SIZE - 1);
+
+       for (i = 0; i < num_pages && write_bytes > 0; i++, offset = 0) {
+               size_t count = min_t(size_t,
+                                    PAGE_CACHE_SIZE - offset, write_bytes);
+               struct page *page = prepared_pages[i];
+               fault_in_pages_readable(buf, count);
+
+               /* Copy data from userspace to the current page */
+               kmap(page);
+               page_fault = __copy_from_user(page_address(page) + offset,
+                                             buf, count);
+               /* Flush processor's dcache for this page */
+               flush_dcache_page(page);
+               kunmap(page);
+               buf += count;
+               write_bytes -= count;
+
+               if (page_fault)
+                       break;
+       }
+       return page_fault ? -EFAULT : 0;
+}
+
+static void btrfs_drop_pages(struct page **pages, size_t num_pages)
+{
+       size_t i;
+       for (i = 0; i < num_pages; i++) {
+               if (!pages[i])
+                       break;
+               unlock_page(pages[i]);
+               mark_page_accessed(pages[i]);
+               page_cache_release(pages[i]);
+       }
+}
+static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root,
+                                  struct file *file,
+                                  struct page **pages,
+                                  size_t num_pages,
+                                  loff_t pos,
+                                  size_t write_bytes)
+{
+       int i;
+       int offset;
+       int err = 0;
+       int ret;
+       int this_write;
+
+       for (i = 0; i < num_pages; i++) {
+               offset = pos & (PAGE_CACHE_SIZE -1);
+               this_write = min(PAGE_CACHE_SIZE - offset, write_bytes);
+               ret = nobh_commit_write(file, pages[i], offset,
+                                        offset + this_write);
+               pos += this_write;
+               if (ret) {
+                       err = ret;
+                       goto failed;
+               }
+               WARN_ON(this_write > write_bytes);
+               write_bytes -= this_write;
+       }
+failed:
+       return err;
+}
+
+static int prepare_pages(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root,
+                        struct file *file,
+                        struct page **pages,
+                        size_t num_pages,
+                        loff_t pos,
+                        size_t write_bytes)
+{
+       int i;
+       unsigned long index = pos >> PAGE_CACHE_SHIFT;
+       struct inode *inode = file->f_path.dentry->d_inode;
+       int offset;
+       int err = 0;
+       int ret;
+       int this_write;
+       loff_t isize = i_size_read(inode);
+
+       memset(pages, 0, num_pages * sizeof(struct page *));
+
+       for (i = 0; i < num_pages; i++) {
+               pages[i] = grab_cache_page(inode->i_mapping, index + i);
+               if (!pages[i]) {
+                       err = -ENOMEM;
+                       goto failed_release;
+               }
+               offset = pos & (PAGE_CACHE_SIZE -1);
+               this_write = min(PAGE_CACHE_SIZE - offset, write_bytes);
+               ret = nobh_prepare_write(pages[i], offset,
+                                        offset + this_write,
+                                        btrfs_get_block_lock);
+               pos += this_write;
+               if (ret) {
+                       err = ret;
+                       goto failed_truncate;
+               }
+               WARN_ON(this_write > write_bytes);
+               write_bytes -= this_write;
+       }
+       return 0;
+
+failed_release:
+       btrfs_drop_pages(pages, num_pages);
+       return err;
+
+failed_truncate:
+       btrfs_drop_pages(pages, num_pages);
+       if (pos > isize)
+               vmtruncate(inode, isize);
+       return err;
+}
+
+static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       loff_t pos;
+       size_t num_written = 0;
+       int err = 0;
+       int ret = 0;
+       struct btrfs_trans_handle *trans;
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct btrfs_root *root = btrfs_sb(inode->i_sb);
+       struct page *pages[1];
+
+       if (file->f_flags & O_DIRECT)
+               return -EINVAL;
+       pos = *ppos;
+
+       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       current->backing_dev_info = inode->i_mapping->backing_dev_info;
+       err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
+       if (err)
+               goto out;
+       if (count == 0)
+               goto out;
+       err = remove_suid(file->f_path.dentry);
+       if (err)
+               goto out;
+       file_update_time(file);
+       mutex_lock(&inode->i_mutex);
+       while(count > 0) {
+               size_t offset = pos & (PAGE_CACHE_SIZE - 1);
+               size_t write_bytes = min(count, PAGE_CACHE_SIZE - offset);
+               size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT;
+               mutex_lock(&root->fs_info->fs_mutex);
+               trans = btrfs_start_transaction(root, 1);
+
+               ret = prepare_pages(trans, root, file, pages, num_pages,
+                                   pos, write_bytes);
+               BUG_ON(ret);
+               ret = btrfs_copy_from_user(pos, num_pages,
+                                          write_bytes, pages, buf);
+               BUG_ON(ret);
+
+               mutex_unlock(&root->fs_info->fs_mutex);
+
+               ret = dirty_and_release_pages(trans, root, file, pages,
+                                             num_pages, pos, write_bytes);
+               BUG_ON(ret);
+               btrfs_drop_pages(pages, num_pages);
+
+               ret = btrfs_end_transaction(trans, root);
+
+               buf += write_bytes;
+               count -= write_bytes;
+               pos += write_bytes;
+               num_written += write_bytes;
+
+               balance_dirty_pages_ratelimited(inode->i_mapping);
+               cond_resched();
+       }
+       mutex_unlock(&inode->i_mutex);
+out:
+       *ppos = pos;
+       current->backing_dev_info = NULL;
+       return num_written ? num_written : err;
+}
+
 static int btrfs_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
@@ -948,7 +1156,7 @@ static struct address_space_operations btrfs_aops = {
        .writepage      = btrfs_writepage,
        .sync_page      = block_sync_page,
        .prepare_write  = btrfs_prepare_write,
-       .commit_write   = nobh_commit_write,
+       .commit_write   = btrfs_commit_write,
 };
 
 static struct inode_operations btrfs_file_inode_operations = {
@@ -958,14 +1166,10 @@ static struct inode_operations btrfs_file_inode_operations = {
 static struct file_operations btrfs_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,
+       .aio_read       = generic_file_aio_read,
+       .write          = btrfs_file_write,
        .mmap           = generic_file_mmap,
        .open           = generic_file_open,
-       .sendfile       = generic_file_sendfile,
-       .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
 };
 
 static int __init init_btrfs_fs(void)