gcc/liboffloadmic/runtime/emulator/coi_host.cpp

1314 lines
35 KiB
C++

/*
Copyright (c) 2014-2015 Intel Corporation. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
HOLDER 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.
*/
#include "coi_host.h"
#include "coi_version_asm.h"
#define CYCLE_FREQUENCY 1000000000
/* Environment variables. */
extern char **environ;
/* List of directories for removing on exit. */
char **tmp_dirs;
unsigned tmp_dirs_num = 0;
/* Number of emulated MIC engines. */
long num_engines;
/* Mutex to sync parallel execution. */
pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
typedef enum
{
BUFFER_NORMAL,
BUFFER_MEMORY
} buffer_t;
typedef struct
{
COI_ISA_TYPE type;
uint32_t index;
char *dir;
} Engine;
typedef struct
{
char *name;
void *ptr;
} Function;
typedef struct
{
int pipe_host;
int pipe_target;
} Pipeline;
typedef struct
{
pid_t pid;
Engine *engine;
Function **functions;
Pipeline *pipeline;
} Process;
typedef struct
{
buffer_t type;
char *name;
int fd;
int fd_target;
uint64_t size;
void *data;
void *data_target;
Process *process;
} Buffer;
static COIRESULT
read_long_env (const char *env_name, long *var, long var_default)
{
char *str = getenv (env_name);
char *s;
if (!str || *str == '\0')
*var = var_default;
else
{
errno = 0;
*var = strtol (str, &s, 0);
if (errno != 0 || s == str || *s != '\0')
COIERROR ("Variable %s has invalid value.", env_name);
}
return COI_SUCCESS;
}
__attribute__((constructor))
static void
init ()
{
if (read_long_env (OFFLOAD_EMUL_NUM_ENV, &num_engines, 1) == COI_ERROR)
exit (0);
}
/* Helper function for directory removing. */
static COIRESULT remove_directory (char *path)
{
char *file;
struct dirent *entry;
struct stat statfile;
DIR *dir = opendir (path);
if (dir == NULL)
COIERROR ("Cannot open directory %s.", dir);
while (entry = readdir (dir))
{
if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
continue;
MALLOC (char *, file, strlen (path) + strlen (entry->d_name) + 2);
sprintf (file, "%s/%s", path, entry->d_name);
if (stat (file, &statfile) < 0)
COIERROR ("Cannot retrieve information about file %s.", file);
if (S_ISDIR (statfile.st_mode))
{
if (remove_directory (file) == COI_ERROR)
return COI_ERROR;
}
else
{
if (unlink (file) < 0)
COIERROR ("Cannot unlink file %s.", file);
}
free (file);
}
if (closedir (dir) < 0)
COIERROR ("Cannot close directory %s.", path);
if (rmdir (path) < 0)
COIERROR ("Cannot remove directory %s.", path);
return COI_SUCCESS;
}
__attribute__((destructor))
static void
cleanup ()
{
unsigned i;
for (i = 0; i < tmp_dirs_num; i++)
{
remove_directory (tmp_dirs[i]);
free (tmp_dirs[i]);
}
if (tmp_dirs)
free (tmp_dirs);
}
extern "C"
{
COIRESULT
SYMBOL_VERSION (COIBufferCopy, 1) (COIBUFFER dest_buffer,
COIBUFFER source_buffer,
uint64_t dest_offset,
uint64_t source_offset,
uint64_t length,
COI_COPY_TYPE type,
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion) // Ignored
{
COITRACE ("COIBufferCopy");
/* Convert input arguments. */
Buffer *dest = (Buffer *) dest_buffer;
Buffer *source = (Buffer *) source_buffer;
/* Features of liboffload. */
assert (type == COI_COPY_UNSPECIFIED);
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Map buffers if needed. */
if (dest->data == 0 && dest->type == BUFFER_NORMAL)
if (COIBufferMap (dest_buffer, 0, dest->size, (COI_MAP_TYPE) 0,
0, 0, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
if (source->data == 0 && source->type == BUFFER_NORMAL)
if (COIBufferMap (source_buffer, 0, source->size, (COI_MAP_TYPE) 0,
0, 0, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Copy data. */
if (source->data != 0 && dest->data != 0)
memcpy ((void *) ((uintptr_t) dest->data+dest_offset),
(void *) ((uintptr_t) source->data+source_offset), length);
else
{
assert (dest->process == source->process);
Buffer *buffer;
cmd_t cmd = CMD_BUFFER_COPY;
Pipeline *pipeline = dest->process->pipeline;
/* Create intermediary buffer. */
if (COIBufferCreate (length, COI_BUFFER_NORMAL, 0, 0, 1,
(COIPROCESS*) &dest->process,
(COIBUFFER *) &buffer) == COI_ERROR)
return COI_ERROR;
/* Copy from source to intermediary buffer. */
if (source->data == 0)
{
assert (source->data_target != 0);
/* Send data to target. */
WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (pipeline->pipe_target, &(buffer->data_target), sizeof (void *));
WRITE (pipeline->pipe_target, &(source->data_target), sizeof (void *));
WRITE (pipeline->pipe_target, &(buffer->size), sizeof (uint64_t));
/* Receive data from target. */
READ (pipeline->pipe_host, &cmd, sizeof (cmd_t));
}
else
{
if (COIBufferCopy ((COIBUFFER) buffer, source_buffer, 0, source_offset,
length, type, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
}
/* Copy from intermediary buffer to dest. */
if (dest->data == 0)
{
assert (dest->data_target != 0);
/* Send data to target. */
WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (pipeline->pipe_target, &(dest->data_target), sizeof (void *));
WRITE (pipeline->pipe_target, &(buffer->data_target), sizeof (void *));
WRITE (pipeline->pipe_target, &(buffer->size), sizeof (uint64_t));
/* Receive data from target. */
READ (pipeline->pipe_host, &cmd, sizeof (cmd_t));
}
else
{
if (COIBufferCopy (dest_buffer, (COIBUFFER) buffer, dest_offset,
0, length, type, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
}
/* Unmap on target and destroy intermediary buffer. */
if (COIBufferDestroy ((COIBUFFER) buffer) == COI_ERROR)
return COI_ERROR;
}
/* Unmap buffers if needed. */
if (dest->type == BUFFER_NORMAL)
if (COIBufferUnmap ((COIMAPINSTANCE) dest, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
if (source->type == BUFFER_NORMAL)
if (COIBufferUnmap ((COIMAPINSTANCE) source, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferCreate, 1) (uint64_t size,
COI_BUFFER_TYPE type,
uint32_t flags,
const void *init_data,
uint32_t processes_num,
const COIPROCESS *processes,
COIBUFFER *buffer)
{
COITRACE ("COIBufferCreate");
char *shm_name;
cmd_t cmd = CMD_BUFFER_MAP;
int shm_fd;
const int ullong_max_len = 20;
size_t len;
unsigned long long i;
Buffer *buf;
Pipeline *pipeline;
/* Features of liboffload. */
assert (type == COI_BUFFER_NORMAL);
assert ((flags & COI_SINK_MEMORY) == 0);
assert ((flags & COI_SAME_ADDRESS_SINKS) == 0);
assert ((flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0);
assert (init_data == 0);
assert (processes_num == 1);
/* Create shared memory with an unique name. */
MALLOC (char *, shm_name, strlen (SHM_NAME) + ullong_max_len + 1);
for (i = 0; i >= 0; i++)
{
sprintf (shm_name, SHM_NAME"%lu", i);
shm_fd = shm_open (shm_name, O_CLOEXEC | O_CREAT | O_EXCL | O_RDWR,
S_IRUSR | S_IWUSR);
if (shm_fd > 0)
break;
}
if (ftruncate (shm_fd, size) < 0)
COIERROR ("Cannot truncate shared memory file.");
/* Create buffer. */
MALLOC (Buffer *, buf, sizeof (Buffer));
buf->data = 0;
buf->fd = shm_fd;
buf->process = (Process *) processes[0];
buf->size = size;
buf->type = BUFFER_NORMAL;
STRDUP (buf->name, shm_name);
/* Map buffer on target. */
len = strlen (buf->name) + 1;
pipeline = buf->process->pipeline;
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Send data to target. */
WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (pipeline->pipe_target, &len, sizeof (size_t));
WRITE (pipeline->pipe_target, buf->name, len);
WRITE (pipeline->pipe_target, &(buf->size), sizeof (uint64_t));
/* Receive data from target. */
READ (pipeline->pipe_host, &(buf->fd_target), sizeof (int));
READ (pipeline->pipe_host, &(buf->data_target), sizeof (void *));
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
/* Prepare output arguments. */
*buffer = (COIBUFFER) buf;
/* Clean up. */
free (shm_name);
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferCreateFromMemory, 1) (uint64_t size,
COI_BUFFER_TYPE type,
uint32_t flags,
void *memory,
uint32_t processes_num,
const COIPROCESS *processes,
COIBUFFER *buffer)
{
COITRACE ("COIBufferCreateFromMemory");
Buffer *buf;
/* Features of liboffload. */
assert (type == COI_BUFFER_NORMAL);
assert ((flags & COI_SAME_ADDRESS_SINKS) == 0);
assert ((flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0);
assert (processes_num == 1);
/* Create buffer. */
MALLOC (Buffer *, buf, sizeof (Buffer));
buf->data = (flags & COI_SINK_MEMORY) == 0 ? memory : 0;
buf->data_target = (flags & COI_SINK_MEMORY) != 0 ? memory : 0;
buf->process = (Process *) processes[0];
buf->size = size;
buf->type = BUFFER_MEMORY;
/* Prepare output argument. */
*buffer = (COIBUFFER) buf;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferDestroy, 1) (COIBUFFER buffer)
{
COITRACE ("COIBufferDestroy");
cmd_t cmd = CMD_BUFFER_UNMAP;
/* Convert input arguments. */
Buffer *buf = (Buffer *) buffer;
Pipeline *pipeline = buf->process->pipeline;
/* Unmap buffer on host. */
if (buf->data != 0 && buf->type == BUFFER_NORMAL)
if (COIBufferUnmap ((COIMAPINSTANCE) buffer, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Unmap buffer on target. */
if (buf->data_target != 0)
{
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Send data to target. */
WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (pipeline->pipe_target, &(buf->fd_target), sizeof (int));
WRITE (pipeline->pipe_target, &(buf->data_target), sizeof (void *));
WRITE (pipeline->pipe_target, &(buf->size), sizeof (uint64_t));
/* Receive data from target. */
READ (pipeline->pipe_host, &cmd, sizeof (cmd_t));
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
}
/* Unlink shared memory. */
if (buf->type == BUFFER_NORMAL)
{
if (close (buf->fd) < 0)
COIERROR ("Cannot close shared memory file.");
if (shm_unlink (buf->name) < 0)
COIERROR ("Cannot unlink shared memory.");
free (buf->name);
}
/* Clean up. */
free (buf);
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferGetSinkAddress, 1) (COIBUFFER buffer,
uint64_t *data)
{
COITRACE ("COIBufferGetSinkAddress");
/* Convert input arguments. */
Buffer *buf = (Buffer *) buffer;
/* Here should come BUFFER_NORMAL buffer. */
assert (buf->type == BUFFER_NORMAL);
/* Prepare output argument. */
*data = (uint64_t) buf->data_target;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferMap, 1) (COIBUFFER buffer,
uint64_t offset,
uint64_t length,
COI_MAP_TYPE type, // Ignored
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion, // Ignored
COIMAPINSTANCE *map_instance,
void **data)
{
COITRACE ("COIBufferMap");
/* Features of liboffload. */
assert (offset == 0);
/* Convert input arguments. */
Buffer *buf = (Buffer *) buffer;
/* Only BUFFER_NORMAL buffers should come here. */
assert (buf->type == BUFFER_NORMAL);
/* Map shared memory. */
buf->data = mmap (NULL, buf->size, PROT_READ | PROT_WRITE,
MAP_SHARED, buf->fd, 0);
if (buf->data == NULL)
COIERROR ("Cannot map shared memory.");
/* Prepare output arguments. */
if (map_instance != 0)
*map_instance = (COIMAPINSTANCE) buf;
if (data != 0)
*data = buf->data;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferRead, 1) (COIBUFFER buffer,
uint64_t offset,
void *data,
uint64_t length,
COI_COPY_TYPE type,
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion) // Ignored
{
COITRACE ("COIBufferRead");
/* Convert input arguments. */
Buffer *buf = (Buffer *) buffer;
/* Features of liboffload. */
assert (type == COI_COPY_UNSPECIFIED);
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Map buffers if needed. */
if (buf->data == 0 && buf->type == BUFFER_NORMAL)
if (COIBufferMap (buffer, 0, buf->size, (COI_MAP_TYPE) 0,
0, 0, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Copy data. */
memcpy (data, (void *) ((uintptr_t) buf->data+offset), length);
/* Unmap buffers if needed. */
if (buf->type == BUFFER_NORMAL)
if (COIBufferUnmap ((COIMAPINSTANCE) buf, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferSetState, 1) (COIBUFFER buffer,
COIPROCESS process,
COI_BUFFER_STATE state,
COI_BUFFER_MOVE_FLAG flag,
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion) // Ignored
{
COITRACE ("COIBufferSetState");
/* Features of liboffload. */
assert (flag == COI_BUFFER_NO_MOVE);
/* Looks like we have nothing to do here. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferUnmap, 1) (COIMAPINSTANCE map_instance,
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion) // Ignored
{
COITRACE ("COIBufferUnmap");
/* Convert input arguments. */
Buffer *buffer = (Buffer *) map_instance;
/* Only BUFFER_NORMAL buffers should come here. */
assert (buffer->type == BUFFER_NORMAL);
/* Unmap shared memory. */
if (munmap (buffer->data, buffer->size) < 0)
COIERROR ("Cannot unmap shared memory.");
buffer->data = 0;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIBufferWrite, 1) (COIBUFFER buffer,
uint64_t offset,
const void *data,
uint64_t length,
COI_COPY_TYPE type,
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
COIEVENT *completion) // Ignored
{
COITRACE ("COIBufferWrite");
/* Convert input arguments. */
Buffer *buf = (Buffer *) buffer;
/* Features of liboffload. */
assert (type == COI_COPY_UNSPECIFIED);
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Map buffers if needed. */
if (buf->data == 0 && buf->type == BUFFER_NORMAL)
if (COIBufferMap (buffer, 0, buf->size, (COI_MAP_TYPE) 0,
0, 0, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Copy data. */
memcpy ((void *) ((uintptr_t) buf->data+offset), data, length);
/* Unmap buffers if needed. */
if (buf->type == BUFFER_NORMAL)
if (COIBufferUnmap ((COIMAPINSTANCE) buf, 0, 0, 0) == COI_ERROR)
return COI_ERROR;
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIEngineGetCount, 1) (COI_ISA_TYPE isa,
uint32_t *count)
{
COITRACE ("COIEngineGetCount");
/* Features of liboffload. */
assert (isa == COI_ISA_MIC);
/* Prepare output arguments. */
*count = num_engines;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIEngineGetHandle, 1) (COI_ISA_TYPE isa,
uint32_t index,
COIENGINE *handle)
{
COITRACE ("COIEngineGetHandle");
Engine *engine;
/* Features of liboffload. */
assert (isa == COI_ISA_MIC);
/* Check engine index. */
if (index >= num_engines)
COIERROR ("Wrong engine index.");
/* Create engine handle. */
MALLOC (Engine *, engine, sizeof (Engine));
engine->dir = NULL;
engine->index = index;
engine->type = isa;
/* Prepare output argument. */
*handle = (COIENGINE) engine;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIEventWait, 1) (uint16_t events_num, // Ignored
const COIEVENT *events, // Ignored
int32_t timeout, // Ignored
uint8_t wait_all,
uint32_t *signaled_num,
uint32_t *signaled_indices)
{
COITRACE ("COIEventWait");
/* Features of liboffload. */
assert (wait_all == 1);
assert (signaled_num == 0);
assert (signaled_indices == 0);
/* Looks like we have nothing to do here. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIPipelineCreate, 1) (COIPROCESS process,
COI_CPU_MASK mask,
uint32_t stack_size, // Ignored
COIPIPELINE *pipeline)
{
COITRACE ("COIPipelineCreate");
/* Features of liboffload. */
assert (mask == 0);
/* Prepare output arguments. */
*pipeline = (COIPIPELINE) ((Process *) process)->pipeline;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIPipelineDestroy, 1) (COIPIPELINE pipeline)
{
COITRACE ("COIPipelineDestroy");
/* Do nothing here. Pipeline will be closed during COIProcessDestroy. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIPipelineRunFunction, 1) (COIPIPELINE pipeline,
COIFUNCTION function,
uint32_t buffers_num,
const COIBUFFER *buffers,
const COI_ACCESS_FLAGS *access_flags, // Ignored
uint32_t dependencies_num, // Ignored
const COIEVENT *dependencies, // Ignored
const void *misc_data,
uint16_t misc_data_len,
void *return_data,
uint16_t return_data_len,
COIEVENT *completion) // Ignored
{
COITRACE ("COIPipelineRunFunction");
cmd_t cmd = CMD_RUN_FUNCTION;
int ret_len;
uint32_t i;
uint64_t size;
void *ptr;
/* Convert input arguments. */
Buffer **bufs = (Buffer **) buffers;
Function *func = (Function *) function;
Pipeline *pipe = (Pipeline *) pipeline;
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Send data to target. */
WRITE (pipe->pipe_target, &cmd, sizeof (cmd_t));
WRITE (pipe->pipe_target, &(func->ptr), sizeof (void *));
WRITE (pipe->pipe_target, &buffers_num, sizeof (uint32_t));
for (i = 0; i < buffers_num; i++)
{
WRITE (pipe->pipe_target, &(bufs[i]->size), sizeof (uint64_t));
WRITE (pipe->pipe_target, &(bufs[i]->data_target), sizeof (void *));
}
WRITE (pipe->pipe_target, &misc_data_len, sizeof (uint16_t));
if (misc_data_len > 0)
WRITE (pipe->pipe_target, misc_data, misc_data_len);
WRITE (pipe->pipe_target, &return_data_len, sizeof (uint16_t));
/* Receive data from target. In emulator we don't need any asynchronous data
transfer, so we wait for target process whether it has any data or not. */
ret_len = read (pipe->pipe_host, return_data_len > 0 ? return_data : &cmd,
return_data_len > 0 ? return_data_len : sizeof (cmd_t));
if (ret_len == 0)
return COI_PROCESS_DIED;
else if (ret_len != (return_data_len > 0 ? return_data_len : sizeof (cmd_t)))
COIERROR ("Cannot read from pipe.");
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessCreateFromMemory, 1) (COIENGINE engine,
const char *bin_name,
const void *bin_buffer,
uint64_t bin_buffer_len,
int argc,
const char **argv,
uint8_t inherit_env,
const char **additional_env,
uint8_t proxy_active, // Ignored
const char *proxyfs_root, // Ignored
uint64_t buffer_space, // Ignored
const char *lib_search_path,
const char *file_of_origin, // Ignored
uint64_t file_of_origin_offset, // Ignored
COIPROCESS *process)
{
COITRACE ("COIProcessCreateFromMemory");
const int run_max_args_num = 128;
char **envp;
char *run_argv[run_max_args_num];
char *emul_run = getenv (OFFLOAD_EMUL_RUN_ENV);
char *env_name, *tok;
char *pipe_host_path, *pipe_target_path, *pipes_path, *target_exe;
FILE *file;
int fd;
int i, j, env_i, env_num;
int pipe_host, pipe_target;
const int uint_max_len = 11;
pid_t pid;
Pipeline *pipeline;
Process *proc;
/* Features of liboffload. */
assert (argc == 0);
assert (argv == 0);
/* Convert input arguments. */
Engine *eng = (Engine *) engine;
/* Create temporary directory for engine files. */
assert (eng->dir == NULL);
STRDUP (eng->dir, ENGINE_PATH);
if (mkdtemp (eng->dir) == NULL)
COIERROR ("Cannot create temporary directory %s.", eng->dir);
/* Save path to engine directory for clean up on exit. */
tmp_dirs_num++;
tmp_dirs = (char **) realloc (tmp_dirs, tmp_dirs_num * sizeof (char *));
if (!tmp_dirs)
COIERROR ("Cannot allocate memory.");
STRDUP (tmp_dirs[tmp_dirs_num - 1], eng->dir);
/* Create target executable file. */
MALLOC (char *, target_exe, strlen (eng->dir) + strlen (bin_name) + 2);
sprintf (target_exe, "%s/%s", eng->dir, bin_name);
fd = open (target_exe, O_CLOEXEC | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd < 0)
COIERROR ("Cannot create file %s.", target_exe);
file = fdopen (fd, "wb");
if (file == NULL)
COIERROR ("Cannot associate stream with file descriptor.");
if (fwrite (bin_buffer, 1, bin_buffer_len, file) != bin_buffer_len)
COIERROR ("Cannot write in file %s.", target_exe);
if (fclose (file) != 0)
COIERROR ("Cannot close file %s.", target_exe);
/* Fix file permissions. */
if (chmod (target_exe, S_IRWXU) < 0)
COIERROR ("Cannot change permissions for file %s.", target_exe);
/* Create directory for pipes to prevent names collision. */
MALLOC (char *, pipes_path, strlen (PIPES_PATH) + strlen (eng->dir) + 1);
sprintf (pipes_path, "%s" PIPES_PATH, eng->dir);
if (mkdir (pipes_path, S_IRWXU) < 0)
COIERROR ("Cannot create folder %s.", pipes_path);
/* Create pipes. */
MALLOC (char *, pipe_host_path,
strlen (PIPE_HOST_PATH) + strlen (eng->dir) + 1);
MALLOC (char *, pipe_target_path,
strlen (PIPE_TARGET_PATH) + strlen (eng->dir) + 1);
if (pipe_target_path == NULL)
COIERROR ("Cannot allocate memory.");
sprintf (pipe_host_path, "%s" PIPE_HOST_PATH, eng->dir);
sprintf (pipe_target_path, "%s" PIPE_TARGET_PATH, eng->dir);
if (mkfifo (pipe_host_path, S_IRUSR | S_IWUSR) < 0)
COIERROR ("Cannot create pipe %s.", pipe_host_path);
if (mkfifo (pipe_target_path, S_IRUSR | S_IWUSR) < 0)
COIERROR ("Cannot create pipe %s.", pipe_target_path);
/* Prepare argv. */
if (emul_run == NULL || strcmp (emul_run, "") == 0)
{
STRDUP (run_argv[0], target_exe);
run_argv[1] = (char *) NULL;
}
else
{
char *ptr, *tmp;
i = 0;
STRDUP (tmp, emul_run);
tok = strtok_r (tmp, " ", &ptr);
while (tok != NULL)
{
if (i >= run_max_args_num)
COIERROR ("Run command has too many arguments.");
STRDUP (run_argv[i++], tok);
tok = strtok_r (NULL, " ", &ptr);
}
STRDUP (run_argv[i], target_exe);
run_argv[i+1] = (char *) NULL;
free (tmp);
}
/* Prepare envp. */
/* FIXME: take into account additional_env. */
assert (additional_env == NULL);
env_num = 0;
if (inherit_env == true)
while (environ[env_num++]);
env_num += 4; // LD_LIBRARY_PATH, MIC_DIR, MIC_INDEX, NULL
MALLOC (char **, envp, env_num * sizeof (char *));
env_i = 0;
if (inherit_env == true)
for (i = 0; environ[i] != NULL; i++)
{
STRDUP (env_name, environ[i]);
for (j = 0; env_name[j] != '=' && env_name[j] != '\0'; j++);
env_name[j] = '\0';
if (strcmp (env_name, "LD_LIBRARY_PATH") != 0
&& strcmp (env_name, MIC_DIR_ENV) != 0
&& strcmp (env_name, MIC_INDEX_ENV) != 0)
STRDUP (envp[env_i++], environ[i]);
free (env_name);
}
MALLOC (char *, envp[env_i], strlen (MIC_DIR_ENV) + strlen (eng->dir) + 2);
sprintf (envp[env_i], "%s=%s", MIC_DIR_ENV, eng->dir);
MALLOC (char *, envp[env_i+1], strlen (MIC_INDEX_ENV) + uint_max_len + 1);
sprintf (envp[env_i+1], "%s=%u", MIC_INDEX_ENV, eng->index);
MALLOC (char *, envp[env_i+2],
strlen ("LD_LIBRARY_PATH=") + strlen (lib_search_path) + 1);
sprintf (envp[env_i+2], "LD_LIBRARY_PATH=%s", lib_search_path);
envp[env_i+3] = (char *) NULL;
/* Create target process. */
pid = vfork ();
if (pid < 0)
COIERROR ("Cannot create child process.");
if (pid == 0)
{
/* Run target executable. */
if (execvpe (run_argv[0], run_argv, envp) == -1)
COIERROR ("Cannot execute file %s.", target_exe);
}
/* Open pipes. */
pipe_host = open (pipe_host_path, O_CLOEXEC | O_RDONLY);
if (pipe_host < 0)
COIERROR ("Cannot open target-to-host pipe.");
pipe_target = open (pipe_target_path, O_CLOEXEC | O_WRONLY);
if (pipe_target < 0)
COIERROR ("Cannot open host-to-target pipe.");
/* Create pipeline handle. */
MALLOC (Pipeline *, pipeline, sizeof (Pipeline));
pipeline->pipe_host = pipe_host;
pipeline->pipe_target = pipe_target;
/* Create process handle. */
MALLOC (Process *, proc, sizeof (Process));
proc->pid = pid;
proc->engine = eng;
proc->functions = 0;
proc->pipeline = pipeline;
/* Prepare output arguments. */
*process = (COIPROCESS) proc;
/* Clean up. */
for (i = 0; run_argv[i] != NULL; i++)
free (run_argv[i]);
for (i = 0; envp[i] != NULL; i++)
free (envp[i]);
free (envp);
free (pipe_host_path);
free (pipe_target_path);
free (pipes_path);
free (target_exe);
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessCreateFromFile, 1) (COIENGINE in_Engine,
const char *in_pBinaryName,
int in_Argc,
const char **in_ppArgv,
uint8_t in_DupEnv,
const char **in_ppAdditionalEnv,
uint8_t in_ProxyActive,
const char *in_Reserved,
uint64_t in_BufferSpace,
const char *in_LibrarySearchPath,
COIPROCESS *out_pProcess)
{
COITRACE ("COIProcessCreateFromFile");
/* liboffloadmic with GCC compiled binaries should never go here. */
assert (false);
return COI_ERROR;
}
COIRESULT
SYMBOL_VERSION (COIProcessDestroy, 1) (COIPROCESS process,
int32_t wait_timeout, // Ignored
uint8_t force,
int8_t *proc_return,
uint32_t *reason)
{
COITRACE ("COIProcessDestroy");
int i;
/* Convert input arguments. */
Process *proc = (Process *) process;
/* Close pipeline. */
if (close (proc->pipeline->pipe_host) < 0)
COIERROR ("Cannot close target-to-host pipe.");
if (close (proc->pipeline->pipe_target) < 0)
COIERROR ("Cannot close host-to-target pipe.");
free (proc->pipeline);
/* Shutdown target process by force. */
if (force)
kill (proc->pid, SIGTERM);
/* Clean up. */
for (i = 0; proc->functions[i] != 0; i++)
{
free (proc->functions[i]->name);
free (proc->functions[i]);
}
free (proc->engine->dir);
free (proc->engine);
free (proc->functions);
free (proc);
/* Prepare output arguments. */
*proc_return = 0;
*reason = 0;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessGetFunctionHandles, 1) (COIPROCESS process,
uint32_t functions_num,
const char **function_names,
COIFUNCTION *function_handles)
{
COITRACE ("COIProcessGetFunctionHandles");
cmd_t cmd = CMD_GET_FUNCTION_HANDLE;
Function *function;
size_t len;
void *ptr;
uint32_t i;
/* Convert input arguments. */
Process *proc = (Process *) process;
/* This function should be called once for the process. */
assert (proc->functions == 0);
/* Create array of function pointers. Last element is 0, what shows
the end of the array. This array is used to free memory when process
is destroyed. */
proc->functions = (Function **) calloc (functions_num + 1,
sizeof (Function *));
if (proc->functions == NULL)
COIERROR ("Cannot allocate memory.");
/* Get handles for functions. */
for (i = 0; i < functions_num; i++)
{
MALLOC (Function *, function, sizeof (Function));
len = strlen (function_names[i]) + 1;
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Send data to target. */
WRITE (proc->pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (proc->pipeline->pipe_target, &len, sizeof (size_t));
WRITE (proc->pipeline->pipe_target, function_names[i], len);
/* Receive data from target. */
READ (proc->pipeline->pipe_host, &ptr, sizeof (void *));
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
/* Prepare output arguments. */
STRDUP (function->name, function_names[i]);
if (function->name == NULL)
COIERROR ("Cannot allocate memory.");
function->ptr = ptr;
function_handles[i] = (COIFUNCTION) function;
/* Save function pointer. */
proc->functions[i] = function;
}
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessLoadLibraryFromMemory, 2) (COIPROCESS in_Process,
const void *in_pLibraryBuffer,
uint64_t in_LibraryBufferLength,
const char *in_pLibraryName,
const char *in_LibrarySearchPath, // Ignored
const char *in_FileOfOrigin, // Ignored
uint64_t in_FileOfOriginOffset, // Ignored
uint32_t in_Flags, // Ignored
COILIBRARY *out_pLibrary)
{
COITRACE ("COIProcessLoadLibraryFromMemory");
const cmd_t cmd = CMD_OPEN_LIBRARY;
char *lib_path;
int fd;
FILE *file;
size_t len;
/* Convert input arguments. */
Process *proc = (Process *) in_Process;
/* Create target library file. */
MALLOC (char *, lib_path,
strlen (proc->engine->dir) + strlen (in_pLibraryName) + 2);
sprintf (lib_path, "%s/%s", proc->engine->dir, in_pLibraryName);
fd = open (lib_path, O_CLOEXEC | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd < 0)
COIERROR ("Cannot create file %s.", lib_path);
file = fdopen (fd, "wb");
if (file == NULL)
COIERROR ("Cannot associate stream with file descriptor.");
if (fwrite (in_pLibraryBuffer, 1, in_LibraryBufferLength, file)
!= in_LibraryBufferLength)
COIERROR ("Cannot write in file %s.", lib_path);
if (fclose (file) != 0)
COIERROR ("Cannot close file %s.", lib_path);
len = strlen (lib_path) + 1;
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Make target open library. */
WRITE (proc->pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (proc->pipeline->pipe_target, &len, sizeof (size_t));
WRITE (proc->pipeline->pipe_target, lib_path, len);
/* Receive data from target. */
void *handle;
READ (proc->pipeline->pipe_host, &handle, sizeof (void *));
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
/* Clean up. */
free (lib_path);
*out_pLibrary = (COILIBRARY) handle;
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessRegisterLibraries, 1) (uint32_t libraries_num,
const void **libraries,
const uint64_t *library_sizes,
const char **files_of_origin,
const uint64_t *file_of_origin_offsets)
{
COITRACE ("COIProcessRegisterLibraries");
/* Looks like we have nothing to do here. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIProcessUnloadLibrary, 1) (COIPROCESS in_Process,
COILIBRARY in_Library)
{
COITRACE ("COIProcessUnloadLibrary");
const cmd_t cmd = CMD_CLOSE_LIBRARY;
/* Convert input arguments. */
Process *proc = (Process *) in_Process;
/* Start critical section. */
if (pthread_mutex_lock (&mutex) != 0)
COIERROR ("Cannot lock mutex.");
/* Make target close library. */
WRITE (proc->pipeline->pipe_target, &cmd, sizeof (cmd_t));
WRITE (proc->pipeline->pipe_target, &in_Library, sizeof (void *));
/* Finish critical section. */
if (pthread_mutex_unlock (&mutex) != 0)
COIERROR ("Cannot unlock mutex.");
return COI_SUCCESS;
}
uint64_t
SYMBOL_VERSION (COIPerfGetCycleFrequency, 1) ()
{
COITRACE ("COIPerfGetCycleFrequency");
return (uint64_t) CYCLE_FREQUENCY;
}
COIRESULT
SYMBOL_VERSION (COIPipelineClearCPUMask, 1) (COI_CPU_MASK *in_Mask)
{
COITRACE ("COIPipelineClearCPUMask");
/* Looks like we have nothing to do here. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIPipelineSetCPUMask, 1) (COIPROCESS in_Process,
uint32_t in_CoreID,
uint8_t in_ThreadID,
COI_CPU_MASK *out_pMask)
{
COITRACE ("COIPipelineSetCPUMask");
/* Looks like we have nothing to do here. */
return COI_SUCCESS;
}
COIRESULT
SYMBOL_VERSION (COIEngineGetInfo, 1) (COIENGINE in_EngineHandle,
uint32_t in_EngineInfoSize,
COI_ENGINE_INFO *out_pEngineInfo)
{
COITRACE ("COIEngineGetInfo");
out_pEngineInfo->ISA = COI_ISA_x86_64;
out_pEngineInfo->NumCores = 1;
out_pEngineInfo->NumThreads = 8;
out_pEngineInfo->CoreMaxFrequency = SYMBOL_VERSION(COIPerfGetCycleFrequency,1)() / 1000000;
out_pEngineInfo->PhysicalMemory = 1024;
out_pEngineInfo->PhysicalMemoryFree = 1024;
out_pEngineInfo->SwapMemory = 1024;
out_pEngineInfo->SwapMemoryFree = 1024;
out_pEngineInfo->MiscFlags = COI_ENG_ECC_DISABLED;
return COI_SUCCESS;
}
} // extern "C"