diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-03-29 11:56:46 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-03-29 11:56:46 -0400 |
commit | 75dfe3960e602e63ea42ac7a2a0520832b189ffa (patch) | |
tree | 106e8b09afcf5facfba8ba0cf3343e46d5db3ce3 | |
parent | 30ae8467483d7ab023b5e728bf7d74a575c78023 (diff) |
btrfs_file_write -- first pass
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/super.c | 222 |
1 files changed, 213 insertions, 9 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 151c1002d74e..7914b31f5bcd 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -9,6 +9,8 @@ | |||
9 | #include <linux/smp_lock.h> | 9 | #include <linux/smp_lock.h> |
10 | #include <linux/backing-dev.h> | 10 | #include <linux/backing-dev.h> |
11 | #include <linux/mpage.h> | 11 | #include <linux/mpage.h> |
12 | #include <linux/swap.h> | ||
13 | #include <linux/writeback.h> | ||
12 | #include "ctree.h" | 14 | #include "ctree.h" |
13 | #include "disk-io.h" | 15 | #include "disk-io.h" |
14 | #include "transaction.h" | 16 | #include "transaction.h" |
@@ -755,7 +757,7 @@ printk("btrfs sync_fs\n"); | |||
755 | return 0; | 757 | return 0; |
756 | } | 758 | } |
757 | 759 | ||
758 | static int btrfs_get_block(struct inode *inode, sector_t iblock, | 760 | static int btrfs_get_block_lock(struct inode *inode, sector_t iblock, |
759 | struct buffer_head *result, int create) | 761 | struct buffer_head *result, int create) |
760 | { | 762 | { |
761 | int ret; | 763 | int ret; |
@@ -772,7 +774,6 @@ static int btrfs_get_block(struct inode *inode, sector_t iblock, | |||
772 | struct btrfs_disk_key *found_key; | 774 | struct btrfs_disk_key *found_key; |
773 | 775 | ||
774 | btrfs_init_path(&path); | 776 | btrfs_init_path(&path); |
775 | mutex_lock(&root->fs_info->fs_mutex); | ||
776 | if (create) | 777 | if (create) |
777 | trans = btrfs_start_transaction(root, 1); | 778 | trans = btrfs_start_transaction(root, 1); |
778 | 779 | ||
@@ -848,6 +849,16 @@ allocate: | |||
848 | out: | 849 | out: |
849 | if (trans) | 850 | if (trans) |
850 | btrfs_end_transaction(trans, root); | 851 | btrfs_end_transaction(trans, root); |
852 | return err; | ||
853 | } | ||
854 | |||
855 | static int btrfs_get_block(struct inode *inode, sector_t iblock, | ||
856 | struct buffer_head *result, int create) | ||
857 | { | ||
858 | int err; | ||
859 | struct btrfs_root *root = btrfs_sb(inode->i_sb); | ||
860 | mutex_lock(&root->fs_info->fs_mutex); | ||
861 | err = btrfs_get_block_lock(inode, iblock, result, create); | ||
851 | mutex_unlock(&root->fs_info->fs_mutex); | 862 | mutex_unlock(&root->fs_info->fs_mutex); |
852 | return err; | 863 | return err; |
853 | } | 864 | } |
@@ -855,8 +866,15 @@ out: | |||
855 | static int btrfs_prepare_write(struct file *file, struct page *page, | 866 | static int btrfs_prepare_write(struct file *file, struct page *page, |
856 | unsigned from, unsigned to) | 867 | unsigned from, unsigned to) |
857 | { | 868 | { |
869 | WARN_ON(1); | ||
858 | return nobh_prepare_write(page, from, to, btrfs_get_block); | 870 | return nobh_prepare_write(page, from, to, btrfs_get_block); |
859 | } | 871 | } |
872 | static int btrfs_commit_write(struct file *file, struct page *page, | ||
873 | unsigned from, unsigned to) | ||
874 | { | ||
875 | WARN_ON(1); | ||
876 | return nobh_commit_write(file, page, from, to); | ||
877 | } | ||
860 | 878 | ||
861 | static void btrfs_write_super(struct super_block *sb) | 879 | static void btrfs_write_super(struct super_block *sb) |
862 | { | 880 | { |
@@ -903,6 +921,196 @@ static void btrfs_truncate(struct inode *inode) | |||
903 | mark_inode_dirty(inode); | 921 | mark_inode_dirty(inode); |
904 | } | 922 | } |
905 | 923 | ||
924 | static int btrfs_copy_from_user(loff_t pos, int num_pages, int write_bytes, | ||
925 | struct page **prepared_pages, | ||
926 | const char __user * buf) | ||
927 | { | ||
928 | long page_fault = 0; | ||
929 | int i; | ||
930 | int offset = pos & (PAGE_CACHE_SIZE - 1); | ||
931 | |||
932 | for (i = 0; i < num_pages && write_bytes > 0; i++, offset = 0) { | ||
933 | size_t count = min_t(size_t, | ||
934 | PAGE_CACHE_SIZE - offset, write_bytes); | ||
935 | struct page *page = prepared_pages[i]; | ||
936 | fault_in_pages_readable(buf, count); | ||
937 | |||
938 | /* Copy data from userspace to the current page */ | ||
939 | kmap(page); | ||
940 | page_fault = __copy_from_user(page_address(page) + offset, | ||
941 | buf, count); | ||
942 | /* Flush processor's dcache for this page */ | ||
943 | flush_dcache_page(page); | ||
944 | kunmap(page); | ||
945 | buf += count; | ||
946 | write_bytes -= count; | ||
947 | |||
948 | if (page_fault) | ||
949 | break; | ||
950 | } | ||
951 | return page_fault ? -EFAULT : 0; | ||
952 | } | ||
953 | |||
954 | static void btrfs_drop_pages(struct page **pages, size_t num_pages) | ||
955 | { | ||
956 | size_t i; | ||
957 | for (i = 0; i < num_pages; i++) { | ||
958 | if (!pages[i]) | ||
959 | break; | ||
960 | unlock_page(pages[i]); | ||
961 | mark_page_accessed(pages[i]); | ||
962 | page_cache_release(pages[i]); | ||
963 | } | ||
964 | } | ||
965 | static int dirty_and_release_pages(struct btrfs_trans_handle *trans, | ||
966 | struct btrfs_root *root, | ||
967 | struct file *file, | ||
968 | struct page **pages, | ||
969 | size_t num_pages, | ||
970 | loff_t pos, | ||
971 | size_t write_bytes) | ||
972 | { | ||
973 | int i; | ||
974 | int offset; | ||
975 | int err = 0; | ||
976 | int ret; | ||
977 | int this_write; | ||
978 | |||
979 | for (i = 0; i < num_pages; i++) { | ||
980 | offset = pos & (PAGE_CACHE_SIZE -1); | ||
981 | this_write = min(PAGE_CACHE_SIZE - offset, write_bytes); | ||
982 | ret = nobh_commit_write(file, pages[i], offset, | ||
983 | offset + this_write); | ||
984 | pos += this_write; | ||
985 | if (ret) { | ||
986 | err = ret; | ||
987 | goto failed; | ||
988 | } | ||
989 | WARN_ON(this_write > write_bytes); | ||
990 | write_bytes -= this_write; | ||
991 | } | ||
992 | failed: | ||
993 | return err; | ||
994 | } | ||
995 | |||
996 | static int prepare_pages(struct btrfs_trans_handle *trans, | ||
997 | struct btrfs_root *root, | ||
998 | struct file *file, | ||
999 | struct page **pages, | ||
1000 | size_t num_pages, | ||
1001 | loff_t pos, | ||
1002 | size_t write_bytes) | ||
1003 | { | ||
1004 | int i; | ||
1005 | unsigned long index = pos >> PAGE_CACHE_SHIFT; | ||
1006 | struct inode *inode = file->f_path.dentry->d_inode; | ||
1007 | int offset; | ||
1008 | int err = 0; | ||
1009 | int ret; | ||
1010 | int this_write; | ||
1011 | loff_t isize = i_size_read(inode); | ||
1012 | |||
1013 | memset(pages, 0, num_pages * sizeof(struct page *)); | ||
1014 | |||
1015 | for (i = 0; i < num_pages; i++) { | ||
1016 | pages[i] = grab_cache_page(inode->i_mapping, index + i); | ||
1017 | if (!pages[i]) { | ||
1018 | err = -ENOMEM; | ||
1019 | goto failed_release; | ||
1020 | } | ||
1021 | offset = pos & (PAGE_CACHE_SIZE -1); | ||
1022 | this_write = min(PAGE_CACHE_SIZE - offset, write_bytes); | ||
1023 | ret = nobh_prepare_write(pages[i], offset, | ||
1024 | offset + this_write, | ||
1025 | btrfs_get_block_lock); | ||
1026 | pos += this_write; | ||
1027 | if (ret) { | ||
1028 | err = ret; | ||
1029 | goto failed_truncate; | ||
1030 | } | ||
1031 | WARN_ON(this_write > write_bytes); | ||
1032 | write_bytes -= this_write; | ||
1033 | } | ||
1034 | return 0; | ||
1035 | |||
1036 | failed_release: | ||
1037 | btrfs_drop_pages(pages, num_pages); | ||
1038 | return err; | ||
1039 | |||
1040 | failed_truncate: | ||
1041 | btrfs_drop_pages(pages, num_pages); | ||
1042 | if (pos > isize) | ||
1043 | vmtruncate(inode, isize); | ||
1044 | return err; | ||
1045 | } | ||
1046 | |||
1047 | static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | ||
1048 | size_t count, loff_t *ppos) | ||
1049 | { | ||
1050 | loff_t pos; | ||
1051 | size_t num_written = 0; | ||
1052 | int err = 0; | ||
1053 | int ret = 0; | ||
1054 | struct btrfs_trans_handle *trans; | ||
1055 | struct inode *inode = file->f_path.dentry->d_inode; | ||
1056 | struct btrfs_root *root = btrfs_sb(inode->i_sb); | ||
1057 | struct page *pages[1]; | ||
1058 | |||
1059 | if (file->f_flags & O_DIRECT) | ||
1060 | return -EINVAL; | ||
1061 | pos = *ppos; | ||
1062 | |||
1063 | vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); | ||
1064 | current->backing_dev_info = inode->i_mapping->backing_dev_info; | ||
1065 | err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); | ||
1066 | if (err) | ||
1067 | goto out; | ||
1068 | if (count == 0) | ||
1069 | goto out; | ||
1070 | err = remove_suid(file->f_path.dentry); | ||
1071 | if (err) | ||
1072 | goto out; | ||
1073 | file_update_time(file); | ||
1074 | mutex_lock(&inode->i_mutex); | ||
1075 | while(count > 0) { | ||
1076 | size_t offset = pos & (PAGE_CACHE_SIZE - 1); | ||
1077 | size_t write_bytes = min(count, PAGE_CACHE_SIZE - offset); | ||
1078 | size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >> | ||
1079 | PAGE_CACHE_SHIFT; | ||
1080 | mutex_lock(&root->fs_info->fs_mutex); | ||
1081 | trans = btrfs_start_transaction(root, 1); | ||
1082 | |||
1083 | ret = prepare_pages(trans, root, file, pages, num_pages, | ||
1084 | pos, write_bytes); | ||
1085 | BUG_ON(ret); | ||
1086 | ret = btrfs_copy_from_user(pos, num_pages, | ||
1087 | write_bytes, pages, buf); | ||
1088 | BUG_ON(ret); | ||
1089 | |||
1090 | mutex_unlock(&root->fs_info->fs_mutex); | ||
1091 | |||
1092 | ret = dirty_and_release_pages(trans, root, file, pages, | ||
1093 | num_pages, pos, write_bytes); | ||
1094 | BUG_ON(ret); | ||
1095 | btrfs_drop_pages(pages, num_pages); | ||
1096 | |||
1097 | ret = btrfs_end_transaction(trans, root); | ||
1098 | |||
1099 | buf += write_bytes; | ||
1100 | count -= write_bytes; | ||
1101 | pos += write_bytes; | ||
1102 | num_written += write_bytes; | ||
1103 | |||
1104 | balance_dirty_pages_ratelimited(inode->i_mapping); | ||
1105 | cond_resched(); | ||
1106 | } | ||
1107 | mutex_unlock(&inode->i_mutex); | ||
1108 | out: | ||
1109 | *ppos = pos; | ||
1110 | current->backing_dev_info = NULL; | ||
1111 | return num_written ? num_written : err; | ||
1112 | } | ||
1113 | |||
906 | static int btrfs_get_sb(struct file_system_type *fs_type, | 1114 | static int btrfs_get_sb(struct file_system_type *fs_type, |
907 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) | 1115 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) |
908 | { | 1116 | { |
@@ -948,7 +1156,7 @@ static struct address_space_operations btrfs_aops = { | |||
948 | .writepage = btrfs_writepage, | 1156 | .writepage = btrfs_writepage, |
949 | .sync_page = block_sync_page, | 1157 | .sync_page = block_sync_page, |
950 | .prepare_write = btrfs_prepare_write, | 1158 | .prepare_write = btrfs_prepare_write, |
951 | .commit_write = nobh_commit_write, | 1159 | .commit_write = btrfs_commit_write, |
952 | }; | 1160 | }; |
953 | 1161 | ||
954 | static struct inode_operations btrfs_file_inode_operations = { | 1162 | static struct inode_operations btrfs_file_inode_operations = { |
@@ -958,14 +1166,10 @@ static struct inode_operations btrfs_file_inode_operations = { | |||
958 | static struct file_operations btrfs_file_operations = { | 1166 | static struct file_operations btrfs_file_operations = { |
959 | .llseek = generic_file_llseek, | 1167 | .llseek = generic_file_llseek, |
960 | .read = do_sync_read, | 1168 | .read = do_sync_read, |
961 | .write = do_sync_write, | 1169 | .aio_read = generic_file_aio_read, |
962 | .aio_read = generic_file_aio_read, | 1170 | .write = btrfs_file_write, |
963 | .aio_write = generic_file_aio_write, | ||
964 | .mmap = generic_file_mmap, | 1171 | .mmap = generic_file_mmap, |
965 | .open = generic_file_open, | 1172 | .open = generic_file_open, |
966 | .sendfile = generic_file_sendfile, | ||
967 | .splice_read = generic_file_splice_read, | ||
968 | .splice_write = generic_file_splice_write, | ||
969 | }; | 1173 | }; |
970 | 1174 | ||
971 | static int __init init_btrfs_fs(void) | 1175 | static int __init init_btrfs_fs(void) |