18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 HUAWEI, Inc. 48c2ecf20Sopenharmony_ci * https://www.huawei.com/ 58c2ecf20Sopenharmony_ci * Created by Gao Xiang <gaoxiang25@huawei.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "internal.h" 88c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <trace/events/erofs.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic void erofs_readendio(struct bio *bio) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct bio_vec *bvec; 158c2ecf20Sopenharmony_ci blk_status_t err = bio->bi_status; 168c2ecf20Sopenharmony_ci struct bvec_iter_all iter_all; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci bio_for_each_segment_all(bvec, bio, iter_all) { 198c2ecf20Sopenharmony_ci struct page *page = bvec->bv_page; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci /* page is already locked */ 228c2ecf20Sopenharmony_ci DBG_BUGON(PageUptodate(page)); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (err) 258c2ecf20Sopenharmony_ci SetPageError(page); 268c2ecf20Sopenharmony_ci else 278c2ecf20Sopenharmony_ci SetPageUptodate(page); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci unlock_page(page); 308c2ecf20Sopenharmony_ci /* page could be reclaimed now */ 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci bio_put(bio); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct page *erofs_get_meta_page(struct super_block *sb, erofs_blk_t blkaddr) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct address_space *const mapping = sb->s_bdev->bd_inode->i_mapping; 388c2ecf20Sopenharmony_ci struct page *page; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci page = read_cache_page_gfp(mapping, blkaddr, 418c2ecf20Sopenharmony_ci mapping_gfp_constraint(mapping, ~__GFP_FS)); 428c2ecf20Sopenharmony_ci /* should already be PageUptodate */ 438c2ecf20Sopenharmony_ci if (!IS_ERR(page)) 448c2ecf20Sopenharmony_ci lock_page(page); 458c2ecf20Sopenharmony_ci return page; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int erofs_map_blocks_flatmode(struct inode *inode, 498c2ecf20Sopenharmony_ci struct erofs_map_blocks *map, 508c2ecf20Sopenharmony_ci int flags) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int err = 0; 538c2ecf20Sopenharmony_ci erofs_blk_t nblocks, lastblk; 548c2ecf20Sopenharmony_ci u64 offset = map->m_la; 558c2ecf20Sopenharmony_ci struct erofs_inode *vi = EROFS_I(inode); 568c2ecf20Sopenharmony_ci bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci trace_erofs_map_blocks_flatmode_enter(inode, map, flags); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE); 618c2ecf20Sopenharmony_ci lastblk = nblocks - tailendpacking; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (offset >= inode->i_size) { 648c2ecf20Sopenharmony_ci /* leave out-of-bound access unmapped */ 658c2ecf20Sopenharmony_ci map->m_flags = 0; 668c2ecf20Sopenharmony_ci map->m_plen = 0; 678c2ecf20Sopenharmony_ci goto out; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* there is no hole in flatmode */ 718c2ecf20Sopenharmony_ci map->m_flags = EROFS_MAP_MAPPED; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (offset < blknr_to_addr(lastblk)) { 748c2ecf20Sopenharmony_ci map->m_pa = blknr_to_addr(vi->raw_blkaddr) + map->m_la; 758c2ecf20Sopenharmony_ci map->m_plen = blknr_to_addr(lastblk) - offset; 768c2ecf20Sopenharmony_ci } else if (tailendpacking) { 778c2ecf20Sopenharmony_ci /* 2 - inode inline B: inode, [xattrs], inline last blk... */ 788c2ecf20Sopenharmony_ci struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci map->m_pa = iloc(sbi, vi->nid) + vi->inode_isize + 818c2ecf20Sopenharmony_ci vi->xattr_isize + erofs_blkoff(map->m_la); 828c2ecf20Sopenharmony_ci map->m_plen = inode->i_size - offset; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* inline data should be located in one meta block */ 858c2ecf20Sopenharmony_ci if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) { 868c2ecf20Sopenharmony_ci erofs_err(inode->i_sb, 878c2ecf20Sopenharmony_ci "inline data cross block boundary @ nid %llu", 888c2ecf20Sopenharmony_ci vi->nid); 898c2ecf20Sopenharmony_ci DBG_BUGON(1); 908c2ecf20Sopenharmony_ci err = -EFSCORRUPTED; 918c2ecf20Sopenharmony_ci goto err_out; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci map->m_flags |= EROFS_MAP_META; 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci erofs_err(inode->i_sb, 978c2ecf20Sopenharmony_ci "internal error @ nid: %llu (size %llu), m_la 0x%llx", 988c2ecf20Sopenharmony_ci vi->nid, inode->i_size, map->m_la); 998c2ecf20Sopenharmony_ci DBG_BUGON(1); 1008c2ecf20Sopenharmony_ci err = -EIO; 1018c2ecf20Sopenharmony_ci goto err_out; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciout: 1058c2ecf20Sopenharmony_ci map->m_llen = map->m_plen; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cierr_out: 1088c2ecf20Sopenharmony_ci trace_erofs_map_blocks_flatmode_exit(inode, map, flags, 0); 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ciint erofs_map_blocks(struct inode *inode, 1138c2ecf20Sopenharmony_ci struct erofs_map_blocks *map, int flags) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout)) { 1168c2ecf20Sopenharmony_ci int err = z_erofs_map_blocks_iter(inode, map, flags); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (map->mpage) { 1198c2ecf20Sopenharmony_ci put_page(map->mpage); 1208c2ecf20Sopenharmony_ci map->mpage = NULL; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci return erofs_map_blocks_flatmode(inode, map, flags); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline struct bio *erofs_read_raw_page(struct bio *bio, 1288c2ecf20Sopenharmony_ci struct address_space *mapping, 1298c2ecf20Sopenharmony_ci struct page *page, 1308c2ecf20Sopenharmony_ci erofs_off_t *last_block, 1318c2ecf20Sopenharmony_ci unsigned int nblocks, 1328c2ecf20Sopenharmony_ci bool ra) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct inode *const inode = mapping->host; 1358c2ecf20Sopenharmony_ci struct super_block *const sb = inode->i_sb; 1368c2ecf20Sopenharmony_ci erofs_off_t current_block = (erofs_off_t)page->index; 1378c2ecf20Sopenharmony_ci int err; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci DBG_BUGON(!nblocks); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (PageUptodate(page)) { 1428c2ecf20Sopenharmony_ci err = 0; 1438c2ecf20Sopenharmony_ci goto has_updated; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* note that for readpage case, bio also equals to NULL */ 1478c2ecf20Sopenharmony_ci if (bio && 1488c2ecf20Sopenharmony_ci /* not continuous */ 1498c2ecf20Sopenharmony_ci *last_block + 1 != current_block) { 1508c2ecf20Sopenharmony_cisubmit_bio_retry: 1518c2ecf20Sopenharmony_ci submit_bio(bio); 1528c2ecf20Sopenharmony_ci bio = NULL; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!bio) { 1568c2ecf20Sopenharmony_ci struct erofs_map_blocks map = { 1578c2ecf20Sopenharmony_ci .m_la = blknr_to_addr(current_block), 1588c2ecf20Sopenharmony_ci }; 1598c2ecf20Sopenharmony_ci erofs_blk_t blknr; 1608c2ecf20Sopenharmony_ci unsigned int blkoff; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW); 1638c2ecf20Sopenharmony_ci if (err) 1648c2ecf20Sopenharmony_ci goto err_out; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* zero out the holed page */ 1678c2ecf20Sopenharmony_ci if (!(map.m_flags & EROFS_MAP_MAPPED)) { 1688c2ecf20Sopenharmony_ci zero_user_segment(page, 0, PAGE_SIZE); 1698c2ecf20Sopenharmony_ci SetPageUptodate(page); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* imply err = 0, see erofs_map_blocks */ 1728c2ecf20Sopenharmony_ci goto has_updated; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* for RAW access mode, m_plen must be equal to m_llen */ 1768c2ecf20Sopenharmony_ci DBG_BUGON(map.m_plen != map.m_llen); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci blknr = erofs_blknr(map.m_pa); 1798c2ecf20Sopenharmony_ci blkoff = erofs_blkoff(map.m_pa); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* deal with inline page */ 1828c2ecf20Sopenharmony_ci if (map.m_flags & EROFS_MAP_META) { 1838c2ecf20Sopenharmony_ci void *vsrc, *vto; 1848c2ecf20Sopenharmony_ci struct page *ipage; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci DBG_BUGON(map.m_plen > PAGE_SIZE); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ipage = erofs_get_meta_page(inode->i_sb, blknr); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (IS_ERR(ipage)) { 1918c2ecf20Sopenharmony_ci err = PTR_ERR(ipage); 1928c2ecf20Sopenharmony_ci goto err_out; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci vsrc = kmap_atomic(ipage); 1968c2ecf20Sopenharmony_ci vto = kmap_atomic(page); 1978c2ecf20Sopenharmony_ci memcpy(vto, vsrc + blkoff, map.m_plen); 1988c2ecf20Sopenharmony_ci memset(vto + map.m_plen, 0, PAGE_SIZE - map.m_plen); 1998c2ecf20Sopenharmony_ci kunmap_atomic(vto); 2008c2ecf20Sopenharmony_ci kunmap_atomic(vsrc); 2018c2ecf20Sopenharmony_ci flush_dcache_page(page); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci SetPageUptodate(page); 2048c2ecf20Sopenharmony_ci /* TODO: could we unlock the page earlier? */ 2058c2ecf20Sopenharmony_ci unlock_page(ipage); 2068c2ecf20Sopenharmony_ci put_page(ipage); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* imply err = 0, see erofs_map_blocks */ 2098c2ecf20Sopenharmony_ci goto has_updated; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* pa must be block-aligned for raw reading */ 2138c2ecf20Sopenharmony_ci DBG_BUGON(erofs_blkoff(map.m_pa)); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* max # of continuous pages */ 2168c2ecf20Sopenharmony_ci if (nblocks > DIV_ROUND_UP(map.m_plen, PAGE_SIZE)) 2178c2ecf20Sopenharmony_ci nblocks = DIV_ROUND_UP(map.m_plen, PAGE_SIZE); 2188c2ecf20Sopenharmony_ci if (nblocks > BIO_MAX_PAGES) 2198c2ecf20Sopenharmony_ci nblocks = BIO_MAX_PAGES; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci bio = bio_alloc(GFP_NOIO, nblocks); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci bio->bi_end_io = erofs_readendio; 2248c2ecf20Sopenharmony_ci bio_set_dev(bio, sb->s_bdev); 2258c2ecf20Sopenharmony_ci bio->bi_iter.bi_sector = (sector_t)blknr << 2268c2ecf20Sopenharmony_ci LOG_SECTORS_PER_BLOCK; 2278c2ecf20Sopenharmony_ci bio->bi_opf = REQ_OP_READ | (ra ? REQ_RAHEAD : 0); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci err = bio_add_page(bio, page, PAGE_SIZE, 0); 2318c2ecf20Sopenharmony_ci /* out of the extent or bio is full */ 2328c2ecf20Sopenharmony_ci if (err < PAGE_SIZE) 2338c2ecf20Sopenharmony_ci goto submit_bio_retry; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci *last_block = current_block; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* shift in advance in case of it followed by too many gaps */ 2388c2ecf20Sopenharmony_ci if (bio->bi_iter.bi_size >= bio->bi_max_vecs * PAGE_SIZE) { 2398c2ecf20Sopenharmony_ci /* err should reassign to 0 after submitting */ 2408c2ecf20Sopenharmony_ci err = 0; 2418c2ecf20Sopenharmony_ci goto submit_bio_out; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return bio; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cierr_out: 2478c2ecf20Sopenharmony_ci /* for sync reading, set page error immediately */ 2488c2ecf20Sopenharmony_ci if (!ra) { 2498c2ecf20Sopenharmony_ci SetPageError(page); 2508c2ecf20Sopenharmony_ci ClearPageUptodate(page); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_cihas_updated: 2538c2ecf20Sopenharmony_ci unlock_page(page); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* if updated manually, continuous pages has a gap */ 2568c2ecf20Sopenharmony_ci if (bio) 2578c2ecf20Sopenharmony_cisubmit_bio_out: 2588c2ecf20Sopenharmony_ci submit_bio(bio); 2598c2ecf20Sopenharmony_ci return err ? ERR_PTR(err) : NULL; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * since we dont have write or truncate flows, so no inode 2648c2ecf20Sopenharmony_ci * locking needs to be held at the moment. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic int erofs_raw_access_readpage(struct file *file, struct page *page) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci erofs_off_t last_block; 2698c2ecf20Sopenharmony_ci struct bio *bio; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci trace_erofs_readpage(page, true); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci bio = erofs_read_raw_page(NULL, page->mapping, 2748c2ecf20Sopenharmony_ci page, &last_block, 1, false); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (IS_ERR(bio)) 2778c2ecf20Sopenharmony_ci return PTR_ERR(bio); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci DBG_BUGON(bio); /* since we have only one bio -- must be NULL */ 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void erofs_raw_access_readahead(struct readahead_control *rac) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci erofs_off_t last_block; 2868c2ecf20Sopenharmony_ci struct bio *bio = NULL; 2878c2ecf20Sopenharmony_ci struct page *page; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci trace_erofs_readpages(rac->mapping->host, readahead_index(rac), 2908c2ecf20Sopenharmony_ci readahead_count(rac), true); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci while ((page = readahead_page(rac))) { 2938c2ecf20Sopenharmony_ci prefetchw(&page->flags); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci bio = erofs_read_raw_page(bio, rac->mapping, page, &last_block, 2968c2ecf20Sopenharmony_ci readahead_count(rac), true); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* all the page errors are ignored when readahead */ 2998c2ecf20Sopenharmony_ci if (IS_ERR(bio)) { 3008c2ecf20Sopenharmony_ci pr_err("%s, readahead error at page %lu of nid %llu\n", 3018c2ecf20Sopenharmony_ci __func__, page->index, 3028c2ecf20Sopenharmony_ci EROFS_I(rac->mapping->host)->nid); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci bio = NULL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci put_page(page); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* the rare case (end in gaps) */ 3118c2ecf20Sopenharmony_ci if (bio) 3128c2ecf20Sopenharmony_ci submit_bio(bio); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic sector_t erofs_bmap(struct address_space *mapping, sector_t block) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct inode *inode = mapping->host; 3188c2ecf20Sopenharmony_ci struct erofs_map_blocks map = { 3198c2ecf20Sopenharmony_ci .m_la = blknr_to_addr(block), 3208c2ecf20Sopenharmony_ci }; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (EROFS_I(inode)->datalayout == EROFS_INODE_FLAT_INLINE) { 3238c2ecf20Sopenharmony_ci erofs_blk_t blks = i_size_read(inode) >> LOG_BLOCK_SIZE; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (block >> LOG_SECTORS_PER_BLOCK >= blks) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW)) 3308c2ecf20Sopenharmony_ci return erofs_blknr(map.m_pa); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* for uncompressed (aligned) files and raw access for other files */ 3368c2ecf20Sopenharmony_ciconst struct address_space_operations erofs_raw_access_aops = { 3378c2ecf20Sopenharmony_ci .readpage = erofs_raw_access_readpage, 3388c2ecf20Sopenharmony_ci .readahead = erofs_raw_access_readahead, 3398c2ecf20Sopenharmony_ci .bmap = erofs_bmap, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 342