162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* file-nommu.c: no-MMU version of ramfs 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/pagemap.h> 1262306a36Sopenharmony_ci#include <linux/highmem.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/backing-dev.h> 1662306a36Sopenharmony_ci#include <linux/ramfs.h> 1762306a36Sopenharmony_ci#include <linux/pagevec.h> 1862306a36Sopenharmony_ci#include <linux/mman.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include "internal.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int ramfs_nommu_setattr(struct mnt_idmap *, struct dentry *, struct iattr *); 2662306a36Sopenharmony_cistatic unsigned long ramfs_nommu_get_unmapped_area(struct file *file, 2762306a36Sopenharmony_ci unsigned long addr, 2862306a36Sopenharmony_ci unsigned long len, 2962306a36Sopenharmony_ci unsigned long pgoff, 3062306a36Sopenharmony_ci unsigned long flags); 3162306a36Sopenharmony_cistatic int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic unsigned ramfs_mmap_capabilities(struct file *file) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return NOMMU_MAP_DIRECT | NOMMU_MAP_COPY | NOMMU_MAP_READ | 3662306a36Sopenharmony_ci NOMMU_MAP_WRITE | NOMMU_MAP_EXEC; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciconst struct file_operations ramfs_file_operations = { 4062306a36Sopenharmony_ci .mmap_capabilities = ramfs_mmap_capabilities, 4162306a36Sopenharmony_ci .mmap = ramfs_nommu_mmap, 4262306a36Sopenharmony_ci .get_unmapped_area = ramfs_nommu_get_unmapped_area, 4362306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 4462306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 4562306a36Sopenharmony_ci .fsync = noop_fsync, 4662306a36Sopenharmony_ci .splice_read = filemap_splice_read, 4762306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 4862306a36Sopenharmony_ci .llseek = generic_file_llseek, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciconst struct inode_operations ramfs_file_inode_operations = { 5262306a36Sopenharmony_ci .setattr = ramfs_nommu_setattr, 5362306a36Sopenharmony_ci .getattr = simple_getattr, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/*****************************************************************************/ 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * add a contiguous set of pages into a ramfs inode when it's truncated from 5962306a36Sopenharmony_ci * size 0 on the assumption that it's going to be used for an mmap of shared 6062306a36Sopenharmony_ci * memory 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ciint ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci unsigned long npages, xpages, loop; 6562306a36Sopenharmony_ci struct page *pages; 6662306a36Sopenharmony_ci unsigned order; 6762306a36Sopenharmony_ci void *data; 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci gfp_t gfp = mapping_gfp_mask(inode->i_mapping); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* make various checks */ 7262306a36Sopenharmony_ci order = get_order(newsize); 7362306a36Sopenharmony_ci if (unlikely(order > MAX_ORDER)) 7462306a36Sopenharmony_ci return -EFBIG; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = inode_newsize_ok(inode, newsize); 7762306a36Sopenharmony_ci if (ret) 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci i_size_write(inode, newsize); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* allocate enough contiguous pages to be able to satisfy the 8362306a36Sopenharmony_ci * request */ 8462306a36Sopenharmony_ci pages = alloc_pages(gfp, order); 8562306a36Sopenharmony_ci if (!pages) 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* split the high-order page into an array of single pages */ 8962306a36Sopenharmony_ci xpages = 1UL << order; 9062306a36Sopenharmony_ci npages = (newsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci split_page(pages, order); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* trim off any pages we don't actually require */ 9562306a36Sopenharmony_ci for (loop = npages; loop < xpages; loop++) 9662306a36Sopenharmony_ci __free_page(pages + loop); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* clear the memory we allocated */ 9962306a36Sopenharmony_ci newsize = PAGE_SIZE * npages; 10062306a36Sopenharmony_ci data = page_address(pages); 10162306a36Sopenharmony_ci memset(data, 0, newsize); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* attach all the pages to the inode's address space */ 10462306a36Sopenharmony_ci for (loop = 0; loop < npages; loop++) { 10562306a36Sopenharmony_ci struct page *page = pages + loop; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = add_to_page_cache_lru(page, inode->i_mapping, loop, 10862306a36Sopenharmony_ci gfp); 10962306a36Sopenharmony_ci if (ret < 0) 11062306a36Sopenharmony_ci goto add_error; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* prevent the page from being discarded on memory pressure */ 11362306a36Sopenharmony_ci SetPageDirty(page); 11462306a36Sopenharmony_ci SetPageUptodate(page); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci unlock_page(page); 11762306a36Sopenharmony_ci put_page(page); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciadd_error: 12362306a36Sopenharmony_ci while (loop < npages) 12462306a36Sopenharmony_ci __free_page(pages + loop++); 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/*****************************************************************************/ 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* assume a truncate from zero size is going to be for the purposes of 13762306a36Sopenharmony_ci * shared mmap */ 13862306a36Sopenharmony_ci if (size == 0) { 13962306a36Sopenharmony_ci if (unlikely(newsize >> 32)) 14062306a36Sopenharmony_ci return -EFBIG; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return ramfs_nommu_expand_for_mapping(inode, newsize); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* check that a decrease in size doesn't cut off any shared mappings */ 14662306a36Sopenharmony_ci if (newsize < size) { 14762306a36Sopenharmony_ci ret = nommu_shrink_inode_mappings(inode, size, newsize); 14862306a36Sopenharmony_ci if (ret < 0) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci truncate_setsize(inode, newsize); 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/*****************************************************************************/ 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * handle a change of attributes 15962306a36Sopenharmony_ci * - we're specifically interested in a change of size 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic int ramfs_nommu_setattr(struct mnt_idmap *idmap, 16262306a36Sopenharmony_ci struct dentry *dentry, struct iattr *ia) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 16562306a36Sopenharmony_ci unsigned int old_ia_valid = ia->ia_valid; 16662306a36Sopenharmony_ci int ret = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* POSIX UID/GID verification for setting inode attributes */ 16962306a36Sopenharmony_ci ret = setattr_prepare(&nop_mnt_idmap, dentry, ia); 17062306a36Sopenharmony_ci if (ret) 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* pick out size-changing events */ 17462306a36Sopenharmony_ci if (ia->ia_valid & ATTR_SIZE) { 17562306a36Sopenharmony_ci loff_t size = inode->i_size; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (ia->ia_size != size) { 17862306a36Sopenharmony_ci ret = ramfs_nommu_resize(inode, ia->ia_size, size); 17962306a36Sopenharmony_ci if (ret < 0 || ia->ia_valid == ATTR_SIZE) 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci /* we skipped the truncate but must still update 18362306a36Sopenharmony_ci * timestamps 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci ia->ia_valid |= ATTR_MTIME|ATTR_CTIME; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, ia); 19062306a36Sopenharmony_ci out: 19162306a36Sopenharmony_ci ia->ia_valid = old_ia_valid; 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/*****************************************************************************/ 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * try to determine where a shared mapping can be made 19862306a36Sopenharmony_ci * - we require that: 19962306a36Sopenharmony_ci * - the pages to be mapped must exist 20062306a36Sopenharmony_ci * - the pages be physically contiguous in sequence 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic unsigned long ramfs_nommu_get_unmapped_area(struct file *file, 20362306a36Sopenharmony_ci unsigned long addr, unsigned long len, 20462306a36Sopenharmony_ci unsigned long pgoff, unsigned long flags) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned long maxpages, lpages, nr_folios, loop, ret, nr_pages, pfn; 20762306a36Sopenharmony_ci struct inode *inode = file_inode(file); 20862306a36Sopenharmony_ci struct folio_batch fbatch; 20962306a36Sopenharmony_ci loff_t isize; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* the mapping mustn't extend beyond the EOF */ 21262306a36Sopenharmony_ci lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; 21362306a36Sopenharmony_ci isize = i_size_read(inode); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = -ENOSYS; 21662306a36Sopenharmony_ci maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT; 21762306a36Sopenharmony_ci if (pgoff >= maxpages) 21862306a36Sopenharmony_ci goto out; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (maxpages - pgoff < lpages) 22162306a36Sopenharmony_ci goto out; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* gang-find the pages */ 22462306a36Sopenharmony_ci folio_batch_init(&fbatch); 22562306a36Sopenharmony_ci nr_pages = 0; 22662306a36Sopenharmony_cirepeat: 22762306a36Sopenharmony_ci nr_folios = filemap_get_folios_contig(inode->i_mapping, &pgoff, 22862306a36Sopenharmony_ci ULONG_MAX, &fbatch); 22962306a36Sopenharmony_ci if (!nr_folios) { 23062306a36Sopenharmony_ci ret = -ENOSYS; 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (ret == -ENOSYS) { 23562306a36Sopenharmony_ci ret = (unsigned long) folio_address(fbatch.folios[0]); 23662306a36Sopenharmony_ci pfn = folio_pfn(fbatch.folios[0]); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci /* check the pages for physical adjacency */ 23962306a36Sopenharmony_ci for (loop = 0; loop < nr_folios; loop++) { 24062306a36Sopenharmony_ci if (pfn + nr_pages != folio_pfn(fbatch.folios[loop])) { 24162306a36Sopenharmony_ci ret = -ENOSYS; 24262306a36Sopenharmony_ci goto out_free; /* leave if not physical adjacent */ 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci nr_pages += folio_nr_pages(fbatch.folios[loop]); 24562306a36Sopenharmony_ci if (nr_pages >= lpages) 24662306a36Sopenharmony_ci goto out_free; /* successfully found desired pages*/ 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (nr_pages < lpages) { 25062306a36Sopenharmony_ci folio_batch_release(&fbatch); 25162306a36Sopenharmony_ci goto repeat; /* loop if pages are missing */ 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci /* okay - all conditions fulfilled */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciout_free: 25662306a36Sopenharmony_ci folio_batch_release(&fbatch); 25762306a36Sopenharmony_ciout: 25862306a36Sopenharmony_ci return ret; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/*****************************************************************************/ 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * set up a mapping for shared memory segments 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cistatic int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (!is_nommu_shared_mapping(vma->vm_flags)) 26862306a36Sopenharmony_ci return -ENOSYS; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci file_accessed(file); 27162306a36Sopenharmony_ci vma->vm_ops = &generic_file_vm_ops; 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 274