Date: Wed, 14 Dec 2011 22:22:19 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r228509 - in head: share/man/man9 sys/kern sys/sys Message-ID: <201112142222.pBEMMJ1U017244@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jhb Date: Wed Dec 14 22:22:19 2011 New Revision: 228509 URL: http://svn.freebsd.org/changeset/base/228509 Log: Add a helper API to allow in-kernel code to map portions of shared memory objects created by shm_open(2) into the kernel's address space. This provides a convenient way for creating shared memory buffers between userland and the kernel without requiring custom character devices. Added: head/share/man/man9/shm_map.9 (contents, props changed) Modified: head/share/man/man9/Makefile head/sys/kern/uipc_shm.c head/sys/sys/mman.h Modified: head/share/man/man9/Makefile ============================================================================== --- head/share/man/man9/Makefile Wed Dec 14 22:14:05 2011 (r228508) +++ head/share/man/man9/Makefile Wed Dec 14 22:22:19 2011 (r228509) @@ -234,6 +234,7 @@ MAN= accept_filter.9 \ sema.9 \ sf_buf.9 \ sglist.9 \ + shm_map.9 \ signal.9 \ sleep.9 \ sleepqueue.9 \ @@ -1111,6 +1112,7 @@ MLINKS+=sglist.9 sglist_alloc.9 \ sglist.9 sglist_reset.9 \ sglist.9 sglist_slice.9 \ sglist.9 sglist_split.9 +MLINKS+=shm_map.9 shm_unmap.9 MLINKS+=signal.9 cursig.9 \ signal.9 execsigs.9 \ signal.9 issignal.9 \ Added: head/share/man/man9/shm_map.9 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/share/man/man9/shm_map.9 Wed Dec 14 22:22:19 2011 (r228509) @@ -0,0 +1,187 @@ +.\" +.\" Copyright (c) 2011 Advanced Computing Technologies LLC +.\" Written by: John H. Baldwin <jhb@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 14, 2011 +.Dt SHM_MAP 9 +.Os +.Sh NAME +.Nm shm_map , +.Nm shm_unmap +.Nd map shared memory objects into the kernel's address space +.Sh SYNOPSIS +.In sys/types.h +.In sys/mman.h +.Ft int +.Fn shm_map "struct file *fp" "size_t size" "off_t offset" "void **memp" +.Ft int +.Fn shm_unmap "struct file *fp" "void *mem" "size_t size" +.Sh DESCRIPTION +The +.Nm shm_map +and +.Nm shm_unmap +functions provide an API for mapping shared memory objects into the kernel. +Shared memory objects are created by +.Xr shm_open 2 . +These objects can then be passed into the kernel via file descriptors. +.Pp +A shared memory object cannot be shrunk while it is mapped into the kernel. +This is to avoid invalidating any pages that may be wired into the kernel's +address space. +Shared memory objects can still be grown while mapped into the kernel. +.Pp +To simplify the accounting needed to enforce the above requirement, +callers of this API are required to unmap the entire region mapped by +.Nm shm_map +when calling +.Nm shm_unmap . +Unmapping only a portion of the region is not permitted. +.Pp +The +.Nm shm_map +function locates the shared memory object associated with the open file +.Fa fp . +It maps the region of that object described by +.Fa offset +and +.Fa size +into the kernel's address space. +If it succeeds, +.Fa *memp +will be set to the start of the mapping. +All pages for the range will be wired into memory upon successful return. +.Pp +The +.Nm shm_unmap +function unmaps a region previously mapped by +.Nm shm_map . +The +.Fa mem +argument should match the value previously returned in +.Fa *memp , +and the +.Fa size +argument should match the value passed to +.Nm shm_map . +.Pp +Note that +.Nm shm_map +will not hold an extra reference on the open file +.Fa fp +for the lifetime of the mapping. +Instead, +the calling code is required to do this if it wishes to use +.Nm shm_unmap +on the region in the future. +.Sh RETURN VALUES +The +.Nm shm_map +and +.Nm shm_unmap +functions return zero on success or an error on failure. +.Sh EXAMPLES +The following function accepts a file descriptor for a shared memory +object. +It maps the first sixteen kilobytes of the object into the kernel, +performs some work on that address, +and then unmaps the address before returning. +.Bd -literal +int +shm_example(int fd) +{ + struct file *fp; + void *mem; + int error; + + error = fget(curthread, fd, CAP_MMAP, &fp) + if (error) + return (error); + error = shm_map(fp, 16384, 0, &mem); + if (error) { + fdrop(fp, curthread); + return (error); + } + + /* Do something with 'mem'. */ + + error = shm_unmap(fp, mem, 16384); + fdrop(fp, curthread); + return (error); +} +.Ed +.Sh ERRORS +The +.Nm shm_map +function returns the following errors on failure: +.Bl -tag -width Er +.It Bq Er EINVAL +The open file +.Fa fp +is not a shared memory object. +.It Bq Er EINVAL +The requested region described by +.Fa offset +and +.Fa size +extends beyond the end of the shared memory object. +.It Bq Er ENOMEM +Insufficient address space was available. +.It Bq Er EACCES +The shared memory object could not be mapped due to a protection error. +.It Bq Er EINVAL +The shared memory object could not be mapped due to some other VM error. +.El +.Pp +The +.Nm shm_unmap +function returns the following errors on failure: +.Bl -tag -width Er +.It Bq Er EINVAL +The open file +.Fa fp +is not a shared memory object. +.It Bq Er EINVAL +The address range described by +.Fa mem +and +.Fa size +is not a valid address range. +.It Bq Er EINVAL +The address range described by +.Fa mem +and +.Fa size +is not backed by the shared memory object associated with the open file +.Fa fp , +or the address range does not cover the entire mapping of the object. +.El +.Sh SEE ALSO +.Xr shm_open 2 +.Sh HISTORY +This API was first introduced in +.Fx 10.0 . Modified: head/sys/kern/uipc_shm.c ============================================================================== --- head/sys/kern/uipc_shm.c Wed Dec 14 22:14:05 2011 (r228508) +++ head/sys/kern/uipc_shm.c Wed Dec 14 22:22:19 2011 (r228509) @@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$"); #include <vm/vm_param.h> #include <vm/pmap.h> #include <vm/vm_map.h> +#include <vm/vm_kern.h> #include <vm/vm_object.h> #include <vm/vm_page.h> #include <vm/vm_pager.h> @@ -265,6 +266,14 @@ shm_dotruncate(struct shmfd *shmfd, off_ /* Are we shrinking? If so, trim the end. */ if (length < shmfd->shm_size) { + /* + * Disallow any requests to shrink the size if this + * object is mapped into the kernel. + */ + if (shmfd->shm_kmappings > 0) { + VM_OBJECT_UNLOCK(object); + return (EBUSY); + } delta = ptoa(object->size - nobjsize); /* Toss in memory pages. */ @@ -725,3 +734,113 @@ out: mtx_unlock(&shm_timestamp_lock); return (error); } + +/* + * Helper routines to allow the backing object of a shared memory file + * descriptor to be mapped in the kernel. + */ +int +shm_map(struct file *fp, size_t size, off_t offset, void **memp) +{ + struct shmfd *shmfd; + vm_offset_t kva, ofs; + vm_object_t obj; + int rv; + + if (fp->f_type != DTYPE_SHM) + return (EINVAL); + shmfd = fp->f_data; + obj = shmfd->shm_object; + VM_OBJECT_LOCK(obj); + /* + * XXXRW: This validation is probably insufficient, and subject to + * sign errors. It should be fixed. + */ + if (offset >= shmfd->shm_size || + offset + size > round_page(shmfd->shm_size)) { + VM_OBJECT_UNLOCK(obj); + return (EINVAL); + } + + shmfd->shm_kmappings++; + vm_object_reference_locked(obj); + VM_OBJECT_UNLOCK(obj); + + /* Map the object into the kernel_map and wire it. */ + kva = vm_map_min(kernel_map); + ofs = offset & PAGE_MASK; + offset = trunc_page(offset); + size = round_page(size + ofs); + rv = vm_map_find(kernel_map, obj, offset, &kva, size, + VMFS_ALIGNED_SPACE, VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE, 0); + if (rv == KERN_SUCCESS) { + rv = vm_map_wire(kernel_map, kva, kva + size, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); + if (rv == KERN_SUCCESS) { + *memp = (void *)(kva + ofs); + return (0); + } + vm_map_remove(kernel_map, kva, kva + size); + } else + vm_object_deallocate(obj); + + /* On failure, drop our mapping reference. */ + VM_OBJECT_LOCK(obj); + shmfd->shm_kmappings--; + VM_OBJECT_UNLOCK(obj); + + switch (rv) { + case KERN_INVALID_ADDRESS: + case KERN_NO_SPACE: + return (ENOMEM); + case KERN_PROTECTION_FAILURE: + return (EACCES); + default: + return (EINVAL); + } +} + +/* + * We require the caller to unmap the entire entry. This allows us to + * safely decrement shm_kmappings when a mapping is removed. + */ +int +shm_unmap(struct file *fp, void *mem, size_t size) +{ + struct shmfd *shmfd; + vm_map_entry_t entry; + vm_offset_t kva, ofs; + vm_object_t obj; + vm_pindex_t pindex; + vm_prot_t prot; + boolean_t wired; + vm_map_t map; + int rv; + + if (fp->f_type != DTYPE_SHM) + return (EINVAL); + shmfd = fp->f_data; + kva = (vm_offset_t)mem; + ofs = kva & PAGE_MASK; + kva = trunc_page(kva); + size = round_page(size + ofs); + map = kernel_map; + rv = vm_map_lookup(&map, kva, VM_PROT_READ | VM_PROT_WRITE, &entry, + &obj, &pindex, &prot, &wired); + if (rv != KERN_SUCCESS) + return (EINVAL); + if (entry->start != kva || entry->end != kva + size) { + vm_map_lookup_done(map, entry); + return (EINVAL); + } + vm_map_lookup_done(map, entry); + if (obj != shmfd->shm_object) + return (EINVAL); + vm_map_remove(map, kva, kva + size); + VM_OBJECT_LOCK(obj); + KASSERT(shmfd->shm_kmappings > 0, ("shm_unmap: object not mapped")); + shmfd->shm_kmappings--; + VM_OBJECT_UNLOCK(obj); + return (0); +} Modified: head/sys/sys/mman.h ============================================================================== --- head/sys/sys/mman.h Wed Dec 14 22:14:05 2011 (r228508) +++ head/sys/sys/mman.h Wed Dec 14 22:22:19 2011 (r228509) @@ -181,6 +181,8 @@ typedef __size_t size_t; #ifdef _KERNEL #include <vm/vm.h> +struct file; + struct shmfd { size_t shm_size; vm_object_t shm_object; @@ -188,6 +190,7 @@ struct shmfd { uid_t shm_uid; gid_t shm_gid; mode_t shm_mode; + int shm_kmappings; /* * Values maintained solely to make this a better-behaved file @@ -203,6 +206,8 @@ struct shmfd { int shm_mmap(struct shmfd *shmfd, vm_size_t objsize, vm_ooffset_t foff, vm_object_t *obj); +int shm_map(struct file *fp, size_t size, off_t offset, void **memp); +int shm_unmap(struct file *fp, void *mem, size_t size); #else /* !_KERNEL */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201112142222.pBEMMJ1U017244>