1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
* fs/nfs/mmap.c by Jon Tombs 15 Aug 1993
*
* This code is from
* linux/mm/mmap.c which was written by obz, Linus and Eric
* and
* linux/mm/memory.c by Linus Torvalds and others
*
* Copyright (C) 1993
*
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/nfs_fs.h>
#include <asm/segment.h>
#include <asm/system.h>
extern int share_page(struct vm_area_struct * area, struct task_struct * tsk,
struct inode * inode, unsigned long address, unsigned long error_code,
unsigned long newpage);
extern unsigned long put_page(struct task_struct * tsk,unsigned long page,
unsigned long address,int prot);
static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
unsigned long address);
extern void file_mmap_free(struct vm_area_struct * area);
extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to,
unsigned long address);
struct vm_operations_struct nfs_file_mmap = {
NULL, /* open */
file_mmap_free, /* close */
nfs_file_mmap_nopage, /* nopage */
NULL, /* wppage */
file_mmap_share, /* share */
NULL, /* unmap */
};
/* This is used for a general mmap of a nfs file */
int nfs_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */
return -EINVAL;
if (off & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return -ENOMEM;
unmap_page_range(addr, len);
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = &nfs_file_mmap;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
}
static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
unsigned long address)
{
struct inode * inode = area->vm_inode;
unsigned int clear;
unsigned long page;
unsigned long tmp;
int n;
int i;
int pos;
struct nfs_fattr fattr;
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
page = get_free_page(GFP_KERNEL);
if (share_page(area, area->vm_task, inode, address, error_code, page)) {
++area->vm_task->min_flt;
return;
}
++area->vm_task->maj_flt;
if (!page) {
oom(current);
put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
return;
}
clear = 0;
if (address + PAGE_SIZE > area->vm_end) {
clear = address + PAGE_SIZE - area->vm_end;
}
n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
for (i = 0; i < (PAGE_SIZE - clear); i += n) {
int hunk, result;
hunk = PAGE_SIZE - i;
if (hunk > n)
hunk = n;
result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
pos, hunk, (char *) (page + i), &fattr);
if (result < 0)
break;
pos += result;
if (result < n) {
i += result;
break;
}
}
#ifdef doweneedthishere
nfs_refresh_inode(inode, &fattr);
#endif
if (!(error_code & PAGE_RW)) {
if (share_page(area, area->vm_task, inode, address, error_code, page))
return;
}
tmp = page + PAGE_SIZE;
while (clear--) {
*(char *)--tmp = 0;
}
if (put_page(area->vm_task,page,address,area->vm_page_prot))
return;
free_page(page);
oom(current);
}