1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-06 13:09:50 +00:00

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.
This commit is contained in:
John Baldwin 2011-12-14 22:22:19 +00:00
parent 58a1ff3bf4
commit fb680e16f4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228509
4 changed files with 313 additions and 0 deletions

View File

@ -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 \

187
share/man/man9/shm_map.9 Normal file
View File

@ -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 .

View File

@ -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_t length)
/* 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 @@ shm_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
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);
}

View File

@ -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 */