1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
#include "vm/shadow.h"
#include "mm/page.h"
#include "mm/pframe.h"
#include "mm/slab.h"
#include "util/debug.h"
#include "util/string.h"
#define SHADOW_SINGLETON_THRESHOLD 5
typedef struct mobj_shadow
{
// the mobj parts of this shadow object
mobj_t mobj;
// a reference to the mobj that is the data source for this shadow object
// This should be a reference to a shadow object of some ancestor process.
// This is used to traverse the shadow object chain.
mobj_t *shadowed;
// a reference to the mobj at the bottom of this shadow object's chain
// this should NEVER be a shadow object (i.e. it should have some type other
// than MOBJ_SHADOW)
mobj_t *bottom_mobj;
} mobj_shadow_t;
#define MOBJ_TO_SO(o) CONTAINER_OF(o, mobj_shadow_t, mobj)
static slab_allocator_t *shadow_allocator;
static long shadow_get_pframe(mobj_t *o, size_t pagenum, long forwrite,
pframe_t **pfp);
static long shadow_fill_pframe(mobj_t *o, pframe_t *pf);
static long shadow_flush_pframe(mobj_t *o, pframe_t *pf);
static void shadow_destructor(mobj_t *o);
static mobj_ops_t shadow_mobj_ops = {.get_pframe = shadow_get_pframe,
.fill_pframe = shadow_fill_pframe,
.flush_pframe = shadow_flush_pframe,
.destructor = shadow_destructor};
/*
* Initialize shadow_allocator using the slab allocator.
*/
void shadow_init()
{
// NOT_YET_IMPLEMENTED("VM: shadow_init");
shadow_allocator = slab_allocator_create("shadow", sizeof(mobj_shadow_t));
KASSERT(shadow_allocator);
}
/*
* Create a shadow object that shadows the given mobj.
*
* Return a new, LOCKED shadow object on success, or NULL upon failure.
*
* Hints:
* 1) Create and initialize a mobj_shadow_t based on the given mobj.
* 2) Set up the bottom object of the shadow chain, which could have two cases:
* a) Either shadowed is a shadow object, and you can use its bottom_mobj
* b) Or shadowed is not a shadow object, in which case it is the bottom
* object of this chain.shadow_create
*
* Make sure to manage the refcounts correctly.
*/
mobj_t *shadow_create(mobj_t *shadowed)
{
// NOT_YET_IMPLEMENTED("VM: shadow_create");
if (!shadowed)
{
return NULL;
}
// create a new shadow object
mobj_shadow_t *so = (mobj_shadow_t *)slab_obj_alloc(shadow_allocator);
if (!so)
{
return NULL;
}
// initialize the mobj_shadow_t
so->shadowed = shadowed;
// set the bottom_mobj based on the two cases
if (shadowed->mo_type == MOBJ_SHADOW)
{
so->bottom_mobj = MOBJ_TO_SO(so->shadowed)->bottom_mobj;
}
else
{
so->bottom_mobj = shadowed;
}
// init the other fields
mobj_ref(so->shadowed);
mobj_lock(so->bottom_mobj);
mobj_ref(so->bottom_mobj);
mobj_unlock(so->bottom_mobj);
// init and lock the shadow object
mobj_init(&so->mobj, MOBJ_SHADOW, &shadow_mobj_ops);
mobj_lock(&so->mobj);
// return the shadow object
return &so->mobj;
}
/*
* Given a shadow object o, collapse its shadow chain as far as you can.
*
* Hints:
* 1) You can only collapse if the shadowed object is a shadow object.
* 2) When collapsing, you must manually migrate pframes from o's shadowed
* object to o, checking to see if a copy doesn't already exist in o.
* 3) Be careful with refcounting! In particular, when you put away o's
* shadowed object, its refcount should drop to 0, initiating its
* destruction (shadow_destructor).
* 4) As a reminder, any refcounting done in shadow_collapse() must play nice
* with any refcounting done in shadow_destructor().
* 5) Pay attention to mobj and pframe locking.
*/
void shadow_collapse(mobj_t *o)
{
// NOT_YET_IMPLEMENTED("VM: shadow_collapse");
mobj_shadow_t *so = MOBJ_TO_SO(o);
mobj_t *next = so->shadowed;
while(next && next->mo_type==MOBJ_SHADOW)
{
// check if the shadowed object is collapsable
if (next->mo_refcount != 1)
{
// continue on the shadow chain
so = MOBJ_TO_SO(next);
next = MOBJ_TO_SO(next)->shadowed;
continue;
}
// migrate the pframes from o's shadowed object to o
mobj_lock(next); // lock before iterover
list_iterate(&next->mo_pframes, pf, pframe_t, pf_link)
{
// see if shadow pframe is in the current shadow object
pframe_t *pf_shadow = NULL;
mobj_lock(&so->mobj);
mobj_find_pframe(&so->mobj, pf->pf_pagenum, &pf_shadow);
mobj_unlock(&so->mobj);
// if so, migrate it
if (!pf_shadow)
{
list_remove(&pf->pf_link);
// by migrate, just add to the list of pframes
list_insert_tail(&so->mobj.mo_pframes, &pf->pf_link);
}
else
{
// if already exists, just release the version we will remove
pframe_release(&pf_shadow);
}
}
// update the pointer to the shadowed object
so->shadowed = MOBJ_TO_SO(next)->shadowed;
// update the refcounts
mobj_lock(so->shadowed);
mobj_ref(so->shadowed);
mobj_put_locked(&next); // "remove" the next
// update next for next iter, now that it's removed
next = so->shadowed;
mobj_unlock(next);
}
}
/*
* Obtain the desired pframe from the given mobj, traversing its shadow chain if
* necessary. This is where copy-on-write logic happens!
*
* Arguments:
* o - The object from which to obtain a pframe
* pagenum - Number of the desired page relative to the object
* forwrite - Set if the caller wants to write to the pframe's data, clear if
* only reading
* pfp - Upon success, pfp should point to the desired pframe.
*
* Return 0 on success, or:
* - Propagate errors from mobj_default_get_pframe() and mobj_get_pframe()
*
* Hints:
* 1) If forwrite is set, use mobj_default_get_pframe().
* 2) If forwrite is clear, check if o already contains the desired frame.
* a) If not, iterate through the shadow chain to find the nearest shadow
* mobj that has the frame. Do not recurse! If the shadow chain is long,
* you will cause a kernel buffer overflow (e.g. from forkbomb).
* b) If no shadow objects have the page, call mobj_get_pframe() to get the
* page from the bottom object and return what it returns.
*
* Pay attention to pframe locking.
*/
static long shadow_get_pframe(mobj_t *o, size_t pagenum, long forwrite,
pframe_t **pfp)
{
// NOT_YET_IMPLEMENTED("VM: shadow_get_pframe");
// return 0;
if (forwrite)
{
return mobj_default_get_pframe(o, pagenum, forwrite, pfp);
}
// check if o already contains the desired frame.
pframe_t *pf = NULL;
mobj_find_pframe(o, pagenum, &pf);
if (pf)
{
*pfp = pf;
return 0;
}
mobj_shadow_t *shadow = MOBJ_TO_SO(o);
mobj_t *current = shadow->shadowed;
while (current && current->mo_type == MOBJ_SHADOW)
{
mobj_lock(current);
mobj_find_pframe(current, pagenum, &pf);
mobj_unlock(current);
if (pf)
{
*pfp = pf;
return 0;
}
shadow = MOBJ_TO_SO(current);
current = shadow->shadowed;
}
mobj_lock(shadow->bottom_mobj);
long ret = mobj_get_pframe(shadow->bottom_mobj, pagenum, forwrite, pfp);
mobj_unlock(shadow->bottom_mobj);
return ret;
}
/*
* Use the given mobj's shadow chain to fill the given pframe.
*
* Return 0 on success, or:
* - Propagate errors from mobj_get_pframe()
*
* Hints:
* 1) Explore mobj_default_get_pframe(), which calls mobj_create_pframe(), to
* understand what state pf is in when this function is called, and how you
* can use it.
* 2) As you can see above, shadow_get_pframe would call
* mobj_default_get_pframe (when the forwrite is set), which would
* create and then fill the pframe (shadow_fill_pframe is called).
* 3) Traverse the shadow chain for a copy of the frame, starting at the given
* mobj's shadowed object. You can use mobj_find_pframe to look for the
* page frame. pay attention to locking/unlocking, and be sure not to
* recurse when traversing.
* 4) If none of the shadow objects have a copy of the frame, use
* mobj_get_pframe on the bottom object to get it.
* 5) After obtaining the desired frame, simply copy its contents into pf.
*/
static long shadow_fill_pframe(mobj_t *o, pframe_t *pf)
{
// NOT_YET_IMPLEMENTED("VM: shadow_fill_pframe");
// return -1;
pframe_t *pf_shadow = NULL;
long ret = 0;
mobj_shadow_t *so = MOBJ_TO_SO(o);
mobj_t *shadowed = so->shadowed;
while (shadowed && shadowed->mo_type == MOBJ_SHADOW)
{
mobj_lock(shadowed);
mobj_find_pframe(shadowed, pf->pf_pagenum, &pf_shadow);
mobj_unlock(shadowed);
if (pf_shadow)
{
memcpy(pf->pf_addr, pf_shadow->pf_addr, PAGE_SIZE);
pframe_release(&pf_shadow);
return 0;
}
so = MOBJ_TO_SO(shadowed);
shadowed = so->shadowed;
}
mobj_lock(so->bottom_mobj);
ret = mobj_get_pframe(so->bottom_mobj, pf->pf_pagenum, 0, &pf_shadow);
mobj_unlock(so->bottom_mobj);
if (ret < 0) {
return ret;
}
memcpy(pf->pf_addr, pf_shadow->pf_addr, PAGE_SIZE);
pframe_release(&pf_shadow);
return 0;
}
/*
* Flush a shadow object's pframe to disk.
*
* Return 0 on success.
*
* Hint:
* - Are shadow objects backed to disk? Do you actually need to do anything
* here?
*/
static long shadow_flush_pframe(mobj_t *o, pframe_t *pf)
{
// NOT_YET_IMPLEMENTED("VM: shadow_flush_pframe");
// return -1;
return 0;
}
/*
* Clean up all resources associated with mobj o.
*
* Hints:
* - Check out mobj_put() to understand how this function gets called.
*
* 1) Call mobj_default_destructor() to flush o's pframes.
* 2) Put the shadow and bottom_mobj members of the shadow object.
* 3) Free the mobj_shadow_t.
*/
static void shadow_destructor(mobj_t *o)
{
// NOT_YET_IMPLEMENTED("VM: shadow_destructor");
mobj_default_destructor(o);
mobj_shadow_t *so = MOBJ_TO_SO(o);
// dbg(DBG_PROC, "shadow_destructor: refcount bottom: %d\n", so->bottom_mobj->mo_refcount);
// dbg(DBG_PROC, "shadow_destructor: refcount: %d\n", so->shadowed->mo_refcount);
// put the shadow and bottom_mobj members of the shadow object
mobj_put(&so->shadowed);
mobj_put(&so->bottom_mobj);
slab_obj_free(shadow_allocator, so);
return;
}
|