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