| From: Hsin-Yi Wang <hsinyi@chromium.org> |
| Subject: squashfs: implement readahead |
| Date: Fri, 17 Jun 2022 16:38:13 +0800 |
| |
| Implement readahead callback for squashfs. It will read datablocks which |
| cover pages in readahead request. For a few cases it will not mark page |
| as uptodate, including: |
| |
| - file end is 0. |
| - zero filled blocks. |
| - current batch of pages isn't in the same datablock. |
| - decompressor error. |
| |
| Otherwise pages will be marked as uptodate. The unhandled pages will be |
| updated by readpage later. |
| |
| Link: https://lkml.kernel.org/r/20220617083810.337573-4-hsinyi@chromium.org |
| Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org> |
| Suggested-by: Matthew Wilcox <willy@infradead.org> |
| Reported-by: Matthew Wilcox <willy@infradead.org> |
| Reported-by: Phillip Lougher <phillip@squashfs.org.uk> |
| Reported-by: Xiongwei Song <Xiongwei.Song@windriver.com> |
| Reported-by: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Hou Tao <houtao1@huawei.com> |
| Cc: kernel test robot <lkp@intel.com> |
| Cc: Marek Szyprowski <m.szyprowski@samsung.com> |
| Cc: Miao Xie <miaoxie@huawei.com> |
| Cc: Zhang Yi <yi.zhang@huawei.com> |
| Cc: Zheng Liang <zhengliang6@huawei.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| fs/squashfs/file.c | 92 ++++++++++++++++++++++++++++++++++++++++++- |
| 1 file changed, 91 insertions(+), 1 deletion(-) |
| |
| --- a/fs/squashfs/file.c~squashfs-implement-readahead |
| +++ a/fs/squashfs/file.c |
| @@ -39,6 +39,7 @@ |
| #include "squashfs_fs_sb.h" |
| #include "squashfs_fs_i.h" |
| #include "squashfs.h" |
| +#include "page_actor.h" |
| |
| /* |
| * Locate cache slot in range [offset, index] for specified inode. If |
| @@ -495,7 +496,96 @@ out: |
| return 0; |
| } |
| |
| +static void squashfs_readahead(struct readahead_control *ractl) |
| +{ |
| + struct inode *inode = ractl->mapping->host; |
| + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
| + size_t mask = (1UL << msblk->block_log) - 1; |
| + unsigned short shift = msblk->block_log - PAGE_SHIFT; |
| + loff_t start = readahead_pos(ractl) & ~mask; |
| + size_t len = readahead_length(ractl) + readahead_pos(ractl) - start; |
| + struct squashfs_page_actor *actor; |
| + unsigned int nr_pages = 0; |
| + struct page **pages; |
| + int i, file_end = i_size_read(inode) >> msblk->block_log; |
| + unsigned int max_pages = 1UL << shift; |
| + |
| + readahead_expand(ractl, start, (len | mask) + 1); |
| + |
| + if (file_end == 0) |
| + return; |
| + |
| + pages = kmalloc_array(max_pages, sizeof(void *), GFP_KERNEL); |
| + if (!pages) |
| + return; |
| + |
| + for (;;) { |
| + pgoff_t index; |
| + int res, bsize; |
| + u64 block = 0; |
| + unsigned int expected; |
| + |
| + nr_pages = __readahead_batch(ractl, pages, max_pages); |
| + if (!nr_pages) |
| + break; |
| + |
| + if (readahead_pos(ractl) >= i_size_read(inode)) |
| + goto skip_pages; |
| + |
| + index = pages[0]->index >> shift; |
| + if ((pages[nr_pages - 1]->index >> shift) != index) |
| + goto skip_pages; |
| + |
| + expected = index == file_end ? |
| + (i_size_read(inode) & (msblk->block_size - 1)) : |
| + msblk->block_size; |
| + |
| + bsize = read_blocklist(inode, index, &block); |
| + if (bsize == 0) |
| + goto skip_pages; |
| + |
| + actor = squashfs_page_actor_init_special(msblk, pages, nr_pages, |
| + expected); |
| + if (!actor) |
| + goto skip_pages; |
| + |
| + res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); |
| + |
| + kfree(actor); |
| + |
| + if (res == expected) { |
| + int bytes; |
| + |
| + /* Last page (if present) may have trailing bytes not filled */ |
| + bytes = res % PAGE_SIZE; |
| + if (pages[nr_pages - 1]->index == file_end && bytes) |
| + memzero_page(pages[nr_pages - 1], bytes, |
| + PAGE_SIZE - bytes); |
| + |
| + for (i = 0; i < nr_pages; i++) { |
| + flush_dcache_page(pages[i]); |
| + SetPageUptodate(pages[i]); |
| + } |
| + } |
| + |
| + for (i = 0; i < nr_pages; i++) { |
| + unlock_page(pages[i]); |
| + put_page(pages[i]); |
| + } |
| + } |
| + |
| + kfree(pages); |
| + return; |
| + |
| +skip_pages: |
| + for (i = 0; i < nr_pages; i++) { |
| + unlock_page(pages[i]); |
| + put_page(pages[i]); |
| + } |
| + kfree(pages); |
| +} |
| |
| const struct address_space_operations squashfs_aops = { |
| - .read_folio = squashfs_read_folio |
| + .read_folio = squashfs_read_folio, |
| + .readahead = squashfs_readahead |
| }; |
| _ |