aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-07-18 13:21:36 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-07-20 20:47:56 -0400
commitb26751575a9aa55fd6dbf3febde3ff06dfadc44f (patch)
tree78fc36a92757eafb90bbeae36495adb369fbf6d7 /fs
parent982d816581eeeacfe5b2b7c6d47d13a157616eff (diff)
Btrfs: implement our own ->llseek
In order to handle SEEK_HOLE/SEEK_DATA we need to implement our own llseek. Basically for the normal SEEK_*'s we will just defer to the generic helper, and for SEEK_HOLE/SEEK_DATA we will use our fiemap helper to figure out the nearest hole or data. Currently this helper doesn't check for delalloc bytes for prealloc space, so for now treat prealloc as data until that is fixed. Thanks, Signed-off-by: Josef Bacik <josef@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.h3
-rw-r--r--fs/btrfs/file.c148
2 files changed, 150 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 9552afc24ef7..f1ff62bff1b3 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2510,6 +2510,9 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
2510int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 2510int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
2511 struct list_head *list, int search_commit); 2511 struct list_head *list, int search_commit);
2512/* inode.c */ 2512/* inode.c */
2513struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
2514 size_t pg_offset, u64 start, u64 len,
2515 int create);
2513 2516
2514/* RHEL and EL kernels have a patch that renames PG_checked to FsMisc */ 2517/* RHEL and EL kernels have a patch that renames PG_checked to FsMisc */
2515#if defined(ClearPageFsMisc) && !defined(ClearPageChecked) 2518#if defined(ClearPageFsMisc) && !defined(ClearPageChecked)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index fa4ef18b66b1..bd4d061c6e4d 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1664,8 +1664,154 @@ out:
1664 return ret; 1664 return ret;
1665} 1665}
1666 1666
1667static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
1668{
1669 struct btrfs_root *root = BTRFS_I(inode)->root;
1670 struct extent_map *em;
1671 struct extent_state *cached_state = NULL;
1672 u64 lockstart = *offset;
1673 u64 lockend = i_size_read(inode);
1674 u64 start = *offset;
1675 u64 orig_start = *offset;
1676 u64 len = i_size_read(inode);
1677 u64 last_end = 0;
1678 int ret = 0;
1679
1680 lockend = max_t(u64, root->sectorsize, lockend);
1681 if (lockend <= lockstart)
1682 lockend = lockstart + root->sectorsize;
1683
1684 len = lockend - lockstart + 1;
1685
1686 len = max_t(u64, len, root->sectorsize);
1687 if (inode->i_size == 0)
1688 return -ENXIO;
1689
1690 lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
1691 &cached_state, GFP_NOFS);
1692
1693 /*
1694 * Delalloc is such a pain. If we have a hole and we have pending
1695 * delalloc for a portion of the hole we will get back a hole that
1696 * exists for the entire range since it hasn't been actually written
1697 * yet. So to take care of this case we need to look for an extent just
1698 * before the position we want in case there is outstanding delalloc
1699 * going on here.
1700 */
1701 if (origin == SEEK_HOLE && start != 0) {
1702 if (start <= root->sectorsize)
1703 em = btrfs_get_extent_fiemap(inode, NULL, 0, 0,
1704 root->sectorsize, 0);
1705 else
1706 em = btrfs_get_extent_fiemap(inode, NULL, 0,
1707 start - root->sectorsize,
1708 root->sectorsize, 0);
1709 if (IS_ERR(em)) {
1710 ret = -ENXIO;
1711 goto out;
1712 }
1713 last_end = em->start + em->len;
1714 if (em->block_start == EXTENT_MAP_DELALLOC)
1715 last_end = min_t(u64, last_end, inode->i_size);
1716 free_extent_map(em);
1717 }
1718
1719 while (1) {
1720 em = btrfs_get_extent_fiemap(inode, NULL, 0, start, len, 0);
1721 if (IS_ERR(em)) {
1722 ret = -ENXIO;
1723 break;
1724 }
1725
1726 if (em->block_start == EXTENT_MAP_HOLE) {
1727 if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
1728 if (last_end <= orig_start) {
1729 free_extent_map(em);
1730 ret = -ENXIO;
1731 break;
1732 }
1733 }
1734
1735 if (origin == SEEK_HOLE) {
1736 *offset = start;
1737 free_extent_map(em);
1738 break;
1739 }
1740 } else {
1741 if (origin == SEEK_DATA) {
1742 if (em->block_start == EXTENT_MAP_DELALLOC) {
1743 if (start >= inode->i_size) {
1744 free_extent_map(em);
1745 ret = -ENXIO;
1746 break;
1747 }
1748 }
1749
1750 *offset = start;
1751 free_extent_map(em);
1752 break;
1753 }
1754 }
1755
1756 start = em->start + em->len;
1757 last_end = em->start + em->len;
1758
1759 if (em->block_start == EXTENT_MAP_DELALLOC)
1760 last_end = min_t(u64, last_end, inode->i_size);
1761
1762 if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
1763 free_extent_map(em);
1764 ret = -ENXIO;
1765 break;
1766 }
1767 free_extent_map(em);
1768 cond_resched();
1769 }
1770 if (!ret)
1771 *offset = min(*offset, inode->i_size);
1772out:
1773 unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
1774 &cached_state, GFP_NOFS);
1775 return ret;
1776}
1777
1778static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int origin)
1779{
1780 struct inode *inode = file->f_mapping->host;
1781 int ret;
1782
1783 mutex_lock(&inode->i_mutex);
1784 switch (origin) {
1785 case SEEK_END:
1786 case SEEK_CUR:
1787 offset = generic_file_llseek_unlocked(file, offset, origin);
1788 goto out;
1789 case SEEK_DATA:
1790 case SEEK_HOLE:
1791 ret = find_desired_extent(inode, &offset, origin);
1792 if (ret) {
1793 mutex_unlock(&inode->i_mutex);
1794 return ret;
1795 }
1796 }
1797
1798 if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
1799 return -EINVAL;
1800 if (offset > inode->i_sb->s_maxbytes)
1801 return -EINVAL;
1802
1803 /* Special lock needed here? */
1804 if (offset != file->f_pos) {
1805 file->f_pos = offset;
1806 file->f_version = 0;
1807 }
1808out:
1809 mutex_unlock(&inode->i_mutex);
1810 return offset;
1811}
1812
1667const struct file_operations btrfs_file_operations = { 1813const struct file_operations btrfs_file_operations = {
1668 .llseek = generic_file_llseek, 1814 .llseek = btrfs_file_llseek,
1669 .read = do_sync_read, 1815 .read = do_sync_read,
1670 .write = do_sync_write, 1816 .write = do_sync_write,
1671 .aio_read = generic_file_aio_read, 1817 .aio_read = generic_file_aio_read,