This patch implements kernel mremap() support for FreeBSD 4.2. Someday when I get the time to upgrade my machine, I may port this to the latest tot, but for now, I don't want it to get lost. In any case, the code in the latest branch looks remarkably unscathed, so it may work as is.
Interesting to note that this should work for remapping sub-pieces of objects - not tested, and not sure how one would use that, but conceptually, you could mmap() a file, then re-order the pages - perhaps useful for large object database access. I can't guarantee this is bug free, but it seems to work, and is at least a head start on playing large realloc() page remapping games to get rid of the copy overhead. Submitted via send-pr as well, but not sure the remote MTA likes me. Happy hacking... Zachary Amsden, zach at mirapoint.com 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)); _______________________________________________ [EMAIL PROTECTED] mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-hackers To unsubscribe, send any mail to "[EMAIL PROTECTED]"