Author: alc
Date: Tue Dec 26 17:59:37 2017
New Revision: 327218
URL: https://svnweb.freebsd.org/changeset/base/327218

Log:
  Refactor vm_map_find(), creating a separate function, vm_map_alignspace(),
  for finding aligned free space in the given map.  With this change, we
  always return KERN_NO_SPACE when we fail to find free space.  Whereas,
  previously, we might return KERN_INVALID_ADDRESS.  Also, with this change,
  we explicitly check for address wrap, rather than relying upon the map's
  min and max addresses to establish sentinel-like regions.
  
  This refactoring was inspired by the problem that we addressed in r326098.
  
  Reviewed by:  kib
  Tested by:    pho
  Discussed with:       markj
  MFC after:    3 weeks
  Differential Revision:        https://reviews.freebsd.org/D13346

Modified:
  head/sys/vm/vm_map.c

Modified: head/sys/vm/vm_map.c
==============================================================================
--- head/sys/vm/vm_map.c        Tue Dec 26 17:12:16 2017        (r327217)
+++ head/sys/vm/vm_map.c        Tue Dec 26 17:59:37 2017        (r327218)
@@ -132,6 +132,9 @@ static int vmspace_zinit(void *mem, int size, int flag
 static int vm_map_zinit(void *mem, int ize, int flags);
 static void _vm_map_init(vm_map_t map, pmap_t pmap, vm_offset_t min,
     vm_offset_t max);
+static int vm_map_alignspace(vm_map_t map, vm_object_t object,
+    vm_ooffset_t offset, vm_offset_t *addr, vm_size_t length,
+    vm_offset_t max_addr, vm_offset_t alignment);
 static void vm_map_entry_deallocate(vm_map_entry_t entry, boolean_t 
system_map);
 static void vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry);
 static void vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry);
