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