Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Dec 2003 15:51:06 -0800
From:      Zachary Amsden <zach@mirapoint.com>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/59912: mremap() implementation lacking
Message-ID:  <10ff9356.ec22d770.81cce00@alpo.mirapoint.com>
Resent-Message-ID: <200312030000.hB300H6u091108@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         59912
>Category:       kern
>Synopsis:       mremap() implementation lacking
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Tue Dec 02 16:00:17 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Zachary Amsden
>Release:        FreeBSD 4.2-RELEASE i386
>Organization:
Mirapoint
>Environment:
System: FreeBSD amsden.mirapoint.com 4.2-RELEASE FreeBSD 4.2-RELEASE #0:
Mon Nov 20 13:02:55 GMT 2000
jkh@bento.FreeBSD.org:/usr/src/sys/compile/GENERIC i386

>Description:
mremap() kernel implementation missing, may present Linux emulation
problems.

>How-To-Repeat:

>Fix:

Differences ...
--- sys/sys/mman.h        Thu Mar 20 15:34:49 2003
+++ sys/sys/mman.h   Mon Dec 16 17:29:04 2002
@@ -123,6 +123,11 @@
 #define        MINCORE_REFERENCED_OTHER 0x8 /* Page has been referenced */
 #define        MINCORE_MODIFIED_OTHER  0x10 /* Page has been modified */
 
+/*
+ * Flags for mremap
+ */
+#define MREMAP_MAYMOVE         0x100 /* Region may be moved in memory */
+
 #ifndef _KERNEL
 
 #include <sys/cdefs.h>
@@ -148,6 +153,7 @@
 int    mincore __P((const void *, size_t, char *));
 int    minherit __P((void *, size_t, int));
 #endif
+caddr_t mremap __P((void *, size_t, size_t, unsigned long));
 __END_DECLS
 
 #endif /* !_KERNEL */

--- sys/kern/syscalls.master
+++ sys/kern/syscalls.master
@@ -522,3 +522,5 @@
                            struct kevent *eventlist, int nevents, \
                            const struct timespec *timeout); }
 364    STD     BSD     { int settaskgroup (int group); }
+365    STD     BSD     { caddr_t mremap(void *old_address, size_t old_size, size_t new_size, \
+                           unsigned long flags); }

