#include #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; }