static void handle_memory_continue(struct vmproc *vmp, message *m,
void *arg, void *statearg);
-static int handle_memory_step(struct hm_state *hmstate);
+static int handle_memory_step(struct hm_state *hmstate, int retry);
static void handle_memory_final(struct hm_state *state, int result);
/*===========================================================================*
return;
}
- r = handle_memory_step(state);
+ r = handle_memory_step(state, TRUE /*retry*/);
assert(state->valid == VALID);
state.valid = VALID;
state.vfs_avail = vfs_avail;
- r = handle_memory_step(&state);
+ r = handle_memory_step(&state, FALSE /*retry*/);
if(r == SUSPEND) {
assert(caller != NONE);
}
}
-static int handle_memory_step(struct hm_state *hmstate)
+static int handle_memory_step(struct hm_state *hmstate, int retry)
{
struct vir_region *region;
+ vir_bytes offset, length, sublen;
+ int r;
/* Page-align memory and length. */
assert(hmstate);
assert(!(hmstate->len % VM_PAGE_SIZE));
while(hmstate->len > 0) {
- int r;
if(!(region = map_lookup(hmstate->vmp, hmstate->mem, NULL))) {
#if VERBOSE
map_printmap(hmstate->vmp);
printf("VM: do_memory: write to unwritable map\n");
#endif
return EFAULT;
- } else {
- vir_bytes offset, sublen;
- assert(region->vaddr <= hmstate->mem);
- assert(!(region->vaddr % VM_PAGE_SIZE));
- offset = hmstate->mem - region->vaddr;
- sublen = hmstate->len;
- if(offset + sublen > region->length)
- sublen = region->length - offset;
-
+ }
+
+ assert(region->vaddr <= hmstate->mem);
+ assert(!(region->vaddr % VM_PAGE_SIZE));
+ offset = hmstate->mem - region->vaddr;
+ length = hmstate->len;
+ if (offset + length > region->length)
+ length = region->length - offset;
+
+ /*
+ * Handle one page at a time. While it seems beneficial to
+ * handle multiple pages in one go, the opposite is true:
+ * map_handle_memory will handle one page at a time anyway, and
+ * if we give it the whole range multiple times, it will have
+ * to recheck pages it already handled. In addition, in order
+ * to handle one-shot pages, we need to know whether we are
+ * retrying a single page, and that is not possible if this is
+ * hidden in map_handle_memory.
+ */
+ while (length > 0) {
+ sublen = VM_PAGE_SIZE;
+
+ assert(sublen <= length);
+ assert(offset + sublen <= region->length);
+
+ /*
+ * Upon the second try for this range, do not allow
+ * calling into VFS again. This prevents eternal loops
+ * in case the FS messes up, and allows one-shot pages
+ * to be mapped in on the second call.
+ */
if((region->def_memtype == &mem_type_mappedfile &&
- !hmstate->vfs_avail) || hmstate->caller == NONE) {
- r = map_handle_memory(hmstate->vmp, region, offset,
- sublen, hmstate->wrflag, NULL, NULL, 0);
+ (!hmstate->vfs_avail || retry)) ||
+ hmstate->caller == NONE) {
+ r = map_handle_memory(hmstate->vmp, region,
+ offset, sublen, hmstate->wrflag, NULL,
+ NULL, 0);
assert(r != SUSPEND);
} else {
- r = map_handle_memory(hmstate->vmp, region, offset,
- sublen, hmstate->wrflag, handle_memory_continue,
- hmstate, sizeof(*hmstate));
+ r = map_handle_memory(hmstate->vmp, region,
+ offset, sublen, hmstate->wrflag,
+ handle_memory_continue, hmstate,
+ sizeof(*hmstate));
}
if(r != OK) return r;
hmstate->len -= sublen;
hmstate->mem += sublen;
+
+ offset += sublen;
+ length -= sublen;
+ retry = FALSE;
}
}