vm: allow split of mem_anon_contig region
[minix3.git] / minix / servers / vm / mem_file.c
blobef25cff559dd6728941dff029c6004cccbc3fb4c
2 /* This file implements the methods of memory-mapped files. */
4 #include <assert.h>
6 #include "proto.h"
7 #include "vm.h"
8 #include "region.h"
9 #include "glo.h"
10 #include "cache.h"
12 /* These functions are static so as to not pollute the
13 * global namespace, and are accessed through their function
14 * pointers.
17 static void mappedfile_split(struct vmproc *vmp, struct vir_region *vr,
18 struct vir_region *r1, struct vir_region *r2);
19 static int mappedfile_unreference(struct phys_region *pr);
20 static int mappedfile_pagefault(struct vmproc *vmp, struct vir_region *region,
21 struct phys_region *ph, int write, vfs_callback_t callback, void *state,
22 int len, int *io);
23 static int mappedfile_sanitycheck(struct phys_region *pr, const char *file, int line);
24 static int mappedfile_writable(struct phys_region *pr);
25 static int mappedfile_copy(struct vir_region *vr, struct vir_region *newvr);
26 static int mappedfile_lowshrink(struct vir_region *vr, vir_bytes len);
27 static void mappedfile_delete(struct vir_region *region);
28 static int mappedfile_pt_flags(struct vir_region *vr);
30 struct mem_type mem_type_mappedfile = {
31 .name = "file-mapped memory",
32 .ev_unreference = mappedfile_unreference,
33 .ev_pagefault = mappedfile_pagefault,
34 .ev_sanitycheck = mappedfile_sanitycheck,
35 .ev_copy = mappedfile_copy,
36 .writable = mappedfile_writable,
37 .ev_split = mappedfile_split,
38 .ev_lowshrink = mappedfile_lowshrink,
39 .ev_delete = mappedfile_delete,
40 .pt_flags = mappedfile_pt_flags,
43 static int mappedfile_pt_flags(struct vir_region *vr){
44 #if defined(__arm__)
45 return ARM_VM_PTE_CACHED;
46 #else
47 return 0;
48 #endif
51 static int mappedfile_unreference(struct phys_region *pr)
53 assert(pr->ph->refcount == 0);
54 if(pr->ph->phys != MAP_NONE)
55 free_mem(ABS2CLICK(pr->ph->phys), 1);
56 return OK;
59 static int cow_block(struct vmproc *vmp, struct vir_region *region,
60 struct phys_region *ph, u16_t clearend)
62 int r;
64 if((r=mem_cow(region, ph, MAP_NONE, MAP_NONE)) != OK) {
65 printf("mappedfile_pagefault: COW failed\n");
66 return r;
69 /* After COW we are a normal piece of anonymous memory. */
70 ph->memtype = &mem_type_anon;
72 if(clearend) {
73 phys_bytes phaddr = ph->ph->phys, po = VM_PAGE_SIZE-clearend;
74 assert(clearend < VM_PAGE_SIZE);
75 phaddr += po;
76 if(sys_memset(NONE, 0, phaddr, clearend) != OK) {
77 panic("cow_block: clearend failed\n");
81 return OK;
84 static int mappedfile_pagefault(struct vmproc *vmp, struct vir_region *region,
85 struct phys_region *ph, int write, vfs_callback_t cb,
86 void *state, int statelen, int *io)
88 u32_t allocflags;
89 int procfd = region->param.file.fdref->fd;
91 allocflags = vrallocflags(region->flags);
93 assert(ph->ph->refcount > 0);
94 assert(region->param.file.inited);
95 assert(region->param.file.fdref);
96 assert(region->param.file.fdref->dev != NO_DEV);
98 /* Totally new block? Create it. */
99 if(ph->ph->phys == MAP_NONE) {
100 struct cached_page *cp;
101 u64_t referenced_offset =
102 region->param.file.offset + ph->offset;
103 if(region->param.file.fdref->ino == VMC_NO_INODE) {
104 cp = find_cached_page_bydev(region->param.file.fdref->dev,
105 referenced_offset, VMC_NO_INODE, 0, 1);
106 } else {
107 cp = find_cached_page_byino(region->param.file.fdref->dev,
108 region->param.file.fdref->ino, referenced_offset, 1);
110 if(cp) {
111 int result = OK;
112 pb_unreferenced(region, ph, 0);
113 pb_link(ph, cp->page, ph->offset, region);
115 if(roundup(ph->offset+region->param.file.clearend,
116 VM_PAGE_SIZE) >= region->length) {
117 result = cow_block(vmp, region, ph,
118 region->param.file.clearend);
119 } else if(result == OK && write) {
120 result = cow_block(vmp, region, ph, 0);
123 return result;
126 if(!cb) {
127 #if 0
128 printf("VM: mem_file: no callback, returning EFAULT\n");
129 #endif
130 sys_diagctl_stacktrace(vmp->vm_endpoint);
131 return EFAULT;
134 if(vfs_request(VMVFSREQ_FDIO, procfd, vmp, referenced_offset,
135 VM_PAGE_SIZE, cb, NULL, state, statelen) != OK) {
136 printf("VM: mappedfile_pagefault: vfs_request failed\n");
137 return ENOMEM;
139 *io = 1;
140 return SUSPEND;
143 if(!write) {
144 #if 0
145 printf("mappedfile_pagefault: nonwrite fault?\n");
146 #endif
147 return OK;
150 return cow_block(vmp, region, ph, 0);
153 static int mappedfile_sanitycheck(struct phys_region *pr, const char *file, int line)
155 MYASSERT(usedpages_add(pr->ph->phys, VM_PAGE_SIZE) == OK);
156 return OK;
159 static int mappedfile_writable(struct phys_region *pr)
161 /* We are never writable. */
162 return 0;
165 int mappedfile_copy(struct vir_region *vr, struct vir_region *newvr)
167 assert(vr->param.file.inited);
168 mappedfile_setfile(newvr->parent, newvr, vr->param.file.fdref->fd,
169 vr->param.file.offset,
170 vr->param.file.fdref->dev, vr->param.file.fdref->ino,
171 vr->param.file.clearend, 0, 0);
172 assert(newvr->param.file.inited);
174 return OK;
177 int mappedfile_setfile(struct vmproc *owner,
178 struct vir_region *region, int fd, u64_t offset,
179 dev_t dev, ino_t ino, u16_t clearend, int prefill, int mayclosefd)
181 vir_bytes vaddr;
182 struct fdref *newref;
184 newref = fdref_dedup_or_new(owner, ino, dev, fd, mayclosefd);
186 assert(newref);
187 assert(!region->param.file.inited);
188 assert(dev != NO_DEV);
189 fdref_ref(newref, region);
190 region->param.file.offset = offset;
191 region->param.file.clearend = clearend;
192 region->param.file.inited = 1;
194 if(!prefill) return OK;
196 for(vaddr = 0; vaddr < region->length; vaddr+=VM_PAGE_SIZE) {
197 struct cached_page *cp = NULL;
198 struct phys_region *pr;
199 u64_t referenced_offset = offset + vaddr;
201 if(roundup(vaddr+region->param.file.clearend,
202 VM_PAGE_SIZE) >= region->length) {
203 break;
206 if(ino == VMC_NO_INODE) {
207 cp = find_cached_page_bydev(dev, referenced_offset,
208 VMC_NO_INODE, 0, 1);
209 } else {
210 cp = find_cached_page_byino(dev, ino,
211 referenced_offset, 1);
213 if(!cp) continue;
214 if(!(pr = pb_reference(cp->page, vaddr, region,
215 &mem_type_mappedfile))) {
216 printf("mappedfile_setfile: pb_reference failed\n");
217 break;
219 if(map_ph_writept(region->parent, region, pr) != OK) {
220 printf("mappedfile_setfile: map_ph_writept failed\n");
221 break;
225 return OK;
228 static void mappedfile_split(struct vmproc *vmp, struct vir_region *vr,
229 struct vir_region *r1, struct vir_region *r2)
231 assert(!r1->param.file.inited);
232 assert(!r2->param.file.inited);
233 assert(vr->param.file.inited);
234 assert(r1->length + r2->length == vr->length);
235 assert(vr->def_memtype == &mem_type_mappedfile);
236 assert(r1->def_memtype == &mem_type_mappedfile);
237 assert(r2->def_memtype == &mem_type_mappedfile);
239 r1->param.file = vr->param.file;
240 r2->param.file = vr->param.file;
242 fdref_ref(vr->param.file.fdref, r1);
243 fdref_ref(vr->param.file.fdref, r2);
245 r1->param.file.clearend = 0;
246 r2->param.file.offset += r1->length;
248 assert(r1->param.file.inited);
249 assert(r2->param.file.inited);
252 static int mappedfile_lowshrink(struct vir_region *vr, vir_bytes len)
254 assert(vr->param.file.inited);
255 vr->param.file.offset += len;
256 return OK;
259 static void mappedfile_delete(struct vir_region *region)
261 assert(region->def_memtype == &mem_type_mappedfile);
262 assert(region->param.file.inited);
263 assert(region->param.file.fdref);
264 fdref_deref(region);
265 region->param.file.inited = 0;