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:
parent
58a1ff3bf4
commit
fb680e16f4
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228509
@ -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
187
share/man/man9/shm_map.9
Normal 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 .
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user