aboutsummaryrefslogtreecommitdiff
path: root/kernel/mm/mobj.c
blob: 4b9c80f29ede7af0fb42bee895654b85d654ac13 (plain)
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
#include "errno.h"

#include "mm/mobj.h"
#include "mm/pframe.h"

#include "util/debug.h"
#include <util/string.h>

/*
 * Initialize o according to type and ops. If ops do not specify a
 * get_pframe function, set it to the default, mobj_default_get_pframe.
 * Do the same with the destructor function pointer.
 *
 * Upon return, the refcount of the mobj should be 1.
 */
void mobj_init(mobj_t *o, long type, mobj_ops_t *ops)
{
    o->mo_type = type;

    memcpy(&o->mo_ops, ops, sizeof(mobj_ops_t));

    if (!o->mo_ops.get_pframe)
    {
        o->mo_ops.get_pframe = mobj_default_get_pframe;
        KASSERT(o->mo_ops.fill_pframe);
        KASSERT(o->mo_ops.flush_pframe);
    }
    if (!o->mo_ops.destructor)
    {
        o->mo_ops.destructor = mobj_default_destructor;
    }

    kmutex_init(&o->mo_mutex);

    o->mo_refcount = ATOMIC_INIT(1);
    list_init(&o->mo_pframes);
}

/*
 * Lock the mobj's mutex
 */
inline void mobj_lock(mobj_t *o) { kmutex_lock(&o->mo_mutex); }

/*
 * Unlock the mobj's mutex
 */
inline void mobj_unlock(mobj_t *o) { kmutex_unlock(&o->mo_mutex); }

/*
 * Increment refcount
 */
void mobj_ref(mobj_t *o)
{
    atomic_inc(&o->mo_refcount);
}

void mobj_put_locked(mobj_t **op)
{
    mobj_unlock(*op);
    mobj_put(op);
}

/*
 * Decrement refcount, and set *op = NULL.
 * If the refcount drop to 0, call the destructor, otherwise unlock the mobj.
 */
void mobj_put(mobj_t **op)
{
    mobj_t *o = *op;
    KASSERT(o->mo_refcount);
    *op = NULL;

    dbg(DBG_ERROR, "count: %d\n", o->mo_refcount);
    if (atomic_dec_and_test(&o->mo_refcount))
    {
        dbg(DBG_ERROR, "count: %d\n", o->mo_refcount);

        KASSERT(!kmutex_owns_mutex(&o->mo_mutex));
        o->mo_ops.destructor(o);
    }
    else
    {
        dbg(DBG_ERROR, "count: %d\n", o->mo_refcount);
    }
}

/*
 * Find a pframe that already exists in the memory object's mo_pframes list.
 * If a pframe is found, it must be locked upon return from this function using
 * pf_mutex.
 */
void mobj_find_pframe(mobj_t *o, uint64_t pagenum, pframe_t **pfp)
{
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    list_iterate(&o->mo_pframes, pf, pframe_t, pf_link)
    {
        if (pf->pf_pagenum == pagenum)
        {
            kmutex_lock(&pf->pf_mutex);
            *pfp = pf;
            return;
        }
    }
    *pfp = NULL;
}

/*
 * Wrapper around the memory object's get_pframe function
 * Assert a sane state of the world surrounding the call to get_pframe
 */
long mobj_get_pframe(mobj_t *o, uint64_t pagenum, long forwrite,
                     pframe_t **pfp)
{
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    *pfp = NULL;
    long ret = o->mo_ops.get_pframe(o, pagenum, forwrite, pfp);
    KASSERT((!*pfp && ret) || kmutex_owns_mutex(&(*pfp)->pf_mutex));
    return ret;
}

/*
 * Create and initialize a pframe and add it to the mobj's mo_pframes list.
 * Upon successful return, the pframe's pf_mutex is locked.
 */
#ifdef OLD
static void mobj_create_pframe(mobj_t *o, uint64_t pagenum, pframe_t **pfp)
#endif
void mobj_create_pframe(mobj_t *o, uint64_t pagenum, uint64_t loc, pframe_t **pfp)
{
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    pframe_t *pf = pframe_create();
    if (pf)
    {
        kmutex_lock(&pf->pf_mutex);

        pf->pf_pagenum = pagenum;
        pf->pf_loc = loc;
        list_insert_tail(&o->mo_pframes, &pf->pf_link);
    }
    KASSERT(!pf || kmutex_owns_mutex(&pf->pf_mutex));
    *pfp = pf;
}

/*
 * The default get pframe that is at the center of the mobj/pframe subsystem.
 * This is the routine that is used when the memory object does not have a 
 * get_pframe function associated with it (or called in the case of shadow objects
 * when the forwrite flag is set).
 *
 * First, check if an pframe already exists in the mobj, creating one as
 * necessary. Then, ensure that the pframe's contents are loaded: i.e. that
 * pf->pf_addr is non-null. You will want to use page_alloc() and fill_pframe
 * function pointer of the mobj. Finally, if forwrite is true, mark the pframe
 * as dirtied. The resulting pframe should be set in *pfp.
 *
 * Note that upon failure, *pfp MUST be null. As always, make sure you cleanup
 * properly in all error cases (especially if fill_prame fails)
 *
 * Upon successful return, *pfp refers to the found pframe and MUST be locked.
 *
 * Error cases mobj_default_get_pframe is responsible for generating:
 *  - ENOMEM: either cannot create the pframe or cannot allocate memory for
 *            the pframe's contents
 */
