xref: /kernel/linux/linux-5.10/mm/msync.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	linux/mm/msync.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1994-1999  Linus Torvalds
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * The msync() system call.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/mman.h>
148c2ecf20Sopenharmony_ci#include <linux/file.h>
158c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * MS_SYNC syncs the entire file - including mappings.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
228c2ecf20Sopenharmony_ci * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
238c2ecf20Sopenharmony_ci * Now it doesn't do anything, since dirty pages are properly tracked.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * The application may now run fsync() to
268c2ecf20Sopenharmony_ci * write out the dirty pages and wait on the writeout and check the result.
278c2ecf20Sopenharmony_ci * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
288c2ecf20Sopenharmony_ci * async writeout immediately.
298c2ecf20Sopenharmony_ci * So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
308c2ecf20Sopenharmony_ci * applications.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	unsigned long end;
358c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->mm;
368c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
378c2ecf20Sopenharmony_ci	int unmapped_error = 0;
388c2ecf20Sopenharmony_ci	int error = -EINVAL;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	start = untagged_addr(start);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
438c2ecf20Sopenharmony_ci		goto out;
448c2ecf20Sopenharmony_ci	if (offset_in_page(start))
458c2ecf20Sopenharmony_ci		goto out;
468c2ecf20Sopenharmony_ci	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
478c2ecf20Sopenharmony_ci		goto out;
488c2ecf20Sopenharmony_ci	error = -ENOMEM;
498c2ecf20Sopenharmony_ci	len = (len + ~PAGE_MASK) & PAGE_MASK;
508c2ecf20Sopenharmony_ci	end = start + len;
518c2ecf20Sopenharmony_ci	if (end < start)
528c2ecf20Sopenharmony_ci		goto out;
538c2ecf20Sopenharmony_ci	error = 0;
548c2ecf20Sopenharmony_ci	if (end == start)
558c2ecf20Sopenharmony_ci		goto out;
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * If the interval [start,end) covers some unmapped address ranges,
588c2ecf20Sopenharmony_ci	 * just ignore them, but return -ENOMEM at the end.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
618c2ecf20Sopenharmony_ci	vma = find_vma(mm, start);
628c2ecf20Sopenharmony_ci	for (;;) {
638c2ecf20Sopenharmony_ci		struct file *file;
648c2ecf20Sopenharmony_ci		loff_t fstart, fend;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		/* Still start < end. */
678c2ecf20Sopenharmony_ci		error = -ENOMEM;
688c2ecf20Sopenharmony_ci		if (!vma)
698c2ecf20Sopenharmony_ci			goto out_unlock;
708c2ecf20Sopenharmony_ci		/* Here start < vma->vm_end. */
718c2ecf20Sopenharmony_ci		if (start < vma->vm_start) {
728c2ecf20Sopenharmony_ci			start = vma->vm_start;
738c2ecf20Sopenharmony_ci			if (start >= end)
748c2ecf20Sopenharmony_ci				goto out_unlock;
758c2ecf20Sopenharmony_ci			unmapped_error = -ENOMEM;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci		/* Here vma->vm_start <= start < vma->vm_end. */
788c2ecf20Sopenharmony_ci		if ((flags & MS_INVALIDATE) &&
798c2ecf20Sopenharmony_ci				(vma->vm_flags & VM_LOCKED)) {
808c2ecf20Sopenharmony_ci			error = -EBUSY;
818c2ecf20Sopenharmony_ci			goto out_unlock;
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci		file = vma->vm_file;
848c2ecf20Sopenharmony_ci		fstart = (start - vma->vm_start) +
858c2ecf20Sopenharmony_ci			 ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
868c2ecf20Sopenharmony_ci		fend = fstart + (min(end, vma->vm_end) - start) - 1;
878c2ecf20Sopenharmony_ci		start = vma->vm_end;
888c2ecf20Sopenharmony_ci		if ((flags & MS_SYNC) && file &&
898c2ecf20Sopenharmony_ci				(vma->vm_flags & VM_SHARED)) {
908c2ecf20Sopenharmony_ci			get_file(file);
918c2ecf20Sopenharmony_ci			mmap_read_unlock(mm);
928c2ecf20Sopenharmony_ci			error = vfs_fsync_range(file, fstart, fend, 1);
938c2ecf20Sopenharmony_ci			fput(file);
948c2ecf20Sopenharmony_ci			if (error || start >= end)
958c2ecf20Sopenharmony_ci				goto out;
968c2ecf20Sopenharmony_ci			mmap_read_lock(mm);
978c2ecf20Sopenharmony_ci			vma = find_vma(mm, start);
988c2ecf20Sopenharmony_ci		} else {
998c2ecf20Sopenharmony_ci			if (start >= end) {
1008c2ecf20Sopenharmony_ci				error = 0;
1018c2ecf20Sopenharmony_ci				goto out_unlock;
1028c2ecf20Sopenharmony_ci			}
1038c2ecf20Sopenharmony_ci			vma = vma->vm_next;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ciout_unlock:
1078c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
1088c2ecf20Sopenharmony_ciout:
1098c2ecf20Sopenharmony_ci	return error ? : unmapped_error;
1108c2ecf20Sopenharmony_ci}
111