@@ -1484,6 +1487,70 @@ vm_map_fixed(vm_map_t map, vm_object_t object, vm_ooff
 }
 
 /*
+ * Searches for the specified amount of free space in the given map with the
+ * specified alignment.  Performs an address-ordered, first-fit search from
+ * the given address "*addr", with an optional upper bound "max_addr".  If the
+ * parameter "alignment" is zero, then the alignment is computed from the
+ * given (object, offset) pair so as to enable the greatest possible use of
+ * superpage mappings.  Returns KERN_SUCCESS and the address of the free space
+ * in "*addr" if successful.  Otherwise, returns KERN_NO_SPACE.
+ *
+ * The map must be locked.  Initially, there must be at least "length" bytes
+ * of free space at the given address.
+ */
+static int
+vm_map_alignspace(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
+    vm_offset_t *addr, vm_size_t length, vm_offset_t max_addr,
+    vm_offset_t alignment)
+{
+       vm_offset_t aligned_addr, free_addr;
+
+       VM_MAP_ASSERT_LOCKED(map);
+       free_addr = *addr;
+       KASSERT(!vm_map_findspace(map, free_addr, length, addr) &&
+           free_addr == *addr, ("caller provided insufficient free space"));
+       for (;;) {
+               /*
+                * At the start of every iteration, the free space at address
+                * "*addr" is at least "length" bytes.
+                */
+               if (alignment == 0)
+                       pmap_align_superpage(object, offset, addr, length);
+               else if ((*addr & (alignment - 1)) != 0) {
+                       *addr &= ~(alignment - 1);
+                       *addr += alignment;
+               }
+               aligned_addr = *addr;
+               if (aligned_addr == free_addr) {
+                       /*
+                        * Alignment did not change "*addr", so "*addr" must
+                        * still provide sufficient free space.
+                        */
+                       return (KERN_SUCCESS);
+               }
+
+               /*
+                * Test for address wrap on "*addr".  A wrapped "*addr" could
+                * be a valid address, in which case vm_map_findspace() cannot
+                * be relied upon to fail.
+                */
+               if (aligned_addr < free_addr ||
+                   vm_map_findspace(map, aligned_addr, length, addr) ||
+                   (max_addr != 0 && *addr + length > max_addr))
+                       return (KERN_NO_SPACE);
+               free_addr = *addr;
+               if (free_addr == aligned_addr) {
+                       /*
+                        * If a successful call to vm_map_findspace() did not
+                        * change "*addr", then "*addr" must still be aligned
+                        * and provide sufficient free space.
+                        */
+                       return (KERN_SUCCESS);
+               }
+       }
+}
+
+/*
  *     vm_map_find finds an unallocated region in the target address
  *     map with the given length.  The search is defined to be
  *     first-fit from the specified address; the region found is
@@ -1498,8 +1565,8 @@ vm_map_find(vm_map_t map, vm_object_t object, vm_ooffs
            vm_size_t length, vm_offset_t max_addr, int find_space,
            vm_prot_t prot, vm_prot_t max, int cow)
 {
-       vm_offset_t alignment, initial_addr, start;
-       int result;
+       vm_offset_t alignment, min_addr;
+       int rv;
 
        KASSERT((cow & (MAP_STACK_GROWS_DOWN | MAP_STACK_GROWS_UP)) == 0 ||
            object == NULL,
@@ -1512,50 +1579,39 @@ vm_map_find(vm_map_t map, vm_object_t object, vm_ooffs
                alignment = (vm_offset_t)1 << (find_space >> 8);
        } else
                alignment = 0;
-       initial_addr = *addr;
        vm_map_lock(map);
+       if (find_space != VMFS_NO_SPACE) {
+               KASSERT(find_space == VMFS_ANY_SPACE ||
+                   find_space == VMFS_OPTIMAL_SPACE ||
+                   find_space == VMFS_SUPER_SPACE ||
+                   alignment != 0, ("unexpected VMFS flag"));
+               min_addr = *addr;
 again:
-       start = initial_addr;
-       do {
-               if (find_space != VMFS_NO_SPACE) {
-                       if (vm_map_findspace(map, start, length, addr) ||
-                           (max_addr != 0 && *addr + length > max_addr)) {
-                               if (find_space == VMFS_OPTIMAL_SPACE) {
-                                       find_space = VMFS_ANY_SPACE;
-                                       goto again;
-                               }
-                               vm_map_unlock(map);
-                               return (KERN_NO_SPACE);
+               if (vm_map_findspace(map, min_addr, length, addr) ||
+                   (max_addr != 0 && *addr + length > max_addr)) {
+                       rv = KERN_NO_SPACE;
+                       goto done;
+               }
+               if (find_space != VMFS_ANY_SPACE &&
+                   (rv = vm_map_alignspace(map, object, offset, addr, length,
+                   max_addr, alignment)) != KERN_SUCCESS) {
+                       if (find_space == VMFS_OPTIMAL_SPACE) {
+                               find_space = VMFS_ANY_SPACE;
+                               goto again;
                        }
-                       switch (find_space) {
-                       case VMFS_SUPER_SPACE:
-                       case VMFS_OPTIMAL_SPACE:
-                               pmap_align_superpage(object, offset, addr,
-                                   length);
-                               break;
-                       case VMFS_ANY_SPACE:
-                               break;
-                       default:
-                               if ((*addr & (alignment - 1)) != 0) {
-                                       *addr &= ~(alignment - 1);
-                                       *addr += alignment;
-                               }
-                               break;
-                       }
-
-                       start = *addr;
+                       goto done;
                }
-               if ((cow & (MAP_STACK_GROWS_DOWN | MAP_STACK_GROWS_UP)) != 0) {
-                       result = vm_map_stack_locked(map, start, length,
-                           sgrowsiz, prot, max, cow);
-               } else {
-                       result = vm_map_insert(map, object, offset, start,
-                           start + length, prot, max, cow);
-               }
-       } while (result == KERN_NO_SPACE && find_space != VMFS_NO_SPACE &&
-           find_space != VMFS_ANY_SPACE);
+       }
+       if ((cow & (MAP_STACK_GROWS_DOWN | MAP_STACK_GROWS_UP)) != 0) {
+               rv = vm_map_stack_locked(map, *addr, length, sgrowsiz, prot,
+                   max, cow);
+       } else {
+               rv = vm_map_insert(map, object, offset, *addr, *addr + length,
+                   prot, max, cow);
+       }
+done:
        vm_map_unlock(map);
-       return (result);
+       return (rv);
 }
 
 /*
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to