ea38369feeefd01ed0cc836e27707ab6ac0657a0
[barrelfish] / lib / barrelfish / vspace / mmu_aware.c
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
27 /**
28  * \brief Initialize vspace_mmu_aware struct
29  *
30  * \param state   The struct to initialize
31  * \param init    The buffer to use to initialize the struct
32  * \param size    The size of anon memobj to create
33  *
34  * Initializes the struct according to the type of MMU
35  */
36 errval_t vspace_mmu_aware_init(struct vspace_mmu_aware *state, size_t size)
37 {
38     return vspace_mmu_aware_init_aligned(state, size, 0,
39             VREGION_FLAGS_READ_WRITE);
40 }
41
42 errval_t vspace_mmu_aware_init_aligned(struct vspace_mmu_aware *state,
43         size_t size, size_t alignment, vregion_flags_t flags)
44 {
45     state->size = size;
46     state->consumed = 0;
47     state->alignment = alignment;
48
49     errval_t err;
50
51     size = ROUND_UP(size, BASE_PAGE_SIZE);
52     err = memobj_create_anon(&state->memobj, size, 0);
53     if (err_is_fail(err)) {
54         return err_push(err, LIB_ERR_MEMOBJ_CREATE_ANON);
55     }
56
57     err = vregion_map_aligned(&state->vregion, get_current_vspace(),
58                               &state->memobj.m, 0, size,
59                               flags, alignment);
60     if (err_is_fail(err)) {
61         return err_push(err, LIB_ERR_VREGION_MAP);
62     }
63     state->offset = state->mapoffset = 0;
64
65     return SYS_ERR_OK;
66 }
67
68 /**
69  * \brief Create mappings
70  *
71  * \param state     The object metadata
72  * \param frame     An empty slot to place the frame capability in
73  * \param req_size  The required amount by the application
74  * \param retbuf    Pointer to return the mapped buffer
75  * \param retsize   The actual size returned
76  *
77  * This function will returns a special error code if frame_create
78  * fails due to the constrains to the memory server (amount of memory
79  * or region of memory). This is to facilitate retrying with different
80  * constraints.
81  */
82 errval_t vspace_mmu_aware_map(struct vspace_mmu_aware *state,
83                               struct capref frame, size_t req_size,
84                               void **retbuf, size_t *retsize)
85 {
86     errval_t err;
87
88     // Calculate how much still to map in
89     size_t origsize = req_size;
90     assert(state->mapoffset >= state->offset);
91     if(state->mapoffset - state->offset > req_size) {
92         req_size = 0;
93     } else {
94         req_size -= state->mapoffset - state->offset;
95     }
96     size_t alloc_size = req_size;
97     size_t ret_size = 0;
98
99     if (req_size > 0) {
100         if ((state->mapoffset & LARGE_PAGE_MASK) == 0 &&
101             state->alignment >= LARGE_PAGE_SIZE)
102         {
103             // this is an opportunity to switch to 2M pages
104             // we know that we can use large pages without jumping through hoops
105             // if state->alignment is at least LARGE_PAGE_SIZE as we always create
106             // the vregion with VREGION_FLAGS_LARGE.
107             alloc_size = ROUND_UP(req_size, LARGE_PAGE_SIZE);
108         }
109         // Create frame of appropriate size
110 retry:
111         err = frame_create(frame, alloc_size, &ret_size);
112         if (err_is_fail(err)) {
113             if (err_no(err) == LIB_ERR_RAM_ALLOC_MS_CONSTRAINTS) {
114                 // we can only get 4k frames for now; retry with 4k
115                 if (alloc_size > BASE_PAGE_SIZE && origsize < BASE_PAGE_SIZE) {
116                     alloc_size = BASE_PAGE_SIZE;
117                     goto retry;
118                 }
119                 return err_push(err, LIB_ERR_FRAME_CREATE_MS_CONSTRAINTS);
120             }
121             return err_push(err, LIB_ERR_FRAME_CREATE);
122         }
123         assert(ret_size >= req_size);
124         origsize += ret_size - req_size;
125         req_size = ret_size;
126
127         if (state->consumed + req_size > state->size) {
128             err = cap_delete(frame);
129             if (err_is_fail(err)) {
130                 debug_err(__FILE__, __func__, __LINE__, err,
131                           "cap_delete failed");
132             }
133             return LIB_ERR_VSPACE_MMU_AWARE_NO_SPACE;
134         }
135
136         // Map it in
137         err = state->memobj.m.f.fill(&state->memobj.m, state->mapoffset, frame,
138                                      req_size);
139         if (err_is_fail(err)) {
140             return err_push(err, LIB_ERR_MEMOBJ_FILL);
141         }
142         err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion,
143                                           state->mapoffset, 0);
144         if (err_is_fail(err)) {
145             return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
146         }
147     }
148
149     // Return buffer
150     genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
151     *retbuf = (void*)vspace_genvaddr_to_lvaddr(gvaddr);
152     *retsize = origsize;
153     state->mapoffset += req_size;
154     state->offset += origsize;
155     state->consumed += origsize;
156
157     return SYS_ERR_OK;
158 }
159
160 errval_t vspace_mmu_aware_reset(struct vspace_mmu_aware *state,
161                                 struct capref frame, size_t size)
162 {
163     errval_t err;
164     struct vregion *vregion;
165     struct capref oldframe;
166     void *vbuf;
167     // create copy of new region
168     err = slot_alloc(&oldframe);
169     if (err_is_fail(err)) {
170         return err;
171     }
172     err = cap_copy(oldframe, frame);
173     if (err_is_fail(err)) {
174         return err;
175     }
176     err = vspace_map_one_frame_attr_aligned(&vbuf, size, oldframe,
177             VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE, LARGE_PAGE_SIZE,
178             NULL, &vregion);
179     if (err_is_fail(err)) {
180         return err;
181     }
182     // copy over data to new frame
183     genvaddr_t gen_base = vregion_get_base_addr(&state->vregion);
184     memcpy(vbuf, (void*)gen_base, state->mapoffset);
185
186     err = vregion_destroy(vregion);
187     if (err_is_fail(err)) {
188         return err;
189     }
190
191     size_t offset = 0;
192     // Unmap backing frames for [0, size) in state.vregion
193     do {
194         err = state->memobj.m.f.unfill(&state->memobj.m, 0, &oldframe,
195                 &offset);
196         if (err_is_fail(err) &&
197             err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET)
198         {
199             return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
200         }
201         struct frame_identity fi;
202         // increase address
203         err = invoke_frame_identify(oldframe, &fi);
204         if (err_is_fail(err)) {
205             return err;
206         }
207         offset += (1UL<<fi.bits);
208         err = cap_destroy(oldframe);
209         if (err_is_fail(err)) {
210             return err;
211         }
212     } while(offset < state->mapoffset);
213
214     // Map new frame in
215     err = state->memobj.m.f.fill(&state->memobj.m, 0, frame, size);
216     if (err_is_fail(err)) {
217         return err_push(err, LIB_ERR_MEMOBJ_FILL);
218     }
219     err = state->memobj.m.f.pagefault(&state->memobj.m, &state->vregion, 0, 0);
220     if (err_is_fail(err)) {
221         return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
222     }
223
224     state->mapoffset = size;
225     return SYS_ERR_OK;
226 }
227
228 errval_t vspace_mmu_aware_unmap(struct vspace_mmu_aware *state,
229                                 lvaddr_t base, size_t bytes)
230 {
231     errval_t err;
232     struct capref frame;
233     genvaddr_t gvaddr = vregion_get_base_addr(&state->vregion) + state->offset;
234     lvaddr_t eaddr = vspace_genvaddr_to_lvaddr(gvaddr);
235     genvaddr_t offset;
236     genvaddr_t gen_base = vspace_lvaddr_to_genvaddr(base)
237         - vregion_get_base_addr(&state->vregion);
238     genvaddr_t min_offset = 0;
239     bool success = false;
240
241     assert(vspace_lvaddr_to_genvaddr(base) >= vregion_get_base_addr(&state->vregion));
242     assert(base + bytes == (lvaddr_t)eaddr);
243
244     assert(bytes <= state->consumed);
245     assert(bytes <= state->offset);
246
247     // Reduce offset
248     state->offset -= bytes;
249     state->consumed -= bytes;
250
251     // Free only in bigger blocks
252     if(state->mapoffset - state->offset > MIN_MEM_FOR_FREE) {
253         do {
254             // Unmap and return (via unfill) frames from base
255             err = state->memobj.m.f.unfill(&state->memobj.m, gen_base,
256                                            &frame, &offset);
257             if(err_is_fail(err) && err_no(err) != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET) {
258                 return err_push(err, LIB_ERR_MEMOBJ_UNMAP_REGION);
259             }
260
261             // Delete frame cap
262             if(err_is_ok(err)) {
263                 success = true;
264                 if (min_offset == 0 || min_offset > offset) {
265                     min_offset = offset;
266                 }
267
268                 err = cap_destroy(frame);
269                 if(err_is_fail(err)) {
270                     return err;
271                 }
272             }
273         } while(err != LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET);
274
275 //    state->consumed -= bytes;
276         if (success) {
277             state->mapoffset = min_offset;
278         }
279     }
280
281     return SYS_ERR_OK;
282 }