1 2 /* This file implements the methods of memory-mapped files. */ 3 4 #include <assert.h> 5 6 #include "proto.h" 7 #include "vm.h" 8 #include "region.h" 9 #include "glo.h" 10 #include "cache.h" 11 12 /* These functions are static so as to not pollute the 13 * global namespace, and are accessed through their function 14 * pointers. 15 */ 16 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); 29 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, 41 }; 42 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 49 } 50 51 static int mappedfile_unreference(struct phys_region *pr) 52 { 53 assert(pr->ph->refcount == 0); 54 if(pr->ph->phys != MAP_NONE) 55 free_mem(ABS2CLICK(pr->ph->phys), 1); 56 return OK; 57 } 58 59 static int cow_block(struct vmproc *vmp, struct vir_region *region, 60 struct phys_region *ph, u16_t clearend) 61 { 62 int r; 63 64 if((r=mem_cow(region, ph, MAP_NONE, MAP_NONE)) != OK) { 65 printf("mappedfile_pagefault: COW failed\n"); 66 return r; 67 } 68 69 /* After COW we are a normal piece of anonymous memory. */ 70 ph->memtype = &mem_type_anon; 71 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"); 78 } 79 } 80 81 return OK; 82 } 83 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) 87 { 88 u32_t allocflags; 89 int procfd = region->param.file.fdref->fd; 90 91 allocflags = vrallocflags(region->flags); 92 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); 97 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); 109 } 110 /* 111 * Normally, a cache hit saves a round-trip to the file system 112 * to load the page. However, if the page in the VM cache is 113 * marked for one-time use, then force a round-trip through the 114 * file system anyway, so that the FS can update the page by 115 * by readding it to the cache. Thus, for one-time use pages, 116 * no caching is performed. This approach is correct even in 117 * the light of concurrent requests and disappearing processes 118 * but relies on VM requests to VFS being fully serialized. 119 */ 120 if(cp && (!cb || !(cp->flags & VMSF_ONCE))) { 121 int result = OK; 122 pb_unreferenced(region, ph, 0); 123 pb_link(ph, cp->page, ph->offset, region); 124 125 if(roundup(ph->offset+region->param.file.clearend, 126 VM_PAGE_SIZE) >= region->length) { 127 result = cow_block(vmp, region, ph, 128 region->param.file.clearend); 129 } else if(result == OK && write) { 130 result = cow_block(vmp, region, ph, 0); 131 } 132 133 /* Discard one-use pages after mapping them in. */ 134 if (result == OK && (cp->flags & VMSF_ONCE)) 135 rmcache(cp); 136 137 return result; 138 } 139 140 if(!cb) { 141 #if 0 142 printf("VM: mem_file: no callback, returning EFAULT\n"); 143 sys_diagctl_stacktrace(vmp->vm_endpoint); 144 #endif 145 return EFAULT; 146 } 147 148 if(vfs_request(VMVFSREQ_FDIO, procfd, vmp, referenced_offset, 149 VM_PAGE_SIZE, cb, NULL, state, statelen) != OK) { 150 printf("VM: mappedfile_pagefault: vfs_request failed\n"); 151 return ENOMEM; 152 } 153 *io = 1; 154 return SUSPEND; 155 } 156 157 if(!write) { 158 #if 0 159 printf("mappedfile_pagefault: nonwrite fault?\n"); 160 #endif 161 return OK; 162 } 163 164 return cow_block(vmp, region, ph, 0); 165 } 166 167 static int mappedfile_sanitycheck(struct phys_region *pr, const char *file, int line) 168 { 169 MYASSERT(usedpages_add(pr->ph->phys, VM_PAGE_SIZE) == OK); 170 return OK; 171 } 172 173 static int mappedfile_writable(struct phys_region *pr) 174 { 175 /* We are never writable. */ 176 return 0; 177 } 178 179 int mappedfile_copy(struct vir_region *vr, struct vir_region *newvr) 180 { 181 assert(vr->param.file.inited); 182 mappedfile_setfile(newvr->parent, newvr, vr->param.file.fdref->fd, 183 vr->param.file.offset, 184 vr->param.file.fdref->dev, vr->param.file.fdref->ino, 185 vr->param.file.clearend, 0, 0); 186 assert(newvr->param.file.inited); 187 188 return OK; 189 } 190 191 int mappedfile_setfile(struct vmproc *owner, 192 struct vir_region *region, int fd, u64_t offset, 193 dev_t dev, ino_t ino, u16_t clearend, int prefill, int mayclosefd) 194 { 195 vir_bytes vaddr; 196 struct fdref *newref; 197 198 newref = fdref_dedup_or_new(owner, ino, dev, fd, mayclosefd); 199 200 assert(newref); 201 assert(!region->param.file.inited); 202 assert(dev != NO_DEV); 203 fdref_ref(newref, region); 204 region->param.file.offset = offset; 205 region->param.file.clearend = clearend; 206 region->param.file.inited = 1; 207 208 if(!prefill) return OK; 209 210 for(vaddr = 0; vaddr < region->length; vaddr+=VM_PAGE_SIZE) { 211 struct cached_page *cp = NULL; 212 struct phys_region *pr; 213 u64_t referenced_offset = offset + vaddr; 214 215 if(roundup(vaddr+region->param.file.clearend, 216 VM_PAGE_SIZE) >= region->length) { 217 break; 218 } 219 220 if(ino == VMC_NO_INODE) { 221 cp = find_cached_page_bydev(dev, referenced_offset, 222 VMC_NO_INODE, 0, 1); 223 } else { 224 cp = find_cached_page_byino(dev, ino, 225 referenced_offset, 1); 226 } 227 /* 228 * If we get a hit for a page that is to be used only once, 229 * then either we found a stale page (due to a process dying 230 * before a requested once-page could be mapped in) or this is 231 * a rare case of concurrent requests for the same page. In 232 * both cases, force the page to be obtained from its FS later. 233 */ 234 if(!cp || (cp->flags & VMSF_ONCE)) continue; 235 if(!(pr = pb_reference(cp->page, vaddr, region, 236 &mem_type_mappedfile))) { 237 printf("mappedfile_setfile: pb_reference failed\n"); 238 break; 239 } 240 if(map_ph_writept(region->parent, region, pr) != OK) { 241 printf("mappedfile_setfile: map_ph_writept failed\n"); 242 break; 243 } 244 } 245 246 return OK; 247 } 248 249 static void mappedfile_split(struct vmproc *vmp, struct vir_region *vr, 250 struct vir_region *r1, struct vir_region *r2) 251 { 252 assert(!r1->param.file.inited); 253 assert(!r2->param.file.inited); 254 assert(vr->param.file.inited); 255 assert(r1->length + r2->length == vr->length); 256 assert(vr->def_memtype == &mem_type_mappedfile); 257 assert(r1->def_memtype == &mem_type_mappedfile); 258 assert(r2->def_memtype == &mem_type_mappedfile); 259 260 r1->param.file = vr->param.file; 261 r2->param.file = vr->param.file; 262 263 fdref_ref(vr->param.file.fdref, r1); 264 fdref_ref(vr->param.file.fdref, r2); 265 266 r1->param.file.clearend = 0; 267 r2->param.file.offset += r1->length; 268 269 assert(r1->param.file.inited); 270 assert(r2->param.file.inited); 271 } 272 273 static int mappedfile_lowshrink(struct vir_region *vr, vir_bytes len) 274 { 275 assert(vr->param.file.inited); 276 vr->param.file.offset += len; 277 return OK; 278 } 279 280 static void mappedfile_delete(struct vir_region *region) 281 { 282 assert(region->def_memtype == &mem_type_mappedfile); 283 assert(region->param.file.inited); 284 assert(region->param.file.fdref); 285 fdref_deref(region); 286 region->param.file.inited = 0; 287 } 288