From owner-freebsd-bugs@FreeBSD.ORG Tue Dec 2 16:00:22 2003 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 5AC0316A4CE for ; Tue, 2 Dec 2003 16:00:22 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 0B40B43FE1 for ; Tue, 2 Dec 2003 16:00:18 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.9/8.12.9) with ESMTP id hB300IFY091109 for ; Tue, 2 Dec 2003 16:00:18 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.9/8.12.9/Submit) id hB300H6u091108; Tue, 2 Dec 2003 16:00:17 -0800 (PST) (envelope-from gnats) Resent-Date: Tue, 2 Dec 2003 16:00:17 -0800 (PST) Resent-Message-Id: <200312030000.hB300H6u091108@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Zachary Amsden Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 747C316A4CF for ; Tue, 2 Dec 2003 15:51:51 -0800 (PST) Received: from sift.mirapoint.com (sift.mirapoint.com [63.107.133.19]) by mx1.FreeBSD.org (Postfix) with ESMTP id E739C4400D for ; Tue, 2 Dec 2003 15:51:12 -0800 (PST) (envelope-from zach@mirapoint.com) Received: from alpo.mirapoint.com (alpo.mirapoint.com [63.107.133.20]) by sift.mirapoint.com (MOS 3.4.2-CR) with ESMTP id AHD08235; Tue, 2 Dec 2003 15:51:06 -0800 (PST) Received: from 63.107.133.19 by alpo.mirapoint.com (MOS 3.4.2-CR) with HTTPS/1.1; Tue, 2 Dec 2003 15:51:06 -0800 Message-Id: <10ff9356.ec22d770.81cce00@alpo.mirapoint.com> Date: Tue, 2 Dec 2003 15:51:06 -0800 From: Zachary Amsden To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/59912: mremap() implementation lacking X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 03 Dec 2003 00:00:22 -0000 >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 @@ -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: