diff options
author | Hugh Dickins <hughd@google.com> | 2012-05-29 18:06:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-29 19:22:23 -0400 |
commit | 4fb5ef089b288942c6fc3f85c4ecb4016c1aa4c3 (patch) | |
tree | aff66acd4b8a0e8cc060f5bf20ad3d6d75bdb7ab /mm | |
parent | 1aac1400319d30786f32b9290e9cc923937b3d57 (diff) |
tmpfs: support SEEK_DATA and SEEK_HOLE
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).
But I don't know who actually uses SEEK_DATA or SEEK_HOLE, and whether it
would be of any use to them on tmpfs. This code adds 92 lines and 752
bytes on x86_64 - is that bloat or worthwhile?
[akpm@linux-foundation.org: fix warning with CONFIG_TMPFS=n]
Signed-off-by: Hugh Dickins <hughd@google.com>
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: Dave Chinner <david@fromorbit.com>
Cc: Marco Stornelli <marco.stornelli@gmail.com>
Cc: Jeff liu <jeff.liu@oracle.com>
Cc: Chris Mason <chris.mason@oracle.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')
-rw-r--r-- | mm/shmem.c | 94 |
1 files changed, 93 insertions, 1 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 191663a8def3..d576b84d913c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -1674,6 +1674,98 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, | |||
1674 | return error; | 1674 | return error; |
1675 | } | 1675 | } |
1676 | 1676 | ||
1677 | /* | ||
1678 | * llseek SEEK_DATA or SEEK_HOLE through the radix_tree. | ||
1679 | */ | ||
1680 | static pgoff_t shmem_seek_hole_data(struct address_space *mapping, | ||
1681 | pgoff_t index, pgoff_t end, int origin) | ||
1682 | { | ||
1683 | struct page *page; | ||
1684 | struct pagevec pvec; | ||
1685 | pgoff_t indices[PAGEVEC_SIZE]; | ||
1686 | bool done = false; | ||
1687 | int i; | ||
1688 | |||
1689 | pagevec_init(&pvec, 0); | ||
1690 | pvec.nr = 1; /* start small: we may be there already */ | ||
1691 | while (!done) { | ||
1692 | pvec.nr = shmem_find_get_pages_and_swap(mapping, index, | ||
1693 | pvec.nr, pvec.pages, indices); | ||
1694 | if (!pvec.nr) { | ||
1695 | if (origin == SEEK_DATA) | ||
1696 | index = end; | ||
1697 | break; | ||
1698 | } | ||
1699 | for (i = 0; i < pvec.nr; i++, index++) { | ||
1700 | if (index < indices[i]) { | ||
1701 | if (origin == SEEK_HOLE) { | ||
1702 | done = true; | ||
1703 | break; | ||
1704 | } | ||
1705 | index = indices[i]; | ||
1706 | } | ||
1707 | page = pvec.pages[i]; | ||
1708 | if (page && !radix_tree_exceptional_entry(page)) { | ||
1709 | if (!PageUptodate(page)) | ||
1710 | page = NULL; | ||
1711 | } | ||
1712 | if (index >= end || | ||
1713 | (page && origin == SEEK_DATA) || | ||
1714 | (!page && origin == SEEK_HOLE)) { | ||
1715 | done = true; | ||
1716 | break; | ||
1717 | } | ||
1718 | } | ||
1719 | shmem_deswap_pagevec(&pvec); | ||
1720 | pagevec_release(&pvec); | ||
1721 | pvec.nr = PAGEVEC_SIZE; | ||
1722 | cond_resched(); | ||
1723 | } | ||
1724 | return index; | ||
1725 | } | ||
1726 | |||
1727 | static loff_t shmem_file_llseek(struct file *file, loff_t offset, int origin) | ||
1728 | { | ||
1729 | struct address_space *mapping; | ||
1730 | struct inode *inode; | ||
1731 | pgoff_t start, end; | ||
1732 | loff_t new_offset; | ||
1733 | |||
1734 | if (origin != SEEK_DATA && origin != SEEK_HOLE) | ||
1735 | return generic_file_llseek_size(file, offset, origin, | ||
1736 | MAX_LFS_FILESIZE); | ||
1737 | mapping = file->f_mapping; | ||
1738 | inode = mapping->host; | ||
1739 | mutex_lock(&inode->i_mutex); | ||
1740 | /* We're holding i_mutex so we can access i_size directly */ | ||
1741 | |||
1742 | if (offset < 0) | ||
1743 | offset = -EINVAL; | ||
1744 | else if (offset >= inode->i_size) | ||
1745 | offset = -ENXIO; | ||
1746 | else { | ||
1747 | start = offset >> PAGE_CACHE_SHIFT; | ||
1748 | end = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
1749 | new_offset = shmem_seek_hole_data(mapping, start, end, origin); | ||
1750 | new_offset <<= PAGE_CACHE_SHIFT; | ||
1751 | if (new_offset > offset) { | ||
1752 | if (new_offset < inode->i_size) | ||
1753 | offset = new_offset; | ||
1754 | else if (origin == SEEK_DATA) | ||
1755 | offset = -ENXIO; | ||
1756 | else | ||
1757 | offset = inode->i_size; | ||
1758 | } | ||
1759 | } | ||
1760 | |||
1761 | if (offset >= 0 && offset != file->f_pos) { | ||
1762 | file->f_pos = offset; | ||
1763 | file->f_version = 0; | ||
1764 | } | ||
1765 | mutex_unlock(&inode->i_mutex); | ||
1766 | return offset; | ||
1767 | } | ||
1768 | |||
1677 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, | 1769 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, |
1678 | loff_t len) | 1770 | loff_t len) |
1679 | { | 1771 | { |
@@ -2679,7 +2771,7 @@ static const struct address_space_operations shmem_aops = { | |||
2679 | static const struct file_operations shmem_file_operations = { | 2771 | static const struct file_operations shmem_file_operations = { |
2680 | .mmap = shmem_mmap, | 2772 | .mmap = shmem_mmap, |
2681 | #ifdef CONFIG_TMPFS | 2773 | #ifdef CONFIG_TMPFS |
2682 | .llseek = generic_file_llseek, | 2774 | .llseek = shmem_file_llseek, |
2683 | .read = do_sync_read, | 2775 | .read = do_sync_read, |
2684 | .write = do_sync_write, | 2776 | .write = do_sync_write, |
2685 | .aio_read = shmem_file_aio_read, | 2777 | .aio_read = shmem_file_aio_read, |