xref: /openbmc/linux/mm/msync.c (revision 4267d1fd)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	linux/mm/msync.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1994-1999  Linus Torvalds
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds /*
91da177e4SLinus Torvalds  * The msync() system call.
101da177e4SLinus Torvalds  */
118f2e9f15SAndrew Morton #include <linux/fs.h>
121da177e4SLinus Torvalds #include <linux/mm.h>
131da177e4SLinus Torvalds #include <linux/mman.h>
149c50823eSAndrew Morton #include <linux/file.h>
151da177e4SLinus Torvalds #include <linux/syscalls.h>
16e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds /*
191da177e4SLinus Torvalds  * MS_SYNC syncs the entire file - including mappings.
201da177e4SLinus Torvalds  *
21204ec841SPeter Zijlstra  * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
22204ec841SPeter Zijlstra  * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
23204ec841SPeter Zijlstra  * Now it doesn't do anything, since dirty pages are properly tracked.
24204ec841SPeter Zijlstra  *
25204ec841SPeter Zijlstra  * The application may now run fsync() to
261da177e4SLinus Torvalds  * write out the dirty pages and wait on the writeout and check the result.
271da177e4SLinus Torvalds  * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
281da177e4SLinus Torvalds  * async writeout immediately.
2916538c40SAmos Waterland  * So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
301da177e4SLinus Torvalds  * applications.
311da177e4SLinus Torvalds  */
SYSCALL_DEFINE3(msync,unsigned long,start,size_t,len,int,flags)326a6160a7SHeiko Carstens SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	unsigned long end;
35204ec841SPeter Zijlstra 	struct mm_struct *mm = current->mm;
361da177e4SLinus Torvalds 	struct vm_area_struct *vma;
37676758bdSAndrew Morton 	int unmapped_error = 0;
38676758bdSAndrew Morton 	int error = -EINVAL;
391da177e4SLinus Torvalds 
40057d3389SAndrey Konovalov 	start = untagged_addr(start);
41057d3389SAndrey Konovalov 
421da177e4SLinus Torvalds 	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
431da177e4SLinus Torvalds 		goto out;
44b0d61c7eSAlexander Kuleshov 	if (offset_in_page(start))
451da177e4SLinus Torvalds 		goto out;
461da177e4SLinus Torvalds 	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
471da177e4SLinus Torvalds 		goto out;
481da177e4SLinus Torvalds 	error = -ENOMEM;
491da177e4SLinus Torvalds 	len = (len + ~PAGE_MASK) & PAGE_MASK;
501da177e4SLinus Torvalds 	end = start + len;
511da177e4SLinus Torvalds 	if (end < start)
521da177e4SLinus Torvalds 		goto out;
531da177e4SLinus Torvalds 	error = 0;
541da177e4SLinus Torvalds 	if (end == start)
551da177e4SLinus Torvalds 		goto out;
561da177e4SLinus Torvalds 	/*
571da177e4SLinus Torvalds 	 * If the interval [start,end) covers some unmapped address ranges,
58f6899bc0SNikita Ermakov 	 * just ignore them, but return -ENOMEM at the end. Besides, if the
59f6899bc0SNikita Ermakov 	 * flag is MS_ASYNC (w/o MS_INVALIDATE) the result would be -ENOMEM
60f6899bc0SNikita Ermakov 	 * anyway and there is nothing left to do, so return immediately.
611da177e4SLinus Torvalds 	 */
62d8ed45c5SMichel Lespinasse 	mmap_read_lock(mm);
63204ec841SPeter Zijlstra 	vma = find_vma(mm, start);
64204ec841SPeter Zijlstra 	for (;;) {
659c50823eSAndrew Morton 		struct file *file;
667fc34a62SMatthew Wilcox 		loff_t fstart, fend;
679c50823eSAndrew Morton 
68204ec841SPeter Zijlstra 		/* Still start < end. */
69204ec841SPeter Zijlstra 		error = -ENOMEM;
70204ec841SPeter Zijlstra 		if (!vma)
71204ec841SPeter Zijlstra 			goto out_unlock;
721da177e4SLinus Torvalds 		/* Here start < vma->vm_end. */
731da177e4SLinus Torvalds 		if (start < vma->vm_start) {
74f6899bc0SNikita Ermakov 			if (flags == MS_ASYNC)
75f6899bc0SNikita Ermakov 				goto out_unlock;
761da177e4SLinus Torvalds 			start = vma->vm_start;
77204ec841SPeter Zijlstra 			if (start >= end)
78204ec841SPeter Zijlstra 				goto out_unlock;
79204ec841SPeter Zijlstra 			unmapped_error = -ENOMEM;
801da177e4SLinus Torvalds 		}
811da177e4SLinus Torvalds 		/* Here vma->vm_start <= start < vma->vm_end. */
82204ec841SPeter Zijlstra 		if ((flags & MS_INVALIDATE) &&
83204ec841SPeter Zijlstra 				(vma->vm_flags & VM_LOCKED)) {
84204ec841SPeter Zijlstra 			error = -EBUSY;
859c50823eSAndrew Morton 			goto out_unlock;
869c50823eSAndrew Morton 		}
879c50823eSAndrew Morton 		file = vma->vm_file;
88496a8e68SNamjae Jeon 		fstart = (start - vma->vm_start) +
89496a8e68SNamjae Jeon 			 ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
907fc34a62SMatthew Wilcox 		fend = fstart + (min(end, vma->vm_end) - start) - 1;
911da177e4SLinus Torvalds 		start = vma->vm_end;
92204ec841SPeter Zijlstra 		if ((flags & MS_SYNC) && file &&
93707c21c8SAndrew Morton 				(vma->vm_flags & VM_SHARED)) {
94707c21c8SAndrew Morton 			get_file(file);
95d8ed45c5SMichel Lespinasse 			mmap_read_unlock(mm);
967fc34a62SMatthew Wilcox 			error = vfs_fsync_range(file, fstart, fend, 1);
97707c21c8SAndrew Morton 			fput(file);
98204ec841SPeter Zijlstra 			if (error || start >= end)
99204ec841SPeter Zijlstra 				goto out;
100d8ed45c5SMichel Lespinasse 			mmap_read_lock(mm);
101204ec841SPeter Zijlstra 			vma = find_vma(mm, start);
1029c50823eSAndrew Morton 		} else {
103204ec841SPeter Zijlstra 			if (start >= end) {
104204ec841SPeter Zijlstra 				error = 0;
105204ec841SPeter Zijlstra 				goto out_unlock;
106204ec841SPeter Zijlstra 			}
107*4267d1fdSLiam R. Howlett 			vma = find_vma(mm, vma->vm_end);
1081da177e4SLinus Torvalds 		}
109204ec841SPeter Zijlstra 	}
1109c50823eSAndrew Morton out_unlock:
111d8ed45c5SMichel Lespinasse 	mmap_read_unlock(mm);
1129c50823eSAndrew Morton out:
113204ec841SPeter Zijlstra 	return error ? : unmapped_error;
1141da177e4SLinus Torvalds }
115