1/** 2 * \file 3 * \brief Creates mapping based on the MMU type 4 * 5 * If the MMU supports translations, then anonymous type is used 6 * (more efficient) else non contiguous memory is used. 7 */ 8 9/* 10 * Copyright (c) 2010, 2011, ETH Zurich. 11 * Copyright (c) 2014, HP Labs. 12 * All rights reserved. 13 * 14 * This file is distributed under the terms in the attached LICENSE file. 15 * If you do not find this file, copies can be found by writing to: 16 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group. 17 */ 18 19#include <barrelfish/barrelfish.h> 20#include <barrelfish/vspace_mmu_aware.h> 21#include <barrelfish/core_state.h> 22#include <string.h> 23 24/// Minimum free memory before we return it to memory server 25#define MIN_MEM_FOR_FREE (1 * 1024 * 1024) 26 27void vspace_mmu_aware_set_slot_alloc(struct vspace_mmu_aware *state, 28 struct slot_allocator *slot_allocator) 29{ 30 if (slot_allocator) { 31 state->slot_alloc = slot_allocator; 32 } else { 33 state->slot_alloc = get_default_slot_allocator(); 34 } 35} 36 37/** 38 * \brief Initialize vspace_mmu_aware struct 39 * 40 * \param state The struct to initialize 41 * \param init The buffer to use to initialize the struct 42 * \param size The size of anon memobj to create 43 * 44 * Initializes the struct according to the type of MMU 45 */ 46errval_t vspace_mmu_aware_init(struct vspace_mmu_aware *state, size_t size) 47{ 48 return vspace_mmu_aware_init_aligned(state, NULL, size, 0, 49 VREGION_FLAGS_READ_WRITE); 50} 51 52errval_t vspace_mmu_aware_init_aligned(struct vspace_mmu_aware *state, 53 struct slot_allocator *slot_allocator, 54 size_t size, size_t alignment, 55 vregion_flags_t flags) 56{ 57 state->size = size; 58 state->consumed = 0; 59 state->alignment = alignment; 60 61 vspace_mmu_aware_set_slot_alloc(state, slot_allocator); 62 63 errval_t err; 64 65 size = ROUND_UP(size, BASE_PAGE_SIZE); 66 err = memobj_create_anon(&state->memobj, size, 0); 67 if (err_is_fail(err)) { 68 return err_push(err, LIB_ERR_MEMOBJ_CREATE_ANON); 69 } 70 71 err = vregion_map_aligned(&state->vregion, get_current_vspace(), 72 &state->memobj.m, 0, size, 73 flags, alignment); 74 if (err_is_fail(err)) { 75 return err_push(err, LIB_ERR_VREGION_MAP); 76 } 77 state->offset = state->mapoffset = 0; 78 79 return SYS_ERR_OK; 80} 81 82/** 83 * \brief Create mappings 84 * 85 * \param state The object metadata 86 * \param frame An empty slot to place the frame capability in 87 * \param req_size The required amount by the application 88 * \param retbuf Pointer to return the mapped buffer 89 * \param retsize The actual size returned 90 * 91 * This function will returns a special error code if frame_create 92 * fails due to the constrains to the memory server (amount of memory 93 * or region of memory). This is to facilitate retrying with different 94 * constraints. 95 */ 96errval_t vspace_mmu_aware_map(struct vspace_mmu_aware *state, size_t req_size, 97 void **retbuf, size_t *retsize) 98{ 99 errval_t err; 100 101 struct capref frame; 102 103 // Calculate how much still to map in 104 size_t origsize = req_size; 105 assert(state->mapoffset >= state->offset); 106 if(state->mapoffset - state->offset > req_size) { 107 req_size = 0; 108 } else { 109 req_size -= state->mapoffset - state->offset; 110 } 111 size_t alloc_size = ROUND_UP(req_size, BASE_PAGE_SIZE); 112 size_t ret_size = 0; 113 114 if (req_size > 0) { 115#if __x86_64__ 116 if ((state->vregion.flags & VREGION_FLAGS_HUGE) && 117 (state->mapoffset & HUGE_PAGE_MASK) == 0) 118 { 119 // this is an opportunity to switch to 1G pages if requested. 120 // we know that we can use large pages without jumping through hoops 121 // if state->vregion.flags has VREGION_FLAGS_HUGE set and 122 // mapoffset is aligned to at least HUGE_PAGE_SIZE. 123 alloc_size = ROUND_UP(req_size, HUGE_PAGE_SIZE); 124 125 // goto allocation directly so we can avoid nasty code interaction 126 // between #if __x86_64__ and the size checks, we want to be able 127 // to use 2M pages on x86_64 also. -SG, 2015-04-30. 128 goto allocate; 129 } 130#endif 131 if ((state->vregion.flags & VREGION_FLAGS_LARGE) && 132 (state->mapoffset & LARGE_PAGE_MASK) == 0) 133 { 134 // this is an opportunity to switch to 2M pages if requested. 135 // we know that we can use large pages without jumping through hoops 136 // if state->vregion.flags has VREGION_FLAGS_LARGE set and 137 // mapoffset is aligned to at least LARGE_PAGE_SIZE. 138 alloc_size = ROUND_UP(req_size, LARGE_PAGE_SIZE); 139 } 140 // Create frame of appropriate size 141allocate: 142 err = state->slot_alloc->alloc(state->slot_alloc, &frame); 143 if (err_is_fail(err)) { 144 return err_push(err, LIB_ERR_SLOT_ALLOC_NO_SPACE); 145 } 146 147 err = frame_create(frame, alloc_size, &ret_size); 148 if (err_is_fail(err)) { 149 if (err_no(err) == LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS) { 150 // we can only get 4k frames for now; retry with 4k 151 if (alloc_size > BASE_PAGE_SIZE && req_size <= BASE_PAGE_SIZE) { 152 alloc_size = BASE_PAGE_SIZE; 153 goto allocate; 154 } 155 return err_push(err, LIB_ERR_FRAME_CREATE_MS_CONSTRAINTS); 156 } 157 return err_push(err, LIB_ERR_FRAME_CREATE); 158 } 159 assert(ret_size >= req_size); 160 origsize += ret_size - req_size; 161 req_size = ret_size; 162 163 if (state->consumed + req_size > state->size) { 164 err = cap_delete(frame); 165 if (err_is_fail(err)) { 166 debug_err(__FILE__, __func__, __LINE__, err, 167 "cap_delete failed"); 168 } 169 state->slot_alloc->free(state->slot_alloc, frame); 170 return LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE; 171 } 172 173 // Map it in 174 err = state->memobj.m.f.fill(&state->memobj.m, state->mapoffset, frame, 175 req_size); 176 if (err_is_fail(err)) { 177 return err_push(err, LIB_ERR_MEMOBJ_FILL); 178 } 179 err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion, 180 state->mapoffset, 0); 181 if (err_is_fail(err)) { 182 return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER); 183 } 184 } 185 186 // Return buffer 187 genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset; 188 *retbuf = (void*)vspace_genvaddr_to_lvaddr(gvaddr); 189 *retsize = origsize; 190 state->mapoffset += req_size; 191 state->offset += origsize; 192 state->consumed += origsize; 193 194 return SYS_ERR_OK; 195} 196 197errval_t vspace_mmu_aware_reset(struct vspace_mmu_aware *state, 198 struct capref frame, size_t size) 199{ 200 errval_t err; 201 struct vregion *vregion; 202 struct capref oldframe; 203 void *vbuf; 204 205 // create copy of new region 206 err = slot_alloc(&oldframe); 207 if (err_is_fail(err)) { 208 return err; 209 } 210 err = cap_copy(oldframe, frame); 211 if (err_is_fail(err)) { 212 return err; 213 } 214 err = vspace_map_one_frame_attr_aligned(&vbuf, size, oldframe, 215 VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE, LARGE_PAGE_SIZE, 216 NULL, &vregion); 217 if (err_is_fail(err)) { 218 return err; 219 } 220 // copy over data to new frame 221 genvaddr_t gen_base = vregion_get_base_addr(&state->vregion); 222 memcpy(vbuf, (void*)(lvaddr_t)gen_base, state->mapoffset); 223 224 err = vregion_destroy(vregion); 225 if (err_is_fail(err)) { 226 return err; 227 } 228 229 genvaddr_t offset = 0; 230 // Unmap backing frames for [0, size) in state.vregion 231 do { 232 err = state->memobj.m.f.unfill(&state->memobj.m, 0, &oldframe, 233 &offset); 234 if (err_is_fail(err) && 235 err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET) 236 { 237 return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION); 238 } 239 struct frame_identity fi; 240 // increase address 241 err = frame_identify(oldframe, &fi); 242 if (err_is_fail(err)) { 243 return err; 244 } 245 offset += fi.bytes; 246 err = cap_destroy(oldframe); 247 if (err_is_fail(err)) { 248 return err; 249 } 250 } while(offset < state->mapoffset); 251 252 // Map new frame in 253 err = state->memobj.m.f.fill(&state->memobj.m, 0, frame, size); 254 if (err_is_fail(err)) { 255 return err_push(err, LIB_ERR_MEMOBJ_FILL); 256 } 257 err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion, 0, 0); 258 if (err_is_fail(err)) { 259 return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER); 260 } 261 262 state->mapoffset = size; 263 return SYS_ERR_OK; 264} 265 266errval_t vspace_mmu_aware_unmap(struct vspace_mmu_aware *state, 267 lvaddr_t base, size_t bytes) 268{ 269 errval_t err; 270 struct capref frame; 271 genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset; 272 lvaddr_t eaddr = vspace_genvaddr_to_lvaddr(gvaddr); 273 genvaddr_t offset; 274 genvaddr_t gen_base = vspace_lvaddr_to_genvaddr(base) 275 - vregion_get_base_addr(&state->vregion); 276 genvaddr_t min_offset = 0; 277 bool success = false; 278 279 assert(vspace_lvaddr_to_genvaddr(base) >= vregion_get_base_addr(&state->vregion)); 280 assert(base + bytes == (lvaddr_t)eaddr); 281 282 assert(bytes <= state->consumed); 283 assert(bytes <= state->offset); 284 285 // Reduce offset 286 state->offset -= bytes; 287 state->consumed -= bytes; 288 289 // Free only in bigger blocks 290 if(state->mapoffset - state->offset > MIN_MEM_FOR_FREE) { 291 do { 292 // Unmap and return (via unfill) frames from base 293 err = state->memobj.m.f.unfill(&state->memobj.m, gen_base, 294 &frame, &offset); 295 if(err_is_fail(err) && err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET) { 296 return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION); 297 } 298 299 // Delete frame cap 300 if(err_is_ok(err)) { 301 success = true; 302 if (min_offset == 0 || min_offset > offset) { 303 min_offset = offset; 304 } 305 306 err = cap_destroy(frame); 307 if(err_is_fail(err)) { 308 return err; 309 } 310 } 311 } while(err != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET); 312 313// state->consumed -= bytes; 314 if (success) { 315 state->mapoffset = min_offset; 316 } 317 } 318 319 return SYS_ERR_OK; 320} 321