diff options
author | Hugh Dickins <hughd@google.com> | 2012-12-12 16:52:21 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 20:38:35 -0500 |
commit | 220f2ac91353dd8b239b70c4b4cf1615b80c1ff5 (patch) | |
tree | a7698dff6855f4ffa0f732a92bf1ec54c519ad87 /mm/shmem.c | |
parent | 01cefaef40c48c714b7476e62781230993e10dbf (diff) |
tmpfs: support SEEK_DATA and SEEK_HOLE (reprise)
Revert 3.5's commit f21f8062201f ("tmpfs: revert SEEK_DATA and
SEEK_HOLE") to reinstate 4fb5ef089b28 ("tmpfs: support SEEK_DATA and
SEEK_HOLE"), with the intervening additional arg to
generic_file_llseek_size().
In 3.8, ext4 is expected to join btrfs, ocfs2 and xfs with proper
SEEK_DATA and SEEK_HOLE support; and a good case has now been made for
it on tmpfs, so let's join the party.
It's quite easy for tmpfs to scan the radix_tree to support llseek's new
SEEK_DATA and SEEK_HOLE options: so add them while the minutiae are
still on my mind (in particular, the !PageUptodate-ness of pages
fallocated but still unwritten).
[akpm@linux-foundation.org: fix warning with CONFIG_TMPFS=n]
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Jaegeuk Hanse <jaegeuk.hanse@gmail.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Cc: Zheng Liu <wenqing.lz@taobao.com>
Cc: Jeff liu <jeff.liu@oracle.com>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Josef Bacik <josef@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Andreas Dilger <adilger@dilger.ca>
Cc: Marco Stornelli <marco.stornelli@gmail.com>
Cc: Chris Mason <chris.mason@fusionio.com>
Cc: Sunil Mushran <sunil.mushran@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 50c5b8f3a359..03f9ba8fb8e5 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -1715,6 +1715,96 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, | |||
1715 | return error; | 1715 | return error; |
1716 | } | 1716 | } |
1717 | 1717 | ||
1718 | /* | ||
1719 | * llseek SEEK_DATA or SEEK_HOLE through the radix_tree. | ||
1720 | */ | ||
1721 | static pgoff_t shmem_seek_hole_data(struct address_space *mapping, | ||
1722 | pgoff_t index, pgoff_t end, int origin) | ||
1723 | { | ||
1724 | struct page *page; | ||
1725 | struct pagevec pvec; | ||
1726 | pgoff_t indices[PAGEVEC_SIZE]; | ||
1727 | bool done = false; | ||
1728 | int i; | ||
1729 | |||
1730 | pagevec_init(&pvec, 0); | ||
1731 | pvec.nr = 1; /* start small: we may be there already */ | ||
1732 | while (!done) { | ||
1733 | pvec.nr = shmem_find_get_pages_and_swap(mapping, index, | ||
1734 | pvec.nr, pvec.pages, indices); | ||
1735 | if (!pvec.nr) { | ||
1736 | if (origin == SEEK_DATA) | ||
1737 | index = end; | ||
1738 | break; | ||
1739 | } | ||
1740 | for (i = 0; i < pvec.nr; i++, index++) { | ||
1741 | if (index < indices[i]) { | ||
1742 | if (origin == SEEK_HOLE) { | ||
1743 | done = true; | ||
1744 | break; | ||
1745 | } | ||
1746 | index = indices[i]; | ||
1747 | } | ||
1748 | page = pvec.pages[i]; | ||
1749 | if (page && !radix_tree_exceptional_entry(page)) { | ||
1750 | if (!PageUptodate(page)) | ||
1751 | page = NULL; | ||
1752 | } | ||
1753 | if (index >= end || | ||
1754 | (page && origin == SEEK_DATA) || | ||
1755 | (!page && origin == SEEK_HOLE)) { | ||
1756 | done = true; | ||
1757 | break; | ||
1758 | } | ||
1759 | } | ||
1760 | shmem_deswap_pagevec(&pvec); | ||
1761 | pagevec_release(&pvec); | ||
1762 | pvec.nr = PAGEVEC_SIZE; | ||
1763 | cond_resched(); | ||
1764 | } | ||
1765 | return index; | ||
1766 | } | ||
1767 | |||
1768 | static loff_t shmem_file_llseek(struct file *file, loff_t offset, int origin) | ||
1769 | { | ||
1770 | struct address_space *mapping = file->f_mapping; | ||
1771 | struct inode *inode = mapping->host; | ||
1772 | pgoff_t start, end; | ||
1773 | loff_t new_offset; | ||
1774 | |||
1775 | if (origin != SEEK_DATA && origin != SEEK_HOLE) | ||
1776 | return generic_file_llseek_size(file, offset, origin, | ||
1777 | MAX_LFS_FILESIZE, i_size_read(inode)); | ||
1778 | mutex_lock(&inode->i_mutex); | ||
1779 | /* We're holding i_mutex so we can access i_size directly */ | ||
1780 | |||
1781 | if (offset < 0) | ||
1782 | offset = -EINVAL; | ||
1783 | else if (offset >= inode->i_size) | ||
1784 | offset = -ENXIO; | ||
1785 | else { | ||
1786 | start = offset >> PAGE_CACHE_SHIFT; | ||
1787 | end = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
1788 | new_offset = shmem_seek_hole_data(mapping, start, end, origin); | ||
1789 | new_offset <<= PAGE_CACHE_SHIFT; | ||
1790 | if (new_offset > offset) { | ||
1791 | if (new_offset < inode->i_size) | ||
1792 | offset = new_offset; | ||
1793 | else if (origin == SEEK_DATA) | ||
1794 | offset = -ENXIO; | ||
1795 | else | ||
1796 | offset = inode->i_size; | ||
1797 | } | ||
1798 | } | ||
1799 | |||
1800 | if (offset >= 0 && offset != file->f_pos) { | ||
1801 | file->f_pos = offset; | ||
1802 | file->f_version = 0; | ||
1803 | } | ||
1804 | mutex_unlock(&inode->i_mutex); | ||
1805 | return offset; | ||
1806 | } | ||
1807 | |||
1718 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, | 1808 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, |
1719 | loff_t len) | 1809 | loff_t len) |
1720 | { | 1810 | { |
@@ -2586,7 +2676,7 @@ static const struct address_space_operations shmem_aops = { | |||
2586 | static const struct file_operations shmem_file_operations = { | 2676 | static const struct file_operations shmem_file_operations = { |
2587 | .mmap = shmem_mmap, | 2677 | .mmap = shmem_mmap, |
2588 | #ifdef CONFIG_TMPFS | 2678 | #ifdef CONFIG_TMPFS |
2589 | .llseek = generic_file_llseek, | 2679 | .llseek = shmem_file_llseek, |
2590 | .read = do_sync_read, | 2680 | .read = do_sync_read, |
2591 | .write = do_sync_write, | 2681 | .write = do_sync_write, |
2592 | .aio_read = shmem_file_aio_read, | 2682 | .aio_read = shmem_file_aio_read, |