--- sys/vm/vm_map.c
+++ sys/vm/vm_map.c
@@ -1011,6 +1011,219 @@
 } 

 /*
+ *     vm_map_extend:
+ *
+ *     Attempt to extend a specified address range
+ *
+ */
+int
+vm_map_extend(map, start, end, newend, flags)
+       vm_map_t map;
+       vm_offset_t *start; /* IN/OUT */
+       vm_offset_t end;
+       vm_offset_t newend;
+       int flags;
+{
+       vm_map_entry_t new_entry;
+       vm_map_entry_t prev_entry;
+       vm_ooffset_t offset;
+       vm_offset_t addr;
+       vm_size_t len;
+       int result;
+       int cow;
+       vm_object_t object;
+
+       if (map == kmem_map || map == mb_map)
+               return (KERN_INVALID_ARGUMENT);
+
+       vm_map_lock(map);
+       addr = *start;
+
+       /*
+        * Check that the start and end points are not bogus.
+        */
+
+       if ((addr < map->min_offset) || (newend > map->max_offset) ||
+           (addr >= end) || (end > newend)) {
+               result = KERN_INVALID_ADDRESS;
+               goto err;
+       }
+
+       /*
+        * Find the entry based on the start address
+        */
+       if (!vm_map_lookup_entry(map, addr, &prev_entry))
+               prev_entry = prev_entry->next;
+
+       /*
+        * Ensure that the start and end occurs in the entry
+        */
+       if ((prev_entry == &map->header) || (prev_entry->end < end) ||
+           (prev_entry->start > addr)) {
+               result = KERN_INVALID_ADDRESS;
+               goto err;
+       }
+       object = prev_entry->object.vm_object;
+       
+  
+       /*
+        * Assert that the next entry doesn't overlap the new end point,
+        * and that the current entry ends at the specified region.
+        */
+       if (((prev_entry->next != &map->header) &&
+            (prev_entry->next->start < newend)) ||
+           (prev_entry->end > end)) {
+               /*
+                * If we are not allowed to move the range, fail
+                */
+               if ((flags & MREMAP_MAYMOVE) == 0) {
+                       result = KERN_NO_SPACE;
+                       goto err;
+               }
+
+               /*
+                * Reverse the eflags to COW arguments.  Ugh.
+                */
+               cow = 0;
+               if ((prev_entry->eflags & MAP_ENTRY_COW) &&
+                   (prev_entry->eflags & MAP_ENTRY_NEEDS_COPY))
+                       cow |= MAP_COPY_ON_WRITE;
+               if (prev_entry->eflags & MAP_ENTRY_NOFAULT)
+                       cow |= MAP_NOFAULT;
+               if (prev_entry->eflags & MAP_ENTRY_NOSYNC)
+                       cow |= MAP_DISABLE_SYNCER;
+               if (prev_entry->eflags & MAP_ENTRY_NOCOREDUMP)
+                       cow |= MAP_DISABLE_COREDUMP;
+       
+               /*
+                * Search for a new range using the old address as a
+                * hint.  Return address in start.
+                */
+               len = newend - addr;
+               *start = pmap_addr_hint(object, addr, len);
+               if (vm_map_findspace(map, *start, len, start)) {
+                       result = KERN_NO_SPACE;
+                       goto err;
+               }
+               result = vm_map_insert(map, object, prev_entry->offset,
+                                    *start, *start + len, prev_entry->protection,
+                                    prev_entry->max_protection, cow);
+               if (result == 0) {
+                       vm_map_lookup_entry(map, *start + len, &new_entry);
+                       if (!new_entry) {
+                               /* Impossible */
+                               vm_map_remove(map, *start, *start + len);
+                               result = KERN_INVALID_ADDRESS;
+                               goto err;
+                       }
+                       if (object)
+                               vm_object_reference(object);
+                       /*
+                        * Found a new region to place this block.  Copy
+                        * the page map or fault the pages into place.
+                        * We do this ourselves, since we don't want to
+                        * trigger COW protection on the page - we are just
+                        * relocating prev_entry.  Deallocating the old map
+                        * also must be done by hand.
+                        *
+                        * First, clip the old region out of the possible
+                        * coalesced entry.
+                        */
+                       vm_map_clip_start(map, prev_entry, addr);
+                       vm_map_clip_end(map, prev_entry, end);
+                       if (prev_entry->wired_count == 0)
+                               pmap_copy(map->pmap, map->pmap, new_entry->start,
+                                         len, prev_entry->start);
+                       else {
+                               vm_fault_copy_entry(map, map, new_entry, prev_entry);
+                               vm_map_entry_unwire(map, prev_entry);
+                       }
+                       if ((object != kernel_object) &&
+                           (object != kmem_object))
+                               pmap_remove(map->pmap, prev_entry->start, prev_entry->end);
+                       vm_map_entry_delete(map, prev_entry);
+                       vm_map_simplify_entry(map, new_entry);
+                       result = KERN_SUCCESS;
+                       goto err;
+               } else {
+                       result = KERN_NO_SPACE;
+               }
+               goto err;
+       }
+
+       offset = prev_entry->offset;
+       if ((prev_entry->wired_count == 0) &&
+           ((object == NULL) ||
+            vm_object_coalesce(object,
+                               OFF_TO_IDX(prev_entry->offset),
+                               (vm_size_t)(prev_entry->end - prev_entry->start),
+                               (vm_size_t)(newend - prev_entry->end)))) {
+               /*
+                * We were able to extend the object.  Determine if we
+                * can extend the previous map entry to include the
+                * new range as well.
+                */
+               if (prev_entry->inheritance == VM_INHERIT_DEFAULT) {
+                       map->size += (newend - prev_entry->end);
+                       prev_entry->end = newend;
+                       result = KERN_SUCCESS;
+                       goto err;
+               } 
+               offset = prev_entry->offset +
+                       (prev_entry->end - prev_entry->start);
+       }
+               
+       /*
+        * If we couldn't extend the object or map for any reason,
+        * we are going to reuse the vm_object from the previous map
+        * entry, so refcount it.
+        */
+       if (object) {
+               vm_object_reference(object);
+               vm_object_clear_flag(object, OBJ_ONEMAPPING);
+       }
+                       
+       /*
+        * Create a new map entry
+        */
+                               
+       new_entry = vm_map_entry_create(map);
+       new_entry->start = end;
+       new_entry->end = newend;   
+                               
+       new_entry->eflags = prev_entry->eflags;
+       new_entry->object.vm_object = prev_entry->object.vm_object;
+       new_entry->offset = offset;
+       new_entry->avail_ssize = 0;
+         
+        new_entry->inheritance = VM_INHERIT_DEFAULT;
+        new_entry->protection = prev_entry->protection;
+        new_entry->max_protection = prev_entry->max_protection;
+        new_entry->wired_count = 0;
+
+        /*
+         * Insert the new entry into the list
+         */
+                       
+        vm_map_entry_link(map, prev_entry, new_entry);
+        map->size += new_entry->end - new_entry->start;
+                       
+        /*
+         * Update the free space hint
+         */
+        if ((map->first_free == prev_entry) &&
+            (prev_entry->end >= new_entry->start)) {
+                map->first_free = new_entry;
+        }
+       result = KERN_SUCCESS;
+                       
+err:
+       vm_map_unlock(map);
+                 
+       return (result);
+}
+        
+/*
  *     vm_map_madvise:
  *
  *     This routine traverses a processes map handling the madvise

--- sys/vm/vm_mmap.c
+++ sys/vm/vm_mmap.c
@@ -152,6 +152,93 @@
 }
 #endif                         /* COMPAT_43 || COMPAT_SUNOS */
                
