1/*
2 *	linux/mm/msync.c
3 *
4 * Copyright (C) 1994-1999  Linus Torvalds
5 */
6
7/*
8 * The msync() system call.
9 */
10#include <linux/fs.h>
11#include <linux/mm.h>
12#include <linux/mman.h>
13#include <linux/file.h>
14#include <linux/syscalls.h>
15#include <linux/sched.h>
16
17/*
18 * MS_SYNC syncs the entire file - including mappings.
19 *
20 * MS_ASYNC does not start I/O (it used to, up to 2.5.67).
21 * Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
22 * Now it doesn't do anything, since dirty pages are properly tracked.
23 *
24 * The application may now run fsync() to
25 * write out the dirty pages and wait on the writeout and check the result.
26 * Or the application may run fadvise(FADV_DONTNEED) against the fd to start
27 * async writeout immediately.
28 * So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
29 * applications.
30 */
31SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
32{
33	unsigned long end;
34	struct mm_struct *mm = current->mm;
35	struct vm_area_struct *vma;
36	int unmapped_error = 0;
37	int error = -EINVAL;
38
39	if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
40		goto out;
41	if (offset_in_page(start))
42		goto out;
43	if ((flags & MS_ASYNC) && (flags & MS_SYNC))
44		goto out;
45	error = -ENOMEM;
46	len = (len + ~PAGE_MASK) & PAGE_MASK;
47	end = start + len;
48	if (end < start)
49		goto out;
50	error = 0;
51	if (end == start)
52		goto out;
53	/*
54	 * If the interval [start,end) covers some unmapped address ranges,
55	 * just ignore them, but return -ENOMEM at the end.
56	 */
57	down_read(&mm->mmap_sem);
58	vma = find_vma(mm, start);
59	for (;;) {
60		struct file *file;
61		loff_t fstart, fend;
62
63		/* Still start < end. */
64		error = -ENOMEM;
65		if (!vma)
66			goto out_unlock;
67		/* Here start < vma->vm_end. */
68		if (start < vma->vm_start) {
69			start = vma->vm_start;
70			if (start >= end)
71				goto out_unlock;
72			unmapped_error = -ENOMEM;
73		}
74		/* Here vma->vm_start <= start < vma->vm_end. */
75		if ((flags & MS_INVALIDATE) &&
76				(vma->vm_flags & VM_LOCKED)) {
77			error = -EBUSY;
78			goto out_unlock;
79		}
80		file = vma->vm_file;
81		fstart = (start - vma->vm_start) +
82			 ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
83		fend = fstart + (min(end, vma->vm_end) - start) - 1;
84		start = vma->vm_end;
85		if ((flags & MS_SYNC) && file &&
86				(vma->vm_flags & VM_SHARED)) {
87			get_file(file);
88			up_read(&mm->mmap_sem);
89			error = vfs_fsync_range(file, fstart, fend, 1);
90			fput(file);
91			if (error || start >= end)
92				goto out;
93			down_read(&mm->mmap_sem);
94			vma = find_vma(mm, start);
95		} else {
96			if (start >= end) {
97				error = 0;
98				goto out_unlock;
99			}
100			vma = vma->vm_next;
101		}
102	}
103out_unlock:
104	up_read(&mm->mmap_sem);
105out:
106	return error ? : unmapped_error;
107}
108