diff options
Diffstat (limited to 'fs/btrfs/super.c')
-rw-r--r-- | fs/btrfs/super.c | 188 |
1 files changed, 154 insertions, 34 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 905b093a85fe..2c2883f2856d 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/string.h> | 8 | #include <linux/string.h> |
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 "ctree.h" | 12 | #include "ctree.h" |
12 | #include "disk-io.h" | 13 | #include "disk-io.h" |
13 | #include "transaction.h" | 14 | #include "transaction.h" |
@@ -17,6 +18,9 @@ | |||
17 | static struct inode_operations btrfs_dir_inode_operations; | 18 | static struct inode_operations btrfs_dir_inode_operations; |
18 | static struct super_operations btrfs_super_ops; | 19 | static struct super_operations btrfs_super_ops; |
19 | static struct file_operations btrfs_dir_file_operations; | 20 | static struct file_operations btrfs_dir_file_operations; |
21 | static struct inode_operations btrfs_file_inode_operations; | ||
22 | static struct address_space_operations btrfs_aops; | ||
23 | static struct file_operations btrfs_file_operations; | ||
20 | 24 | ||
21 | static void btrfs_read_locked_inode(struct inode *inode) | 25 | static void btrfs_read_locked_inode(struct inode *inode) |
22 | { | 26 | { |
@@ -57,6 +61,9 @@ static void btrfs_read_locked_inode(struct inode *inode) | |||
57 | break; | 61 | break; |
58 | #endif | 62 | #endif |
59 | case S_IFREG: | 63 | case S_IFREG: |
64 | inode->i_mapping->a_ops = &btrfs_aops; | ||
65 | inode->i_fop = &btrfs_file_operations; | ||
66 | inode->i_op = &btrfs_file_inode_operations; | ||
60 | break; | 67 | break; |
61 | case S_IFDIR: | 68 | case S_IFDIR: |
62 | inode->i_op = &btrfs_dir_inode_operations; | 69 | inode->i_op = &btrfs_dir_inode_operations; |
@@ -214,35 +221,6 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry, | |||
214 | return d_splice_alias(inode, dentry); | 221 | return d_splice_alias(inode, dentry); |
215 | } | 222 | } |
216 | 223 | ||
217 | static void reada_leaves(struct btrfs_root *root, struct btrfs_path *path) | ||
218 | { | ||
219 | struct buffer_head *bh; | ||
220 | struct btrfs_node *node; | ||
221 | int i; | ||
222 | int nritems; | ||
223 | u64 objectid; | ||
224 | u64 item_objectid; | ||
225 | u64 blocknr; | ||
226 | int slot; | ||
227 | |||
228 | if (!path->nodes[1]) | ||
229 | return; | ||
230 | node = btrfs_buffer_node(path->nodes[1]); | ||
231 | slot = path->slots[1]; | ||
232 | objectid = btrfs_disk_key_objectid(&node->ptrs[slot].key); | ||
233 | nritems = btrfs_header_nritems(&node->header); | ||
234 | for (i = slot; i < nritems; i++) { | ||
235 | item_objectid = btrfs_disk_key_objectid(&node->ptrs[i].key); | ||
236 | if (item_objectid != objectid) | ||
237 | break; | ||
238 | blocknr = btrfs_node_blockptr(node, i); | ||
239 | bh = sb_getblk(root->fs_info->sb, blocknr); | ||
240 | ll_rw_block(READ, 1, &bh); | ||
241 | brelse(bh); | ||
242 | } | ||
243 | |||
244 | } | ||
245 | |||
246 | static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | 224 | static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir) |
247 | { | 225 | { |
248 | struct inode *inode = filp->f_path.dentry->d_inode; | 226 | struct inode *inode = filp->f_path.dentry->d_inode; |
@@ -269,21 +247,18 @@ static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
269 | goto err; | 247 | goto err; |
270 | } | 248 | } |
271 | advance = 0; | 249 | advance = 0; |
272 | reada_leaves(root, &path); | ||
273 | while(1) { | 250 | while(1) { |
274 | leaf = btrfs_buffer_leaf(path.nodes[0]); | 251 | leaf = btrfs_buffer_leaf(path.nodes[0]); |
275 | nritems = btrfs_header_nritems(&leaf->header); | 252 | nritems = btrfs_header_nritems(&leaf->header); |
276 | slot = path.slots[0]; | 253 | slot = path.slots[0]; |
277 | if (advance) { | 254 | if (advance || slot >= nritems) { |
278 | if (slot == nritems -1) { | 255 | if (slot >= nritems -1) { |
279 | ret = btrfs_next_leaf(root, &path); | 256 | ret = btrfs_next_leaf(root, &path); |
280 | if (ret) | 257 | if (ret) |
281 | break; | 258 | break; |
282 | leaf = btrfs_buffer_leaf(path.nodes[0]); | 259 | leaf = btrfs_buffer_leaf(path.nodes[0]); |
283 | nritems = btrfs_header_nritems(&leaf->header); | 260 | nritems = btrfs_header_nritems(&leaf->header); |
284 | slot = path.slots[0]; | 261 | slot = path.slots[0]; |
285 | if (path.nodes[1] && path.slots[1] == 0) | ||
286 | reada_leaves(root, &path); | ||
287 | } else { | 262 | } else { |
288 | slot++; | 263 | slot++; |
289 | path.slots[0]++; | 264 | path.slots[0]++; |
@@ -297,6 +272,8 @@ static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
297 | continue; | 272 | continue; |
298 | if (btrfs_disk_key_offset(&item->key) < filp->f_pos) | 273 | if (btrfs_disk_key_offset(&item->key) < filp->f_pos) |
299 | continue; | 274 | continue; |
275 | |||
276 | advance = 1; | ||
300 | di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); | 277 | di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); |
301 | over = filldir(dirent, (const char *)(di + 1), | 278 | over = filldir(dirent, (const char *)(di + 1), |
302 | btrfs_dir_name_len(di), | 279 | btrfs_dir_name_len(di), |
@@ -524,6 +501,11 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, | |||
524 | err = btrfs_add_nondir(trans, dentry, inode); | 501 | err = btrfs_add_nondir(trans, dentry, inode); |
525 | if (err) | 502 | if (err) |
526 | drop_inode = 1; | 503 | drop_inode = 1; |
504 | else { | ||
505 | inode->i_mapping->a_ops = &btrfs_aops; | ||
506 | inode->i_fop = &btrfs_file_operations; | ||
507 | inode->i_op = &btrfs_file_inode_operations; | ||
508 | } | ||
527 | dir->i_sb->s_dirt = 1; | 509 | dir->i_sb->s_dirt = 1; |
528 | btrfs_end_transaction(trans, root); | 510 | btrfs_end_transaction(trans, root); |
529 | out_unlock: | 511 | out_unlock: |
@@ -623,11 +605,124 @@ printk("btrfs sync_fs\n"); | |||
623 | return 0; | 605 | return 0; |
624 | } | 606 | } |
625 | 607 | ||
608 | static int btrfs_get_block(struct inode *inode, sector_t iblock, | ||
609 | struct buffer_head *result, int create) | ||
610 | { | ||
611 | int ret; | ||
612 | int err = 0; | ||
613 | u64 blocknr; | ||
614 | u64 extent_start = 0; | ||
615 | u64 extent_end = 0; | ||
616 | u64 objectid = inode->i_ino; | ||
617 | struct btrfs_path path; | ||
618 | struct btrfs_root *root = btrfs_sb(inode->i_sb); | ||
619 | struct btrfs_trans_handle *trans = NULL; | ||
620 | struct btrfs_file_extent_item *item; | ||
621 | struct btrfs_leaf *leaf; | ||
622 | struct btrfs_disk_key *found_key; | ||
623 | |||
624 | btrfs_init_path(&path); | ||
625 | mutex_lock(&root->fs_info->fs_mutex); | ||
626 | if (create) | ||
627 | trans = btrfs_start_transaction(root, 1); | ||
628 | |||
629 | |||
630 | ret = btrfs_lookup_file_extent(trans, root, &path, | ||
631 | inode->i_ino, iblock, 1, 0); | ||
632 | if (ret < 0) { | ||
633 | btrfs_release_path(root, &path); | ||
634 | err = ret; | ||
635 | goto out; | ||
636 | } | ||
637 | |||
638 | if (ret != 0) { | ||
639 | if (path.slots[0] == 0) { | ||
640 | btrfs_release_path(root, &path); | ||
641 | goto allocate; | ||
642 | } | ||
643 | path.slots[0]--; | ||
644 | } | ||
645 | |||
646 | item = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0], | ||
647 | struct btrfs_file_extent_item); | ||
648 | leaf = btrfs_buffer_leaf(path.nodes[0]); | ||
649 | blocknr = btrfs_file_extent_disk_blocknr(item); | ||
650 | blocknr += btrfs_file_extent_offset(item); | ||
651 | |||
652 | /* exact match found, use it */ | ||
653 | if (ret == 0) { | ||
654 | err = 0; | ||
655 | map_bh(result, inode->i_sb, blocknr); | ||
656 | btrfs_release_path(root, &path); | ||
657 | goto out; | ||
658 | } | ||
659 | |||
660 | /* are we inside the extent that was found? */ | ||
661 | found_key = &leaf->items[path.slots[0]].key; | ||
662 | if (btrfs_disk_key_objectid(found_key) != objectid || | ||
663 | btrfs_disk_key_type(found_key) != BTRFS_EXTENT_DATA_KEY) { | ||
664 | extent_end = 0; | ||
665 | extent_start = 0; | ||
666 | btrfs_release_path(root, &path); | ||
667 | goto allocate; | ||
668 | } | ||
669 | |||
670 | extent_start = btrfs_disk_key_offset(&leaf->items[path.slots[0]].key); | ||
671 | extent_start += btrfs_file_extent_offset(item); | ||
672 | extent_end = extent_start + btrfs_file_extent_num_blocks(item); | ||
673 | btrfs_release_path(root, &path); | ||
674 | if (iblock >= extent_start && iblock < extent_end) { | ||
675 | err = 0; | ||
676 | map_bh(result, inode->i_sb, blocknr + iblock - extent_start); | ||
677 | goto out; | ||
678 | } | ||
679 | allocate: | ||
680 | /* ok, create a new extent */ | ||
681 | if (!create) { | ||
682 | err = 0; | ||
683 | goto out; | ||
684 | } | ||
685 | ret = btrfs_alloc_file_extent(trans, root, objectid, iblock, | ||
686 | 1, extent_end, &blocknr); | ||
687 | if (ret) { | ||
688 | err = ret; | ||
689 | goto out; | ||
690 | } | ||
691 | map_bh(result, inode->i_sb, blocknr); | ||
692 | |||
693 | out: | ||
694 | if (trans) | ||
695 | btrfs_end_transaction(trans, root); | ||
696 | mutex_unlock(&root->fs_info->fs_mutex); | ||
697 | return err; | ||
698 | } | ||
699 | |||
700 | static int btrfs_prepare_write(struct file *file, struct page *page, | ||
701 | unsigned from, unsigned to) | ||
702 | { | ||
703 | return block_prepare_write(page, from, to, btrfs_get_block); | ||
704 | } | ||
705 | |||
626 | static void btrfs_write_super(struct super_block *sb) | 706 | static void btrfs_write_super(struct super_block *sb) |
627 | { | 707 | { |
628 | btrfs_sync_fs(sb, 1); | 708 | btrfs_sync_fs(sb, 1); |
629 | } | 709 | } |
630 | 710 | ||
711 | static int btrfs_readpage(struct file *file, struct page *page) | ||
712 | { | ||
713 | return mpage_readpage(page, btrfs_get_block); | ||
714 | } | ||
715 | |||
716 | static int btrfs_readpages(struct file *file, struct address_space *mapping, | ||
717 | struct list_head *pages, unsigned nr_pages) | ||
718 | { | ||
719 | return mpage_readpages(mapping, pages, nr_pages, btrfs_get_block); | ||
720 | } | ||
721 | |||
722 | static int btrfs_writepage(struct page *page, struct writeback_control *wbc) | ||
723 | { | ||
724 | return block_write_full_page(page, btrfs_get_block, wbc); | ||
725 | } | ||
631 | 726 | ||
632 | static int btrfs_get_sb(struct file_system_type *fs_type, | 727 | static int btrfs_get_sb(struct file_system_type *fs_type, |
633 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) | 728 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) |
@@ -667,6 +762,31 @@ static struct file_operations btrfs_dir_file_operations = { | |||
667 | .readdir = btrfs_readdir, | 762 | .readdir = btrfs_readdir, |
668 | }; | 763 | }; |
669 | 764 | ||
765 | static struct address_space_operations btrfs_aops = { | ||
766 | .readpage = btrfs_readpage, | ||
767 | .readpages = btrfs_readpages, | ||
768 | .writepage = btrfs_writepage, | ||
769 | .sync_page = block_sync_page, | ||
770 | .prepare_write = btrfs_prepare_write, | ||
771 | .commit_write = generic_commit_write, | ||
772 | }; | ||
773 | |||
774 | static struct inode_operations btrfs_file_inode_operations = { | ||
775 | .truncate = NULL, | ||
776 | }; | ||
777 | |||
778 | static struct file_operations btrfs_file_operations = { | ||
779 | .llseek = generic_file_llseek, | ||
780 | .read = do_sync_read, | ||
781 | .write = do_sync_write, | ||
782 | .aio_read = generic_file_aio_read, | ||
783 | .aio_write = generic_file_aio_write, | ||
784 | .mmap = generic_file_mmap, | ||
785 | .open = generic_file_open, | ||
786 | .sendfile = generic_file_sendfile, | ||
787 | .splice_read = generic_file_splice_read, | ||
788 | .splice_write = generic_file_splice_write, | ||
789 | }; | ||
670 | 790 | ||
671 | static int __init init_btrfs_fs(void) | 791 | static int __init init_btrfs_fs(void) |
672 | { | 792 | { |