18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* file-nommu.c: no-MMU version of ramfs 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/fs.h> 108c2ecf20Sopenharmony_ci#include <linux/mm.h> 118c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 128c2ecf20Sopenharmony_ci#include <linux/highmem.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/backing-dev.h> 168c2ecf20Sopenharmony_ci#include <linux/ramfs.h> 178c2ecf20Sopenharmony_ci#include <linux/pagevec.h> 188c2ecf20Sopenharmony_ci#include <linux/mman.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include "internal.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int ramfs_nommu_setattr(struct dentry *, struct iattr *); 268c2ecf20Sopenharmony_cistatic unsigned long ramfs_nommu_get_unmapped_area(struct file *file, 278c2ecf20Sopenharmony_ci unsigned long addr, 288c2ecf20Sopenharmony_ci unsigned long len, 298c2ecf20Sopenharmony_ci unsigned long pgoff, 308c2ecf20Sopenharmony_ci unsigned long flags); 318c2ecf20Sopenharmony_cistatic int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic unsigned ramfs_mmap_capabilities(struct file *file) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return NOMMU_MAP_DIRECT | NOMMU_MAP_COPY | NOMMU_MAP_READ | 368c2ecf20Sopenharmony_ci NOMMU_MAP_WRITE | NOMMU_MAP_EXEC; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciconst struct file_operations ramfs_file_operations = { 408c2ecf20Sopenharmony_ci .mmap_capabilities = ramfs_mmap_capabilities, 418c2ecf20Sopenharmony_ci .mmap = ramfs_nommu_mmap, 428c2ecf20Sopenharmony_ci .get_unmapped_area = ramfs_nommu_get_unmapped_area, 438c2ecf20Sopenharmony_ci .read_iter = generic_file_read_iter, 448c2ecf20Sopenharmony_ci .write_iter = generic_file_write_iter, 458c2ecf20Sopenharmony_ci .fsync = noop_fsync, 468c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 478c2ecf20Sopenharmony_ci .splice_write = iter_file_splice_write, 488c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciconst struct inode_operations ramfs_file_inode_operations = { 528c2ecf20Sopenharmony_ci .setattr = ramfs_nommu_setattr, 538c2ecf20Sopenharmony_ci .getattr = simple_getattr, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/*****************************************************************************/ 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * add a contiguous set of pages into a ramfs inode when it's truncated from 598c2ecf20Sopenharmony_ci * size 0 on the assumption that it's going to be used for an mmap of shared 608c2ecf20Sopenharmony_ci * memory 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ciint ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned long npages, xpages, loop; 658c2ecf20Sopenharmony_ci struct page *pages; 668c2ecf20Sopenharmony_ci unsigned order; 678c2ecf20Sopenharmony_ci void *data; 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci gfp_t gfp = mapping_gfp_mask(inode->i_mapping); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* make various checks */ 728c2ecf20Sopenharmony_ci order = get_order(newsize); 738c2ecf20Sopenharmony_ci if (unlikely(order >= MAX_ORDER)) 748c2ecf20Sopenharmony_ci return -EFBIG; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ret = inode_newsize_ok(inode, newsize); 778c2ecf20Sopenharmony_ci if (ret) 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci i_size_write(inode, newsize); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* allocate enough contiguous pages to be able to satisfy the 838c2ecf20Sopenharmony_ci * request */ 848c2ecf20Sopenharmony_ci pages = alloc_pages(gfp, order); 858c2ecf20Sopenharmony_ci if (!pages) 868c2ecf20Sopenharmony_ci return -ENOMEM; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* split the high-order page into an array of single pages */ 898c2ecf20Sopenharmony_ci xpages = 1UL << order; 908c2ecf20Sopenharmony_ci npages = (newsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci split_page(pages, order); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* trim off any pages we don't actually require */ 958c2ecf20Sopenharmony_ci for (loop = npages; loop < xpages; loop++) 968c2ecf20Sopenharmony_ci __free_page(pages + loop); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* clear the memory we allocated */ 998c2ecf20Sopenharmony_ci newsize = PAGE_SIZE * npages; 1008c2ecf20Sopenharmony_ci data = page_address(pages); 1018c2ecf20Sopenharmony_ci memset(data, 0, newsize); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* attach all the pages to the inode's address space */ 1048c2ecf20Sopenharmony_ci for (loop = 0; loop < npages; loop++) { 1058c2ecf20Sopenharmony_ci struct page *page = pages + loop; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = add_to_page_cache_lru(page, inode->i_mapping, loop, 1088c2ecf20Sopenharmony_ci gfp); 1098c2ecf20Sopenharmony_ci if (ret < 0) 1108c2ecf20Sopenharmony_ci goto add_error; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* prevent the page from being discarded on memory pressure */ 1138c2ecf20Sopenharmony_ci SetPageDirty(page); 1148c2ecf20Sopenharmony_ci SetPageUptodate(page); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci unlock_page(page); 1178c2ecf20Sopenharmony_ci put_page(page); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciadd_error: 1238c2ecf20Sopenharmony_ci while (loop < npages) 1248c2ecf20Sopenharmony_ci __free_page(pages + loop++); 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/*****************************************************************************/ 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* assume a truncate from zero size is going to be for the purposes of 1378c2ecf20Sopenharmony_ci * shared mmap */ 1388c2ecf20Sopenharmony_ci if (size == 0) { 1398c2ecf20Sopenharmony_ci if (unlikely(newsize >> 32)) 1408c2ecf20Sopenharmony_ci return -EFBIG; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ramfs_nommu_expand_for_mapping(inode, newsize); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* check that a decrease in size doesn't cut off any shared mappings */ 1468c2ecf20Sopenharmony_ci if (newsize < size) { 1478c2ecf20Sopenharmony_ci ret = nommu_shrink_inode_mappings(inode, size, newsize); 1488c2ecf20Sopenharmony_ci if (ret < 0) 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci truncate_setsize(inode, newsize); 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/*****************************************************************************/ 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * handle a change of attributes 1598c2ecf20Sopenharmony_ci * - we're specifically interested in a change of size 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 1648c2ecf20Sopenharmony_ci unsigned int old_ia_valid = ia->ia_valid; 1658c2ecf20Sopenharmony_ci int ret = 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* POSIX UID/GID verification for setting inode attributes */ 1688c2ecf20Sopenharmony_ci ret = setattr_prepare(dentry, ia); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* pick out size-changing events */ 1738c2ecf20Sopenharmony_ci if (ia->ia_valid & ATTR_SIZE) { 1748c2ecf20Sopenharmony_ci loff_t size = inode->i_size; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (ia->ia_size != size) { 1778c2ecf20Sopenharmony_ci ret = ramfs_nommu_resize(inode, ia->ia_size, size); 1788c2ecf20Sopenharmony_ci if (ret < 0 || ia->ia_valid == ATTR_SIZE) 1798c2ecf20Sopenharmony_ci goto out; 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci /* we skipped the truncate but must still update 1828c2ecf20Sopenharmony_ci * timestamps 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci ia->ia_valid |= ATTR_MTIME|ATTR_CTIME; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci setattr_copy(inode, ia); 1898c2ecf20Sopenharmony_ci out: 1908c2ecf20Sopenharmony_ci ia->ia_valid = old_ia_valid; 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/*****************************************************************************/ 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * try to determine where a shared mapping can be made 1978c2ecf20Sopenharmony_ci * - we require that: 1988c2ecf20Sopenharmony_ci * - the pages to be mapped must exist 1998c2ecf20Sopenharmony_ci * - the pages be physically contiguous in sequence 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic unsigned long ramfs_nommu_get_unmapped_area(struct file *file, 2028c2ecf20Sopenharmony_ci unsigned long addr, unsigned long len, 2038c2ecf20Sopenharmony_ci unsigned long pgoff, unsigned long flags) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci unsigned long maxpages, lpages, nr, loop, ret; 2068c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 2078c2ecf20Sopenharmony_ci struct page **pages = NULL, **ptr, *page; 2088c2ecf20Sopenharmony_ci loff_t isize; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* the mapping mustn't extend beyond the EOF */ 2118c2ecf20Sopenharmony_ci lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; 2128c2ecf20Sopenharmony_ci isize = i_size_read(inode); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ret = -ENOSYS; 2158c2ecf20Sopenharmony_ci maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT; 2168c2ecf20Sopenharmony_ci if (pgoff >= maxpages) 2178c2ecf20Sopenharmony_ci goto out; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (maxpages - pgoff < lpages) 2208c2ecf20Sopenharmony_ci goto out; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* gang-find the pages */ 2238c2ecf20Sopenharmony_ci pages = kcalloc(lpages, sizeof(struct page *), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!pages) 2258c2ecf20Sopenharmony_ci goto out_free; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci nr = find_get_pages_contig(inode->i_mapping, pgoff, lpages, pages); 2288c2ecf20Sopenharmony_ci if (nr != lpages) 2298c2ecf20Sopenharmony_ci goto out_free_pages; /* leave if some pages were missing */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* check the pages for physical adjacency */ 2328c2ecf20Sopenharmony_ci ptr = pages; 2338c2ecf20Sopenharmony_ci page = *ptr++; 2348c2ecf20Sopenharmony_ci page++; 2358c2ecf20Sopenharmony_ci for (loop = lpages; loop > 1; loop--) 2368c2ecf20Sopenharmony_ci if (*ptr++ != page++) 2378c2ecf20Sopenharmony_ci goto out_free_pages; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* okay - all conditions fulfilled */ 2408c2ecf20Sopenharmony_ci ret = (unsigned long) page_address(pages[0]); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciout_free_pages: 2438c2ecf20Sopenharmony_ci ptr = pages; 2448c2ecf20Sopenharmony_ci for (loop = nr; loop > 0; loop--) 2458c2ecf20Sopenharmony_ci put_page(*ptr++); 2468c2ecf20Sopenharmony_ciout_free: 2478c2ecf20Sopenharmony_ci kfree(pages); 2488c2ecf20Sopenharmony_ciout: 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/*****************************************************************************/ 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * set up a mapping for shared memory segments 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) 2598c2ecf20Sopenharmony_ci return -ENOSYS; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci file_accessed(file); 2628c2ecf20Sopenharmony_ci vma->vm_ops = &generic_file_vm_ops; 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 265