create.c
Go to the documentation of this file.
/*
* libspe2 - A wrapper library to adapt the JSRE SPU usage model to SPUFS
* Copyright (C) 2005 IBM Corp.
*
* 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; either version 2.1 of the License,
* or (at your option) any later version.
*
* 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, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/spu.h>
#include <sys/stat.h>
#include <unistd.h>
#include "create.h"
#include "spebase.h"
struct fd_attr {
const char *name;
int mode;
};
static const struct fd_attr spe_fd_attr[NUM_MBOX_FDS] = {
[FD_MBOX] = { .name = "mbox", .mode = O_RDONLY },
[FD_MBOX_STAT] = { .name = "mbox_stat", .mode = O_RDONLY },
[FD_IBOX] = { .name = "ibox", .mode = O_RDONLY },
[FD_IBOX_NB] = { .name = "ibox", .mode = O_RDONLY |O_NONBLOCK },
[FD_IBOX_STAT] = { .name = "ibox_stat", .mode = O_RDONLY },
[FD_WBOX] = { .name = "wbox", .mode = O_WRONLY },
[FD_WBOX_NB] = { .name = "wbox", .mode = O_WRONLY|O_NONBLOCK },
[FD_WBOX_STAT] = { .name = "wbox_stat", .mode = O_RDONLY },
[FD_SIG1] = { .name = "signal1", .mode = O_WRONLY },
[FD_SIG2] = { .name = "signal2", .mode = O_WRONLY },
[FD_MFC] = { .name = "mfc", .mode = O_RDWR },
[FD_MSS] = { .name = "mss", .mode = O_RDWR },
};
static void *mapfileat(int dir, const char *filename, int size)
{
int fd_temp;
void *ret;
fd_temp = openat(dir, filename, O_RDWR);
if (fd_temp < 0) {
DEBUG_PRINTF("ERROR: Could not open SPE %s file.\n", filename);
errno = EFAULT;
return MAP_FAILED;
}
ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_temp, 0);
close(fd_temp);
return ret;
}
static int setsignotify(int dir, const char *filename)
{
int fd_sig, rc = 0;
char one = '1';
fd_sig = openat(dir, filename, O_RDWR);
if (fd_sig < 0)
return -1;
if (write(fd_sig, &one, sizeof(one)) != sizeof(one))
rc = -1;
close(fd_sig);
return 0;
}
void _base_spe_context_lock(spe_context_ptr_t spe, enum fd_name fdesc)
{
pthread_mutex_lock(&spe->base_private->fd_lock[fdesc]);
}
void _base_spe_context_unlock(spe_context_ptr_t spe, enum fd_name fdesc)
{
pthread_mutex_unlock(&spe->base_private->fd_lock[fdesc]);
}
int _base_spe_open_if_closed(struct spe_context *spe, enum fd_name fdesc, int locked)
{
if (!locked)
_base_spe_context_lock(spe, fdesc);
/* already open? */
if (spe->base_private->spe_fds_array[fdesc] != -1) {
spe->base_private->spe_fds_refcount[fdesc]++;
} else {
spe->base_private->spe_fds_array[fdesc] =
openat(spe->base_private->fd_spe_dir,
spe_fd_attr[fdesc].name,
spe_fd_attr[fdesc].mode);
if (spe->base_private->spe_fds_array[(int)fdesc] > 0)
spe->base_private->spe_fds_refcount[(int)fdesc]++;
}
if (!locked)
_base_spe_context_unlock(spe, fdesc);
return spe->base_private->spe_fds_array[(int)fdesc];
}
void _base_spe_close_if_open(struct spe_context *spe, enum fd_name fdesc)
{
_base_spe_context_lock(spe, fdesc);
if (spe->base_private->spe_fds_array[(int)fdesc] != -1 &&
spe->base_private->spe_fds_refcount[(int)fdesc] == 1) {
spe->base_private->spe_fds_refcount[(int)fdesc]--;
close(spe->base_private->spe_fds_array[(int)fdesc]);
spe->base_private->spe_fds_array[(int)fdesc] = -1;
} else if (spe->base_private->spe_fds_refcount[(int)fdesc] > 0) {
spe->base_private->spe_fds_refcount[(int)fdesc]--;
}
_base_spe_context_unlock(spe, fdesc);
}
static int free_spe_context(struct spe_context *spe)
{
int i;
if (spe->base_private->psmap_mmap_base != MAP_FAILED) {
munmap(spe->base_private->psmap_mmap_base, PSMAP_SIZE);
} else {
if (spe->base_private->mfc_mmap_base != MAP_FAILED)
munmap(spe->base_private->mfc_mmap_base, MFC_SIZE);
if (spe->base_private->mssync_mmap_base != MAP_FAILED)
munmap(spe->base_private->mssync_mmap_base, MSS_SIZE);
if (spe->base_private->cntl_mmap_base != MAP_FAILED)
munmap(spe->base_private->cntl_mmap_base, CNTL_SIZE);
if (spe->base_private->signal1_mmap_base != MAP_FAILED)
munmap(spe->base_private->signal1_mmap_base,
SIGNAL_SIZE);
if (spe->base_private->signal2_mmap_base != MAP_FAILED)
munmap(spe->base_private->signal2_mmap_base,
SIGNAL_SIZE);
}
if (spe->base_private->mem_mmap_base != MAP_FAILED)
munmap(spe->base_private->mem_mmap_base, LS_SIZE);
for (i = 0; i < NUM_MBOX_FDS; i++) {
if (spe->base_private->spe_fds_array[i] >= 0)
close(spe->base_private->spe_fds_array[i]);
pthread_mutex_destroy(&spe->base_private->fd_lock[i]);
}
if (spe->base_private->fd_spe_dir >= 0)
close(spe->base_private->fd_spe_dir);
free(spe->base_private);
free(spe);
return 0;
}
spe_context_ptr_t _base_spe_context_create(unsigned int flags,
spe_gang_context_ptr_t gctx, spe_context_ptr_t aff_spe)
{
char pathname[256];
int i, aff_spe_fd = 0;
unsigned int spu_createflags = 0;
struct spe_context *spe = NULL;
struct spe_context_base_priv *priv;
/* We need a loader present to run in emulated isolated mode */
if (flags & SPE_ISOLATE_EMULATE
&& !_base_spe_emulated_loader_present()) {
errno = EINVAL;
return NULL;
}
/* Put some sane defaults into the SPE context */
spe = malloc(sizeof(*spe));
if (!spe) {
DEBUG_PRINTF("ERROR: Could not allocate spe context.\n");
return NULL;
}
memset(spe, 0, sizeof(*spe));
spe->base_private = malloc(sizeof(*spe->base_private));
if (!spe->base_private) {
DEBUG_PRINTF("ERROR: Could not allocate "
"spe->base_private context.\n");
free(spe);
return NULL;
}
/* just a convenience variable */
priv = spe->base_private;
priv->fd_spe_dir = -1;
priv->mem_mmap_base = MAP_FAILED;
priv->psmap_mmap_base = MAP_FAILED;
priv->mssync_mmap_base = MAP_FAILED;
priv->mfc_mmap_base = MAP_FAILED;
priv->cntl_mmap_base = MAP_FAILED;
priv->signal1_mmap_base = MAP_FAILED;
priv->signal2_mmap_base = MAP_FAILED;
priv->loaded_program = NULL;
for (i = 0; i < NUM_MBOX_FDS; i++) {
priv->spe_fds_array[i] = -1;
pthread_mutex_init(&priv->fd_lock[i], NULL);
}
/* initialise spu_createflags */
if (flags & SPE_ISOLATE) {
flags |= SPE_MAP_PS;
spu_createflags |= SPU_CREATE_ISOLATE | SPU_CREATE_NOSCHED;
}
if (flags & SPE_EVENTS_ENABLE)
spu_createflags |= SPU_CREATE_EVENTS_ENABLED;
if (aff_spe)
spu_createflags |= SPU_CREATE_AFFINITY_SPU;
if (flags & SPE_AFFINITY_MEMORY)
spu_createflags |= SPU_CREATE_AFFINITY_MEM;
/* Make the SPUFS directory for the SPE */
if (gctx == NULL)
sprintf(pathname, "/spu/spethread-%i-%lu",
getpid(), (unsigned long)spe);
else
sprintf(pathname, "/spu/%s/spethread-%i-%lu",
gctx->base_private->gangname, getpid(),
(unsigned long)spe);
if (aff_spe)
aff_spe_fd = aff_spe->base_private->fd_spe_dir;
priv->fd_spe_dir = spu_create(pathname, spu_createflags,
S_IRUSR | S_IWUSR | S_IXUSR, aff_spe_fd);
if (priv->fd_spe_dir < 0) {
int errno_saved = errno; /* save errno to prevent being overwritten */
DEBUG_PRINTF("ERROR: Could not create SPE %s\n", pathname);
perror("spu_create()");
free_spe_context(spe);
/* we mask most errors, but leave ENODEV, etc */
switch (errno_saved) {
case ENOTSUP:
case EEXIST:
case EINVAL:
case EBUSY:
case EPERM:
case ENODEV:
errno = errno_saved; /* restore errno */
break;
default:
errno = EFAULT;
break;
}
return NULL;
}
priv->flags = flags;
/* Map the required areas into process memory */
priv->mem_mmap_base = mapfileat(priv->fd_spe_dir, "mem", LS_SIZE);
if (priv->mem_mmap_base == MAP_FAILED) {
DEBUG_PRINTF("ERROR: Could not map SPE memory.\n");
free_spe_context(spe);
errno = ENOMEM;
return NULL;
}
if (flags & SPE_MAP_PS) {
/* It's possible to map the entire problem state area with
* one mmap - try this first */
priv->psmap_mmap_base = mapfileat(priv->fd_spe_dir,
"psmap", PSMAP_SIZE);
if (priv->psmap_mmap_base != MAP_FAILED) {
priv->mssync_mmap_base =
priv->psmap_mmap_base + MSSYNC_OFFSET;
priv->mfc_mmap_base =
priv->psmap_mmap_base + MFC_OFFSET;
priv->cntl_mmap_base =
priv->psmap_mmap_base + CNTL_OFFSET;
priv->signal1_mmap_base =
priv->psmap_mmap_base + SIGNAL1_OFFSET;
priv->signal2_mmap_base =
priv->psmap_mmap_base + SIGNAL2_OFFSET;
} else {
/* map each region separately */
priv->mfc_mmap_base =
mapfileat(priv->fd_spe_dir, "mfc", MFC_SIZE);
priv->mssync_mmap_base =
mapfileat(priv->fd_spe_dir, "mss", MSS_SIZE);
priv->cntl_mmap_base =
mapfileat(priv->fd_spe_dir, "cntl", CNTL_SIZE);
priv->signal1_mmap_base =
mapfileat(priv->fd_spe_dir, "signal1",
SIGNAL_SIZE);
priv->signal2_mmap_base =
mapfileat(priv->fd_spe_dir, "signal2",
SIGNAL_SIZE);
if (priv->mfc_mmap_base == MAP_FAILED ||
priv->cntl_mmap_base == MAP_FAILED ||
priv->signal1_mmap_base == MAP_FAILED ||
priv->signal2_mmap_base == MAP_FAILED) {
DEBUG_PRINTF("ERROR: Could not map SPE "
"PS memory.\n");
free_spe_context(spe);
errno = ENOMEM;
return NULL;
}
}
}
if (flags & SPE_CFG_SIGNOTIFY1_OR) {
if (setsignotify(priv->fd_spe_dir, "signal1_type")) {
DEBUG_PRINTF("ERROR: Could not open SPE "
"signal1_type file.\n");
free_spe_context(spe);
errno = EFAULT;
return NULL;
}
}
if (flags & SPE_CFG_SIGNOTIFY2_OR) {
if (setsignotify(priv->fd_spe_dir, "signal2_type")) {
DEBUG_PRINTF("ERROR: Could not open SPE "
"signal2_type file.\n");
free_spe_context(spe);
errno = EFAULT;
return NULL;
}
}
return spe;
}
static int free_spe_gang_context(struct spe_gang_context *gctx)
{
if (gctx->base_private->fd_gang_dir >= 0)
close(gctx->base_private->fd_gang_dir);
free(gctx->base_private);
free(gctx);
return 0;
}
spe_gang_context_ptr_t _base_spe_gang_context_create(unsigned int flags)
{
char pathname[256];
struct spe_gang_context_base_priv *pgctx = NULL;
struct spe_gang_context *gctx = NULL;
gctx = malloc(sizeof(*gctx));
if (!gctx) {
DEBUG_PRINTF("ERROR: Could not allocate spe context.\n");
return NULL;
}
memset(gctx, 0, sizeof(*gctx));
pgctx = malloc(sizeof(*pgctx));
if (!pgctx) {
DEBUG_PRINTF("ERROR: Could not allocate spe context.\n");
free(gctx);
return NULL;
}
memset(pgctx, 0, sizeof(*pgctx));
gctx->base_private = pgctx;
sprintf(gctx->base_private->gangname, "gang-%i-%lu", getpid(),
(unsigned long)gctx);
sprintf(pathname, "/spu/%s", gctx->base_private->gangname);
gctx->base_private->fd_gang_dir = spu_create(pathname, SPU_CREATE_GANG,
S_IRUSR | S_IWUSR | S_IXUSR);
if (gctx->base_private->fd_gang_dir < 0) {
DEBUG_PRINTF("ERROR: Could not create Gang %s\n", pathname);
free_spe_gang_context(gctx);
errno = EFAULT;
return NULL;
}
gctx->base_private->flags = flags;
return gctx;
}
int _base_spe_context_destroy(spe_context_ptr_t spe)
{
int ret = free_spe_context(spe);
__spe_context_update_event();
return ret;
}
int _base_spe_gang_context_destroy(spe_gang_context_ptr_t gctx)
{
return free_spe_gang_context(gctx);
}