162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	linux/mm/msync.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1994-1999  Linus Torvalds
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * The msync() system call.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/mm.h>
1362306a36Sopenharmony_ci#include <linux/mman.h>
1462306a36Sopenharmony_ci#include <linux/file.h>
1562306a36Sopenharmony_ci#include <linux/syscalls.h>
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * MS_SYNC syncs the entire file - including mappings.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
2262306a36Sopenharmony_ci * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
2362306a36Sopenharmony_ci * Now it doesn't do anything, since dirty pages are properly tracked.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * The application may now run fsync() to
2662306a36Sopenharmony_ci * write out the dirty pages and wait on the writeout and check the result.
2762306a36Sopenharmony_ci * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
2862306a36Sopenharmony_ci * async writeout immediately.
2962306a36Sopenharmony_ci * So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
3062306a36Sopenharmony_ci * applications.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ciSYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	unsigned long end;
3562306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
3662306a36Sopenharmony_ci	struct vm_area_struct *vma;
3762306a36Sopenharmony_ci	int unmapped_error = 0;
3862306a36Sopenharmony_ci	int error = -EINVAL;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	start = untagged_addr(start);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
4362306a36Sopenharmony_ci		goto out;
4462306a36Sopenharmony_ci	if (offset_in_page(start))
4562306a36Sopenharmony_ci		goto out;
4662306a36Sopenharmony_ci	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
4762306a36Sopenharmony_ci		goto out;
4862306a36Sopenharmony_ci	error = -ENOMEM;
4962306a36Sopenharmony_ci	len = (len + ~PAGE_MASK) & PAGE_MASK;
5062306a36Sopenharmony_ci	end = start + len;
5162306a36Sopenharmony_ci	if (end < start)
5262306a36Sopenharmony_ci		goto out;
5362306a36Sopenharmony_ci	error = 0;
5462306a36Sopenharmony_ci	if (end == start)
5562306a36Sopenharmony_ci		goto out;
5662306a36Sopenharmony_ci	/*
5762306a36Sopenharmony_ci	 * If the interval [start,end) covers some unmapped address ranges,
5862306a36Sopenharmony_ci	 * just ignore them, but return -ENOMEM at the end. Besides, if the
5962306a36Sopenharmony_ci	 * flag is MS_ASYNC (w/o MS_INVALIDATE) the result would be -ENOMEM
6062306a36Sopenharmony_ci	 * anyway and there is nothing left to do, so return immediately.
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	mmap_read_lock(mm);
6362306a36Sopenharmony_ci	vma = find_vma(mm, start);
6462306a36Sopenharmony_ci	for (;;) {
6562306a36Sopenharmony_ci		struct file *file;
6662306a36Sopenharmony_ci		loff_t fstart, fend;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		/* Still start < end. */
6962306a36Sopenharmony_ci		error = -ENOMEM;
7062306a36Sopenharmony_ci		if (!vma)
7162306a36Sopenharmony_ci			goto out_unlock;
7262306a36Sopenharmony_ci		/* Here start < vma->vm_end. */
7362306a36Sopenharmony_ci		if (start < vma->vm_start) {
7462306a36Sopenharmony_ci			if (flags == MS_ASYNC)
7562306a36Sopenharmony_ci				goto out_unlock;
7662306a36Sopenharmony_ci			start = vma->vm_start;
7762306a36Sopenharmony_ci			if (start >= end)
7862306a36Sopenharmony_ci				goto out_unlock;
7962306a36Sopenharmony_ci			unmapped_error = -ENOMEM;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci		/* Here vma->vm_start <= start < vma->vm_end. */
8262306a36Sopenharmony_ci		if ((flags & MS_INVALIDATE) &&
8362306a36Sopenharmony_ci				(vma->vm_flags & VM_LOCKED)) {
8462306a36Sopenharmony_ci			error = -EBUSY;
8562306a36Sopenharmony_ci			goto out_unlock;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		file = vma->vm_file;
8862306a36Sopenharmony_ci		fstart = (start - vma->vm_start) +
8962306a36Sopenharmony_ci			 ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
9062306a36Sopenharmony_ci		fend = fstart + (min(end, vma->vm_end) - start) - 1;
9162306a36Sopenharmony_ci		start = vma->vm_end;
9262306a36Sopenharmony_ci		if ((flags & MS_SYNC) && file &&
9362306a36Sopenharmony_ci				(vma->vm_flags & VM_SHARED)) {
9462306a36Sopenharmony_ci			get_file(file);
9562306a36Sopenharmony_ci			mmap_read_unlock(mm);
9662306a36Sopenharmony_ci			error = vfs_fsync_range(file, fstart, fend, 1);
9762306a36Sopenharmony_ci			fput(file);
9862306a36Sopenharmony_ci			if (error || start >= end)
9962306a36Sopenharmony_ci				goto out;
10062306a36Sopenharmony_ci			mmap_read_lock(mm);
10162306a36Sopenharmony_ci			vma = find_vma(mm, start);
10262306a36Sopenharmony_ci		} else {
10362306a36Sopenharmony_ci			if (start >= end) {
10462306a36Sopenharmony_ci				error = 0;
10562306a36Sopenharmony_ci				goto out_unlock;
10662306a36Sopenharmony_ci			}
10762306a36Sopenharmony_ci			vma = find_vma(mm, vma->vm_end);
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ciout_unlock:
11162306a36Sopenharmony_ci	mmap_read_unlock(mm);
11262306a36Sopenharmony_ciout:
11362306a36Sopenharmony_ci	return error ? : unmapped_error;
11462306a36Sopenharmony_ci}
115