long mobj_default_get_pframe(mobj_t *o, uint64_t pagenum, long forwrite,
                             pframe_t **pfp)
{
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    *pfp = NULL;
    pframe_t *pf = NULL;
    mobj_find_pframe(o, pagenum, &pf);
    if (!pf)
    {
        mobj_create_pframe(o, pagenum, 0, &pf);  // XXX is zero correct???
    }
    if (!pf)
    {
        return -ENOMEM;
    }
    KASSERT(kmutex_owns_mutex(&pf->pf_mutex));
    if (!pf->pf_addr)
    {
        KASSERT(!pf->pf_dirty &&
                "dirtied page doesn't have a physical address");
        pf->pf_addr = page_alloc();
        if (!pf->pf_addr)
        {
            return -ENOMEM;
        }

        dbg(DBG_PFRAME, "filling pframe 0x%p (mobj 0x%p page %lu)\n", pf, o,
            pf->pf_pagenum);
        KASSERT(o->mo_ops.fill_pframe);
        long ret = o->mo_ops.fill_pframe(o, pf);
        if (ret)
        {
            page_free(pf->pf_addr);
            pf->pf_addr = NULL;
            kmutex_unlock(&pf->pf_mutex);
            return ret;
        }
    }
    pf->pf_dirty |= forwrite;
    *pfp = pf;
    return 0;
}

/*
 * If the pframe is dirty, call the mobj's flush_pframe; if flush_pframe returns
 * successfully, clear pf_dirty flag and return 0. Otherwise, return what
 * flush_pframe returned.
 *
 * Both o and pf must be locked when calling this function
 */
long mobj_flush_pframe(mobj_t *o, pframe_t *pf)
{
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    KASSERT(kmutex_owns_mutex(&pf->pf_mutex));
    KASSERT(pf->pf_addr && "cannot flush a frame not in memory!");
    dbg(DBG_PFRAME, "pf 0x%p, mobj 0x%p, page %lu\n", pf, o, pf->pf_pagenum);
    if (pf->pf_dirty)
    {
        KASSERT(o->mo_ops.flush_pframe);
        long ret = o->mo_ops.flush_pframe(o, pf);
        if (ret)
            return ret;
        pf->pf_dirty = 0;
    }
    KASSERT(!pf->pf_dirty);
    return 0;
}

/*
 * Iterate through the pframes of the mobj and try to flush each one.
 * If any of them fail, let that reflect in the return value.
 *
 * The mobj o must be locked when calling this function
 */
long mobj_flush(mobj_t *o)
{
    long ret = 0;
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));
    list_iterate(&o->mo_pframes, pf, pframe_t, pf_link)
    {
        kmutex_lock(&pf->pf_mutex); // get the pframe (lock it)
        if (pf->pf_addr)
        {
            ret |= mobj_flush_pframe(o, pf);
        }
        pframe_release(&pf);
    }
    return ret;
}

/*
 * Attempt to flush the pframe. If the flush succeeds, then free the pframe's
 * contents (pf->pf_addr) using page_free, remove the pframe from the mobj's
 * list and call pframe_free.
 *
 * Upon successful return, *pfp MUST be null. If the function returns an error
 * code, *pfp must be unchanged.
 */
long mobj_free_pframe(mobj_t *o, pframe_t **pfp)
{
    pframe_t *pf = *pfp;

    if (pf->pf_addr)
    {
        long ret = mobj_flush_pframe(o, pf);
        if (ret)
            return ret;

        // [+] TODO REMOVE THIS SECTION WHEN FLUSH DOES IT (I.E. WHEN WE HAVE
        // SUPPORT FOR FREEING PFRAME'S IN USE BY UNMAPPING THEM FROM PAGE
        // TABLES THAT USE THEM)
        if (pf->pf_addr)
        {
            page_free(pf->pf_addr);
            pf->pf_addr = NULL;
        }
    }
    *pfp = NULL;
    list_remove(&pf->pf_link);
    pframe_free(&pf);
    return 0;
}

/*
 * Simply flush the memory object
 */
void mobj_default_destructor(mobj_t *o)
{
    mobj_lock(o);
    KASSERT(kmutex_owns_mutex(&o->mo_mutex));

    long ret = 0;
    list_iterate(&o->mo_pframes, pf, pframe_t, pf_link)
    {
        kmutex_lock(&pf->pf_mutex); // get the pframe (lock it)
        ret |= mobj_free_pframe(o, &pf);
    }

    if (ret)
    {
        dbg(DBG_MM,
            "WARNING: flushing pframes in mobj destructor failed for one or "
            "more frames\n"
            "This means the memory for the pframe will be leaked!");
    }

    KASSERT(!kmutex_has_waiters(&o->mo_mutex));
    mobj_unlock(o);
}