diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2001-01-15 19:42:33 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2001-01-15 19:42:33 +0000 |
commit | c6dd43f2632c846acdc7596e405c7b9681ccc606 (patch) | |
tree | bd0cb6fbedbbed7b24175137325a560e1261855f /winsup | |
parent | 6d15efce111e6e57737ec7a405c47a542ed56f69 (diff) | |
download | newlib-c6dd43f2632c846acdc7596e405c7b9681ccc606.zip newlib-c6dd43f2632c846acdc7596e405c7b9681ccc606.tar.gz newlib-c6dd43f2632c846acdc7596e405c7b9681ccc606.tar.bz2 |
* mmap.cc: include <unistd.h>. Define some bit operations for
the new page map.
(mmap_record): Change type of base_address_ to caddr_t.
Add map_map_ member. Add several methods to manipulate map_map_.
(mmap_record::alloc_map): New method.
(mmap_record::free_map): Ditto.
(mmap_record::find_empty): Ditto.
(mmap_record::map_map): Ditto.
(mmap_record::unmap_map): Ditto.
(list::add_record): Change return type to `mmap_record *'.
Allocate page map.
(list::match): New method.
(mmap): Partly rewritten to take care for the new page map. Add some
parameter checking.
(munmap): Ditto.
Diffstat (limited to 'winsup')
-rw-r--r-- | winsup/cygwin/ChangeLog | 18 | ||||
-rw-r--r-- | winsup/cygwin/mmap.cc | 280 |
2 files changed, 265 insertions, 33 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 1eba5ce..9e27600 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,21 @@ +Mon Jan 15 20:34:00 2001 Corinna Vinschen <corinna@vinschen.de> + + * mmap.cc: include <unistd.h>. Define some bit operations for + the new page map. + (mmap_record): Change type of base_address_ to caddr_t. + Add map_map_ member. Add several methods to manipulate map_map_. + (mmap_record::alloc_map): New method. + (mmap_record::free_map): Ditto. + (mmap_record::find_empty): Ditto. + (mmap_record::map_map): Ditto. + (mmap_record::unmap_map): Ditto. + (list::add_record): Change return type to `mmap_record *'. + Allocate page map. + (list::match): New method. + (mmap): Partly rewritten to take care for the new page map. Add some + parameter checking. + (munmap): Ditto. + Mon Jan 15 13:50:00 2001 Corinna Vinschen <corinna@vinschen.de> * heap.cc (heap_init): Fix extern declaration of getpagesize. diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc index edee6ed..5e62776 100644 --- a/winsup/cygwin/mmap.cc +++ b/winsup/cygwin/mmap.cc @@ -9,6 +9,7 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" +#include <unistd.h> #include <stdlib.h> #include <stddef.h> #include <sys/mman.h> @@ -21,6 +22,15 @@ details. */ #include "pinfo.h" #include "security.h" +#define PAGE_CNT(bytes) howmany(bytes,getpagesize()) + +#define PGBITS (sizeof(DWORD)*8) +#define MAPSIZE(pages) howmany(pages,PGBITS) + +#define MAP_SET(n) (map_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS))) +#define MAP_CLR(n) (map_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS))) +#define MAP_ISSET(n) (map_map_[(n)/PGBITS] & (1L << ((n) % PGBITS))) + /* * Simple class used to keep a record of all current * mmap areas in a process. Needed so that @@ -35,12 +45,13 @@ class mmap_record DWORD access_mode_; DWORD offset_; DWORD size_to_map_; - void *base_address_; + caddr_t base_address_; + DWORD *map_map_; public: - mmap_record (int fd, HANDLE h, DWORD ac, DWORD o, DWORD s, void *b) : + mmap_record (int fd, HANDLE h, DWORD ac, DWORD o, DWORD s, caddr_t b) : fdesc_ (fd), mapping_handle_ (h), access_mode_ (ac), offset_ (o), - size_to_map_ (s), base_address_ (b) { ; } + size_to_map_ (s), base_address_ (b) , map_map_ (NULL) { ; } /* Default Copy constructor/operator=/destructor are ok */ @@ -50,9 +61,116 @@ class mmap_record DWORD get_access () const { return access_mode_; } DWORD get_offset () const { return offset_; } DWORD get_size () const { return size_to_map_; } - void *get_address () const { return base_address_; } + caddr_t get_address () const { return base_address_; } + DWORD *get_map () const { return map_map_; } + + void alloc_map () + { + /* Allocate one bit per page */ + map_map_ = (DWORD *) calloc (MAPSIZE(PAGE_CNT (size_to_map_)), + sizeof (DWORD)); + if (os_being_run == winNT) + { + DWORD old_prot; + if (!VirtualProtect (base_address_, size_to_map_, + PAGE_NOACCESS, &old_prot)) + syscall_printf ("-1 = alloc_map (): %E"); + } + } + void free_map () { if (map_map_) free (map_map_); } + + DWORD find_empty (DWORD pages); + DWORD map_map (DWORD off, DWORD len); + BOOL unmap_map (caddr_t addr, DWORD len); }; +DWORD +mmap_record::find_empty (DWORD pages) +{ + DWORD mapped_pages = PAGE_CNT (size_to_map_); + DWORD start; + + for (start = 0; start < mapped_pages - pages; ++start) + if (!MAP_ISSET (start)) + { + DWORD cnt; + for (cnt = 0; cnt < pages; ++cnt) + if (MAP_ISSET (start + cnt)) + break; + if (cnt >= pages) + return start; + } + return (DWORD)-1; +} + +DWORD +mmap_record::map_map (DWORD off, DWORD len) +{ + DWORD prot, old_prot; + switch (access_mode_) + { + case FILE_MAP_WRITE: + prot = PAGE_READWRITE; + break; + case FILE_MAP_READ: + prot = PAGE_READONLY; + break; + default: + prot = PAGE_WRITECOPY; + break; + } + + len = PAGE_CNT (len); + if (fdesc_ == -1 && !off) + { + off = find_empty (len); + if (off != (DWORD)-1) + { + if (os_being_run == winNT + && !VirtualProtect (base_address_ + off * getpagesize (), + len * getpagesize (), prot, &old_prot)) + syscall_printf ("-1 = map_map (): %E"); + + while (len-- > 0) + MAP_SET (off + len); + return off * getpagesize (); + } + return 0L; + } + off -= offset_; + DWORD start = off / getpagesize (); + if (os_being_run == winNT + && !VirtualProtect (base_address_ + start * getpagesize (), + len * getpagesize (), prot, &old_prot)) + syscall_printf ("-1 = map_map (): %E"); + + for (; len-- > 0; ++start) + MAP_SET (start); + return off; +} + +BOOL +mmap_record::unmap_map (caddr_t addr, DWORD len) +{ + DWORD old_prot; + DWORD off = addr - base_address_; + off /= getpagesize (); + len = PAGE_CNT (len); + if (os_being_run == winNT + && !VirtualProtect (base_address_ + off * getpagesize (), + len * getpagesize (), PAGE_NOACCESS, &old_prot)) + syscall_printf ("-1 = unmap_map (): %E"); + + for (; len-- > 0; ++off) + MAP_CLR (off); + /* Return TRUE if all pages are free'd which may result in unmapping + the whole chunk. */ + for (len = MAPSIZE(PAGE_CNT (size_to_map_)); len > 0; ) + if (map_map_[--len]) + return FALSE; + return TRUE; +} + class list { public: mmap_record *recs; @@ -60,8 +178,10 @@ public: int fd; list (); ~list (); - void add_record (mmap_record r); + mmap_record *add_record (mmap_record r); void erase (int i); + mmap_record *match (DWORD off, DWORD len); + off_t match (caddr_t addr, DWORD len, off_t start); }; list::list () @@ -74,10 +194,12 @@ list::list () list::~list () { + for (mmap_record *rec = recs; nrecs-- > 0; ++rec) + rec->free_map (); free (recs); } -void +mmap_record * list::add_record (mmap_record r) { if (nrecs == maxrecs) @@ -85,12 +207,47 @@ list::add_record (mmap_record r) maxrecs += 5; recs = (mmap_record *) realloc (recs, maxrecs * sizeof (mmap_record)); } - recs[nrecs++] = r; + recs[nrecs] = r; + recs[nrecs].alloc_map (); + return recs + nrecs++; +} + +/* Used in mmap() */ +mmap_record * +list::match (DWORD off, DWORD len) +{ + if (fd == -1 && !off) + { + len = PAGE_CNT (len); + for (int i = 0; i < nrecs; ++i) + if (recs[i].find_empty (len) != (DWORD)-1) + return recs + i; + } + else + { + for (int i = 0; i < nrecs; ++i) + if (off >= recs[i].get_offset () + && off + len <= recs[i].get_offset () + recs[i].get_size ()) + return recs + i; + } + return NULL; +} + +/* Used in munmap() */ +off_t +list::match (caddr_t addr, DWORD len, off_t start) +{ + for (int i = start + 1; i < nrecs; ++i) + if (addr >= recs[i].get_address () + && addr + len <= recs[i].get_address () + recs[i].get_size ()) + return i; + return (off_t)-1; } void list::erase (int i) { + recs[i].free_map (); for (; i < nrecs-1; i++) recs[i] = recs[i+1]; nrecs--; @@ -167,10 +324,28 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) syscall_printf ("addr %x, len %d, prot %x, flags %x, fd %d, off %d", addr, len, prot, flags, fd, off); + static DWORD granularity; + if (!granularity) + { + SYSTEM_INFO si; + GetSystemInfo (&si); + granularity = si.dwAllocationGranularity; + } + DWORD access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; if (flags & MAP_PRIVATE) access = FILE_MAP_COPY; + /* Error conditions according to SUSv2 */ + if (off % getpagesize () + || ((flags & MAP_FIXED) && ((DWORD)addr % granularity)) + || !len) + { + set_errno (EINVAL); + syscall_printf ("-1 = mmap(): Invalid parameters"); + return MAP_FAILED; + } + SetResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); #if 0 @@ -183,7 +358,7 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) { set_errno (EINVAL); syscall_printf ("-1 = mmap(): win95 and MAP_FIXED"); - return (caddr_t) -1; + return MAP_FAILED; } #endif @@ -195,21 +370,43 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) { set_errno (ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM"); - return (caddr_t) -1; + ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); + return MAP_FAILED; + } + } + + if (flags & MAP_ANONYMOUS) + fd = -1; + + /* First check if this mapping matches into the chunk of another + already performed mapping. */ + list *l = mmapped_areas->get_list_by_fd (fd); + if (l) + { + mmap_record *rec; + if ((rec = l->match (off, len)) != NULL) + { + off = rec->map_map (off, len); + caddr_t ret = rec->get_address () + off; + syscall_printf ("%x = mmap() succeeded", ret); + ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); + return ret; } } + /* Map always in multipliers of `granularity'-sized chunks. */ + DWORD gran_off = off & ~(granularity - 1); + DWORD gran_len = howmany (len, granularity) * granularity; + fhandler_disk_file fh_paging_file (NULL); fhandler_base *fh; caddr_t base = addr; HANDLE h; - if ((flags & MAP_ANONYMOUS) || fd == -1) + if (fd == -1) { fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE); fh = &fh_paging_file; - /* Ensure that fd is recorded as -1 */ - fd = -1; } else { @@ -219,12 +416,19 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) set_errno (EBADF); syscall_printf ("-1 = mmap(): EBADF"); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); - return (caddr_t) -1; + return MAP_FAILED; } fh = fdtab[fd]; + if (fh->get_device () == FH_DISK) + { + DWORD fsiz = GetFileSize (fh->get_handle (), NULL); + fsiz -= gran_off; + if (gran_len > fsiz) + gran_len = fsiz; + } } - h = fh->mmap (&base, len, access, flags, off); + h = fh->mmap (&base, gran_len, access, flags, gran_off); if (h == INVALID_HANDLE_VALUE) { @@ -232,22 +436,22 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) return MAP_FAILED; } - /* Now we should have a successfully mmaped area. + /* Now we should have a successfully mmapped area. Need to save it so forked children can reproduce it. */ - mmap_record mmap_rec (fd, h, access, off, len, base); + gran_len = PAGE_CNT (gran_len) * getpagesize (); + mmap_record mmap_rec (fd, h, access, gran_off, gran_len, base); /* Get list of mmapped areas for this fd, create a new one if one does not exist yet. */ - list *l = mmapped_areas->get_list_by_fd (fd); if (l == 0) { /* Create a new one */ l = new list; if (l == 0) { - fh->munmap (h, base, len); + fh->munmap (h, base, gran_len); set_errno (ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM"); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); @@ -257,11 +461,12 @@ mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) } /* Insert into the list */ - l->add_record (mmap_rec); - - syscall_printf ("%x = mmap() succeeded", base); + mmap_record *rec = l->add_record (mmap_rec); + off = rec->map_map (off, len); + caddr_t ret = rec->get_address () + off; + syscall_printf ("%x = mmap() succeeded", ret); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); - return base; + return ret; } /* munmap () removes an mmapped area. It insists that base area @@ -273,6 +478,14 @@ munmap (caddr_t addr, size_t len) { syscall_printf ("munmap (addr %x, len %d)", addr, len); + /* Error conditions according to SUSv2 */ + if (((DWORD)addr % getpagesize ()) || !len) + { + set_errno (EINVAL); + syscall_printf ("-1 = munmap(): Invalid parameters"); + return -1; + } + SetResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); /* Check if a mmap'ed area was ever created */ if (mmapped_areas == NULL) @@ -292,11 +505,12 @@ munmap (caddr_t addr, size_t len) list *l = mmapped_areas->lists[it]; if (l != 0) { - for (int li = 0; li < l->nrecs; ++li) + off_t li = -1; + while ((li = l->match(addr, len, li)) >= 0) { - mmap_record rec = l->recs[li]; - if (rec.get_address () == addr && rec.get_size () == len) - { + mmap_record *rec = l->recs + li; + if (rec->unmap_map (addr, len)) + { int fd = l->fd; fhandler_disk_file fh_paging_file (NULL); fhandler_base *fh; @@ -308,17 +522,17 @@ munmap (caddr_t addr, size_t len) } else fh = fdtab[fd]; - fh->munmap (rec.get_handle (), addr, len); + fh->munmap (rec->get_handle (), addr, len); /* Delete the entry. */ l->erase (li); - syscall_printf ("0 = munmap(): %x", addr); - ReleaseResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); - return 0; } - } - } - } + syscall_printf ("0 = munmap(): %x", addr); + ReleaseResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); + return 0; + } + } + } set_errno (EINVAL); syscall_printf ("-1 = munmap(): EINVAL"); |