Files
xen/tools/libs/foreignmemory/linux.c
T
Ian Campbell 3248691679 tools: Refactor foreign memory mapping into libxenforeignmemory
libxenforeignmemory will provide a stable API and ABI for mapping
foreign domain memory (subject to appropriate privileges).

The new library exposes an interface equivalent to
xc_map_foreign_memory_bulk, which all the other
xc_map_foreign_memory_* functions (which remain in libxc) are
implemented in terms of.

Upon request (via #define XC_WANT_COMPAT_MAP_FOREIGN_API) libxenctrl
will provide a compat API for the old names. This is used by qemu-xen
and qemu-trad as well as various in tree things (which required
de-dupping various #includes in some too to get the #define before the
first).

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
[ ijc -- updated MINIOS_UPSTREAM_REVISION and QEMU_TRADITIONAL_REVISION ]
2016-01-22 12:24:03 +00:00

294 lines
7.4 KiB
C

/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
*
* Split out from xc_linus_osdep.c:
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
*/
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "private.h"
#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
int osdep_xenforeignmemory_open(xenforeignmemory_handle *fmem)
{
int flags, saved_errno;
int fd = open("/dev/xen/privcmd", O_RDWR); /* prefer this newer interface */
if ( fd == -1 && ( errno == ENOENT || errno == ENXIO || errno == ENODEV ))
{
/* Fallback to /proc/xen/privcmd */
fd = open("/proc/xen/privcmd", O_RDWR);
}
if ( fd == -1 )
{
PERROR("Could not obtain handle on privileged command interface");
return -1;
}
/* Although we return the file handle as the 'xc handle' the API
does not specify / guarentee that this integer is in fact
a file handle. Thus we must take responsiblity to ensure
it doesn't propagate (ie leak) outside the process */
if ( (flags = fcntl(fd, F_GETFD)) < 0 )
{
PERROR("Could not get file handle flags");
goto error;
}
flags |= FD_CLOEXEC;
if ( fcntl(fd, F_SETFD, flags) < 0 )
{
PERROR("Could not set file handle flags");
goto error;
}
fmem->fd = fd;
return 0;
error:
saved_errno = errno;
close(fd);
errno = saved_errno;
return -1;
}
int osdep_xenforeignmemory_close(xenforeignmemory_handle *fmem)
{
int fd = fmem->fd;
if (fd == -1)
return 0;
return close(fd);
}
static int map_foreign_batch_single(int fd, uint32_t dom,
xen_pfn_t *mfn, unsigned long addr)
{
privcmd_mmapbatch_t ioctlx;
int rc;
ioctlx.num = 1;
ioctlx.dom = dom;
ioctlx.addr = addr;
ioctlx.arr = mfn;
do
{
*mfn ^= PRIVCMD_MMAPBATCH_PAGED_ERROR;
usleep(100);
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx);
}
while ( (rc < 0) && (errno == ENOENT) );
return rc;
}
/*
* Retry mmap of all paged gfns in batches
* retuns < 0 on fatal error
* returns 0 if all gfns left paging state
* returns > 0 if some gfns are still in paging state
*
* Walk all gfns and try to assemble blocks of gfns in paging state.
* This will keep the request ring full and avoids delays.
*/
static int retry_paged(int fd, uint32_t dom, void *addr,
const xen_pfn_t *arr, int *err, unsigned int num)
{
privcmd_mmapbatch_v2_t ioctlx;
int rc, paged = 0, i = 0;
do
{
/* Skip gfns not in paging state */
if ( err[i] != -ENOENT )
{
i++;
continue;
}
paged++;
/* At least one gfn is still in paging state */
ioctlx.num = 1;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr + ((unsigned long)i<<PAGE_SHIFT);
ioctlx.arr = arr + i;
ioctlx.err = err + i;
/* Assemble a batch of requests */
while ( ++i < num )
{
if ( err[i] != -ENOENT )
break;
ioctlx.num++;
}
/* Send request and abort on fatal error */
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH_V2, &ioctlx);
if ( rc < 0 && errno != ENOENT )
goto out;
} while ( i < num );
rc = paged;
out:
return rc;
}
void *osdep_xenforeignmemory_map(xenforeignmemory_handle *fmem,
uint32_t dom, int prot,
const xen_pfn_t *arr, int *err, unsigned int num)
{
int fd = fmem->fd;
privcmd_mmapbatch_v2_t ioctlx;
void *addr;
unsigned int i;
int rc;
addr = mmap(NULL, (unsigned long)num << PAGE_SHIFT, prot, MAP_SHARED,
fd, 0);
if ( addr == MAP_FAILED )
{
PERROR("mmap failed");
return NULL;
}
ioctlx.num = num;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr;
ioctlx.arr = arr;
ioctlx.err = err;
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH_V2, &ioctlx);
/* Command was recognized, some gfn in arr are in paging state */
if ( rc < 0 && errno == ENOENT )
{
do {
usleep(100);
rc = retry_paged(fd, dom, addr, arr, err, num);
} while ( rc > 0 );
}
/* Command was not recognized, use fall back */
else if ( rc < 0 && errno == EINVAL && (int)num > 0 )
{
/*
* IOCTL_PRIVCMD_MMAPBATCH_V2 is not supported - fall back to
* IOCTL_PRIVCMD_MMAPBATCH.
*/
privcmd_mmapbatch_t ioctlx;
xen_pfn_t *pfn;
unsigned int pfn_arr_size = ROUNDUP((num * sizeof(*pfn)), PAGE_SHIFT);
if ( pfn_arr_size <= PAGE_SIZE )
pfn = alloca(num * sizeof(*pfn));
else
{
pfn = mmap(NULL, pfn_arr_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_POPULATE, -1, 0);
if ( pfn == MAP_FAILED )
{
PERROR("mmap of pfn array failed");
(void)munmap(addr, (unsigned long)num << PAGE_SHIFT);
return NULL;
}
}
memcpy(pfn, arr, num * sizeof(*arr));
ioctlx.num = num;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr;
ioctlx.arr = pfn;
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx);
rc = rc < 0 ? -errno : 0;
for ( i = 0; i < num; ++i )
{
switch ( pfn[i] ^ arr[i] )
{
case 0:
err[i] = rc != -ENOENT ? rc : 0;
continue;
default:
err[i] = -EINVAL;
continue;
case PRIVCMD_MMAPBATCH_PAGED_ERROR:
if ( rc != -ENOENT )
{
err[i] = rc ?: -EINVAL;
continue;
}
rc = map_foreign_batch_single(fd, dom, pfn + i,
(unsigned long)addr + ((unsigned long)i<<PAGE_SHIFT));
if ( rc < 0 )
{
rc = -errno;
break;
}
rc = -ENOENT;
continue;
}
break;
}
if ( pfn_arr_size > PAGE_SIZE )
munmap(pfn, pfn_arr_size);
if ( rc == -ENOENT && i == num )
rc = 0;
else if ( rc )
{
errno = -rc;
rc = -1;
}
}
if ( rc < 0 )
{
int saved_errno = errno;
PERROR("ioctl failed");
(void)munmap(addr, (unsigned long)num << PAGE_SHIFT);
errno = saved_errno;
return NULL;
}
return addr;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/