+/*
+ * Memory remap (mremap) system call.  Old address must be page  
+ * aligned.  If the MREMAP_MAYMOVE flag is specified, the pages
+ * may be automatically moved to a new location.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct mremap_args {
+       void *old_address;
+       size_t old_size;
+       size_t new_size;
+       int flags;
+};
+#endif
+                               
+int
+mremap(p, uap)
+       struct proc *p;
+       register struct mremap_args *uap;
+{
+       vm_offset_t addr;
+       vm_size_t osize, nsize;
+       vm_map_t map;
+       int error;
+        
+       addr = (vm_offset_t) uap->old_address;
+       /*
+        * Must be page aligned
+        */
+       if (trunc_page(addr) != addr)
+               return (EINVAL);
+           
+       if (uap->flags & ~MREMAP_MAYMOVE)
+               return (EINVAL);
+       
+       osize = round_page((vm_offset_t)uap->old_size);
+       nsize = round_page((vm_offset_t)uap->new_size);
+       if (osize == 0)
+               return (EINVAL);
+
+       /*
+        * Check for illegal addresses.  Watch out for address wrap... Note
+        * that VM_*_ADDRESS are not constants due to casts (argh).
+        */
+       if (VM_MAXUSER_ADDRESS > 0 && addr + nsize > VM_MAXUSER_ADDRESS)
+               return (EINVAL);
+#ifndef i386
+       if (VM_MIN_ADDRESS > 0 && addr < VM_MIN_ADDRESS)
+               return (EINVAL);
+#endif
+
+       /*
+        * nothing to do
+        */
+       if (nsize == osize)
+               return (0);
+
+       map = &p->p_vmspace->vm_map;
+
+       /*
+        * Shrink case
+        */
+       if (nsize < osize) {
+               /*
+                * Make sure entire range is allocated.
+                */
+               if (!vm_map_check_protection(map, addr, addr + osize, VM_PROT_NONE))
+                       return (EINVAL);
+               /* returns nothing but KERN_SUCCESS anyway */
+               (void) vm_map_remove(map, addr + nsize, addr + osize);
+               p->p_retval[0] = nsize ? (register_t) addr : 0;
+               return (0);
+       }
+
+       error = vm_map_extend(map, &addr, addr + osize, addr + nsize, uap->flags);
+       switch (error) {
+       case KERN_SUCCESS:
+               p->p_retval[0] = addr;
+               return (0);
+       case KERN_NO_SPACE:
+               return (ENOMEM);
+       case KERN_PROTECTION_FAILURE:
+               return (EACCES);
+       case KERN_INVALID_ADDRESS:
+       default:  
+               return (EINVAL);
+       }
+}
         
 /*
  * Memory Map (mmap) system call.  Note that the file offset

--- sys/vm/vm_map.h
+++ sys/vm/vm_map.h
@@ -356,6 +356,7 @@
 int vm_map_inherit __P((vm_map_t, vm_offset_t, vm_offset_t, vm_inherit_t));
 void vm_map_init __P((struct vm_map *, vm_offset_t, vm_offset_t));   
 int vm_map_insert __P((vm_map_t, vm_object_t, vm_ooffset_t,
vm_offset_t, vm_offset_t, vm_prot_t, vm
_prot_t, int));
+int vm_map_extend __P((vm_map_t, vm_offset_t *, vm_offset_t,
vm_offset_t, int));
 int vm_map_lookup __P((vm_map_t *, vm_offset_t, vm_prot_t,
vm_map_entry_t *, vm_object_t *,
     vm_pindex_t *, vm_prot_t *, boolean_t *));
 void vm_map_lookup_done __P((vm_map_t, vm_map_entry_t));
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?10ff9356.ec22d770.81cce00>