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
|
#include <string.h>
#include "userprog/pagedir.h"
#include "userprog/process.h"
#include "filesys/file.h"
#include "threads/thread.h"
#include "threads/vaddr.h"
#include "threads/malloc.h"
#include "threads/palloc.h"
#include "vm/mmap.h"
#include "vm/page.h"
static struct mmap_table_entry *mmap_table_get (struct mmap_table *table,
mapid_t mapid);
/* initialize memory mapped files table */
bool
mmap_table_init (struct mmap_table *table)
{
table->ids = palloc_get_page (PAL_ZERO);
if (table->ids == NULL)
return false;
table->id_cap = PGSIZE / sizeof (table->ids[0]);
table->id_free = 0;
table->id_max = -1;
return true;
}
/* closes all mapped files and frees the content of the table */
void
mmap_table_free (struct mmap_table *table)
{
mapid_t id;
for (id = 0; id <= table->id_max; id++)
mmap_table_remove (table, id);
palloc_free_page (table->ids);
}
/* inserts a new mmap entry in the mmap table.
returns -1 if entry is invalid or already in the table */
mapid_t
mmap_table_insert (struct mmap_table *table, void *upage, struct file *file,
off_t len)
{
off_t ofs = 0;
size_t page_read_bytes;
struct mmap_table_entry *mme;
mapid_t mapid;
/* no more entries left */
if (table->id_free >= table->id_cap)
return -1;
/* create mmap table entry */
mme = malloc (sizeof *mme);
if (mme == NULL)
return -1;
mme->upage = upage;
mme->file = file;
mme->pages = 0;
/* create needed pages in page table */
while (len > 0)
{
page_read_bytes = (len < PGSIZE) ? len : PGSIZE;
if (!page_table_insert_segment (&thread_current ()->page_table, file, ofs,
upage, page_read_bytes, true))
return -1;
/* Advance. */
len -= PGSIZE;
ofs += page_read_bytes;
upage += PGSIZE;
mme->pages++;
}
/* insert entry into our table */
mapid = table->id_free++;
table->ids[mapid] = mme;
/* update index of free/max mapid index */
if (mapid > table->id_max)
table->id_max = mapid;
while (table->ids[table->id_free] != NULL)
{
table->id_free++;
if (table->id_free >= table->id_cap)
break;
}
return mapid;
}
/* get the mmap table entry associated with the given map id.
return NULL if no mapping is associated with the given id */
static struct mmap_table_entry *
mmap_table_get (struct mmap_table *table, mapid_t mapid)
{
if (mapid < 0 || mapid >= table->id_cap || !table->ids[mapid])
return NULL;
return table->ids[mapid];
}
/* remove the entry associated with the given map id from the mmap table.
flushes all dirty/modified pages back to disk. return true if successful */
bool
mmap_table_remove (struct mmap_table *table, mapid_t mapid)
{
struct page_table_entry *pte;
off_t ofs = 0;
struct thread *thread = thread_current ();
struct mmap_table_entry *mme = mmap_table_get(table, mapid);
if (mme == NULL)
return false;
/* fetch pages needed by mapped file and check whether the page is dirty */
while (mme->pages-- > 0)
{
pte = page_table_remove (&thread->page_table, mme->upage + ofs);
if (pte != NULL && pte->loaded &&
pagedir_is_dirty (thread->pagedir, pte->upage))
{
/* write modified page back to disk */
ASSERT (pte->segment.file == mme->file);
process_lock_filesys ();
file_seek (pte->segment.file, pte->segment.ofs);
file_write (pte->segment.file, pte->upage, pte->segment.read_bytes);
process_unlock_filesys ();
}
if (pte != NULL)
free (pte);
ofs += PGSIZE;
}
/* close the file */
process_lock_filesys ();
file_close (mme->file);
process_unlock_filesys ();
/* remove mmap entry from mmap table */
free (mme);
table->ids[mapid] = NULL;
/* update index of free/max mapid index */
if (mapid < table->id_free)
table->id_free = mapid;
while (table->ids[table->id_max] == NULL)
{
table->id_max--;
if (table->id_max < 0)
break;
}
return true;
}
|