/* 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 #include #include #include "coi_host.h" #include "coi_version_asm.h" #define CYCLE_FREQUENCY 1000000000 enum buffer_t { BUFFER_NORMAL, BUFFER_MEMORY }; struct Engine { COI_ISA_TYPE type; uint32_t index; char *dir; }; struct Function { void *ptr; uint32_t num_buffers; uint64_t *bufs_size; void * *bufs_data_target; uint16_t misc_data_len; void *misc_data; uint16_t return_value_len; void *return_value; COIEVENT completion_event; }; struct Callback { COI_EVENT_CALLBACK ptr; const void *data; }; struct Process { pid_t pid; int pipe_host2tgt; int pipe_tgt2host; Engine *engine; void **functions; }; struct Pipeline { pthread_t thread; bool destroy; bool is_destroyed; char *pipe_host2tgt_path; char *pipe_tgt2host_path; int pipe_host2tgt; int pipe_tgt2host; std::queue queue; Process *process; }; struct Buffer { buffer_t type; char *name; int fd; int fd_target; uint64_t size; void *data; void *data_target; Process *process; }; /* Environment variables. */ extern char **environ; /* List of directories for removing on exit. */ static char **tmp_dirs; static unsigned tmp_dirs_num; /* Number of emulated MIC engines. */ static long num_engines; /* Number of the last COI pipeline. */ static uint32_t max_pipeline_num; /* Set of undestroyed pipelines. */ static std::set pipelines; /* Number of the last COI event, the event #0 is always signalled. */ static uint64_t max_event_num = 1; /* Set of created COI events, which are not signalled. */ static std::set non_signalled_events; /* Set of COI events, which encountered errors. */ static std::map errored_events; /* Set of registered callbacks, indexed by event number. */ static std::map callbacks; /* Mutex to sync parallel execution. */ static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; 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 () { for (unsigned i = 0; i < tmp_dirs_num; i++) { remove_directory (tmp_dirs[i]); free (tmp_dirs[i]); } free (tmp_dirs); } static COIRESULT start_critical_section () { if (pthread_mutex_lock (&mutex) != 0) COIERROR ("Cannot lock mutex."); return COI_SUCCESS; } static COIRESULT finish_critical_section () { if (pthread_mutex_unlock (&mutex) != 0) COIERROR ("Cannot unlock mutex."); return COI_SUCCESS; } static bool pipeline_is_destroyed (const Pipeline *pipeline) { start_critical_section (); bool res = pipeline->is_destroyed; finish_critical_section (); return res; } static void maybe_invoke_callback (const COIEVENT event, const COIRESULT result) { std::map::iterator cb = callbacks.find (event.opaque[0]); if (cb != callbacks.end ()) { Callback callback = cb->second; callback.ptr (event, result, callback.data); callbacks.erase (cb); } } static void signal_event (const COIEVENT event, const COIRESULT result) { if (result != COI_SUCCESS) errored_events.insert (std::pair (event.opaque[0], result)); non_signalled_events.erase (event.opaque[0]); maybe_invoke_callback (event, result); } static COIRESULT get_event_result (const COIEVENT event) { COIRESULT res = COI_SUCCESS; std::map::iterator ee = errored_events.find (event.opaque[0]); if (ee != errored_events.end ()) res = ee->second; return res; } extern "C" { COIRESULT SYMBOL_VERSION (COIBufferCopy, 1) (COIBUFFER in_DestBuffer, COIBUFFER in_SourceBuffer, uint64_t in_DestOffset, uint64_t in_SourceOffset, uint64_t in_Length, COI_COPY_TYPE in_Type, uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion) { COITRACE ("COIBufferCopy"); /* Features of liboffloadmic. */ assert (in_DestBuffer != NULL); assert (in_SourceBuffer != NULL); assert (in_Type == COI_COPY_UNSPECIFIED); assert (in_NumDependencies == 0); /* Convert input arguments. */ Buffer *dest = (Buffer *) in_DestBuffer; Buffer *source = (Buffer *) in_SourceBuffer; start_critical_section (); /* Map buffers if needed. */ if (dest->data == 0 && dest->type == BUFFER_NORMAL) if (COIBufferMap (in_DestBuffer, 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 (in_SourceBuffer, 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 + in_DestOffset), (void *) ((uintptr_t) source->data + in_SourceOffset), in_Length); else { assert (dest->process == source->process); Buffer *buffer; cmd_t cmd = CMD_BUFFER_COPY; /* Create intermediary buffer. */ if (COIBufferCreate (in_Length, COI_BUFFER_NORMAL, 0, 0, 1, (COIPROCESS*) &dest->process, (COIBUFFER *) &buffer) == COI_ERROR) return COI_ERROR; int pipe_host2tgt = dest->process->pipe_host2tgt; int pipe_tgt2host = dest->process->pipe_tgt2host; /* Copy from source to intermediary buffer. */ if (source->data == 0) { assert (source->data_target != 0); /* Send data to target. */ WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (pipe_host2tgt, &buffer->data_target, sizeof (void *)); WRITE (pipe_host2tgt, &source->data_target, sizeof (void *)); WRITE (pipe_host2tgt, &buffer->size, sizeof (uint64_t)); /* Receive data from target. */ READ (pipe_tgt2host, &cmd, sizeof (cmd_t)); } else { if (COIBufferCopy ((COIBUFFER) buffer, in_SourceBuffer, 0, in_SourceOffset, in_Length, in_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 (pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (pipe_host2tgt, &dest->data_target, sizeof (void *)); WRITE (pipe_host2tgt, &buffer->data_target, sizeof (void *)); WRITE (pipe_host2tgt, &buffer->size, sizeof (uint64_t)); /* Receive data from target. */ READ (pipe_tgt2host, &cmd, sizeof (cmd_t)); } else { if (COIBufferCopy (in_DestBuffer, (COIBUFFER) buffer, in_DestOffset, 0, in_Length, in_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 (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferCreate, 1) (uint64_t in_Size, COI_BUFFER_TYPE in_Type, uint32_t in_Flags, const void *in_pInitData, uint32_t in_NumProcesses, const COIPROCESS *in_pProcesses, COIBUFFER *out_pBuffer) { COITRACE ("COIBufferCreate"); char *shm_name; int shm_fd; const int ullong_max_len = 20; /* Features of liboffloadmic. */ assert (in_Type == COI_BUFFER_NORMAL); assert ((in_Flags & COI_SINK_MEMORY) == 0); assert ((in_Flags & COI_SAME_ADDRESS_SINKS) == 0); assert ((in_Flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); assert (in_pInitData == NULL); assert (in_NumProcesses == 1); assert (in_pProcesses != NULL); assert (out_pBuffer != NULL); /* Create shared memory with an unique name. */ MALLOC (char *, shm_name, strlen (SHM_NAME) + ullong_max_len + 1); for (unsigned long long 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, in_Size) < 0) COIERROR ("Cannot truncate shared memory file."); /* Create buffer. */ Buffer *buf = new Buffer; buf->data = 0; buf->fd = shm_fd; buf->process = (Process *) in_pProcesses[0]; buf->size = in_Size; buf->type = BUFFER_NORMAL; STRDUP (buf->name, shm_name); /* Map buffer on target. */ size_t len = strlen (buf->name) + 1; start_critical_section (); /* Send data to target. */ const cmd_t cmd = CMD_BUFFER_MAP; int pipe_host2tgt = buf->process->pipe_host2tgt; WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (pipe_host2tgt, &len, sizeof (size_t)); WRITE (pipe_host2tgt, buf->name, len); WRITE (pipe_host2tgt, &buf->size, sizeof (uint64_t)); /* Receive data from target. */ int pipe_tgt2host = buf->process->pipe_tgt2host; READ (pipe_tgt2host, &buf->fd_target, sizeof (int)); READ (pipe_tgt2host, &buf->data_target, sizeof (void *)); finish_critical_section (); /* Prepare output arguments. */ *out_pBuffer = (COIBUFFER) buf; /* Clean up. */ free (shm_name); return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferCreateFromMemory, 1) (uint64_t in_Size, COI_BUFFER_TYPE in_Type, uint32_t in_Flags, void *in_Memory, uint32_t in_NumProcesses, const COIPROCESS *in_pProcesses, COIBUFFER *out_pBuffer) { COITRACE ("COIBufferCreateFromMemory"); /* Features of liboffloadmic. */ assert (in_Type == COI_BUFFER_NORMAL); assert ((in_Flags & COI_SAME_ADDRESS_SINKS) == 0); assert ((in_Flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); assert (in_NumProcesses == 1); assert (in_pProcesses != NULL); assert (out_pBuffer != NULL); /* Create buffer. */ Buffer *buf = new Buffer; buf->data = (in_Flags & COI_SINK_MEMORY) == 0 ? in_Memory : 0; buf->data_target = (in_Flags & COI_SINK_MEMORY) != 0 ? in_Memory : 0; buf->process = (Process *) in_pProcesses[0]; buf->size = in_Size; buf->type = BUFFER_MEMORY; /* Prepare output argument. */ *out_pBuffer = (COIBUFFER) buf; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferDestroy, 1) (COIBUFFER in_Buffer) { COITRACE ("COIBufferDestroy"); cmd_t cmd = CMD_BUFFER_UNMAP; assert (in_Buffer != NULL); /* Convert input arguments. */ Buffer *buf = (Buffer *) in_Buffer; /* Unmap buffer on host. */ if (buf->data != 0 && buf->type == BUFFER_NORMAL) if (COIBufferUnmap ((COIMAPINSTANCE) in_Buffer, 0, 0, 0) == COI_ERROR) return COI_ERROR; /* Unmap buffer on target. */ if (buf->data_target != 0) { start_critical_section (); /* Send data to target. */ int pipe_host2tgt = buf->process->pipe_host2tgt; WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (pipe_host2tgt, &buf->fd_target, sizeof (int)); WRITE (pipe_host2tgt, &buf->data_target, sizeof (void *)); WRITE (pipe_host2tgt, &buf->size, sizeof (uint64_t)); /* Receive data from target. */ READ (buf->process->pipe_tgt2host, &cmd, sizeof (cmd_t)); finish_critical_section (); } /* 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. */ delete buf; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferGetSinkAddress, 1) (COIBUFFER in_Buffer, uint64_t *out_pAddress) { COITRACE ("COIBufferGetSinkAddress"); assert (in_Buffer != NULL); assert (out_pAddress != NULL); /* Convert input arguments. */ Buffer *buf = (Buffer *) in_Buffer; /* Here should come BUFFER_NORMAL buffer. */ assert (buf->type == BUFFER_NORMAL); /* Prepare output argument. */ *out_pAddress = (uint64_t) buf->data_target; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferMap, 1) (COIBUFFER in_Buffer, uint64_t in_Offset, uint64_t in_Length, // Ignored COI_MAP_TYPE in_Type, // Ignored uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion, COIMAPINSTANCE *out_pMapInstance, void **out_ppData) { COITRACE ("COIBufferMap"); /* Features of liboffloadmic. */ assert (in_Offset == 0); assert (in_NumDependencies == 0); /* Convert input arguments. */ Buffer *buf = (Buffer *) in_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 (out_pMapInstance != 0) *out_pMapInstance = (COIMAPINSTANCE) buf; if (out_ppData != 0) *out_ppData = buf->data; if (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferRead, 1) (COIBUFFER in_SourceBuffer, uint64_t in_Offset, void *in_pDestData, uint64_t in_Length, COI_COPY_TYPE in_Type, uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion) { COITRACE ("COIBufferRead"); /* Features of liboffloadmic. */ assert (in_pDestData != NULL); assert (in_Type == COI_COPY_UNSPECIFIED); assert (in_NumDependencies == 0); /* Convert input arguments. */ Buffer *buf = (Buffer *) in_SourceBuffer; start_critical_section (); /* Map buffers if needed. */ if (buf->data == 0 && buf->type == BUFFER_NORMAL) if (COIBufferMap (in_SourceBuffer, 0, buf->size, (COI_MAP_TYPE) 0, 0, 0, 0, 0, 0) == COI_ERROR) return COI_ERROR; /* Copy data. */ memcpy (in_pDestData, (void *) ((uintptr_t) buf->data + in_Offset), in_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 (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferSetState, 1) (COIBUFFER in_Buffer, // Ignored COIPROCESS in_Process, // Ignored COI_BUFFER_STATE in_State, // Ignored COI_BUFFER_MOVE_FLAG in_DataMove, uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion) { COITRACE ("COIBufferSetState"); /* Features of liboffloadmic. */ assert (in_DataMove == COI_BUFFER_NO_MOVE); assert (in_NumDependencies == 0); /* Looks like we have nothing to do here. */ if (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferUnmap, 1) (COIMAPINSTANCE in_MapInstance, uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion) { COITRACE ("COIBufferUnmap"); /* Features of liboffloadmic. */ assert (in_MapInstance != NULL); assert (in_NumDependencies == 0); /* Convert input arguments. */ Buffer *buffer = (Buffer *) in_MapInstance; /* 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; if (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIBufferWrite, 1) (COIBUFFER in_DestBuffer, uint64_t in_Offset, const void *in_pSourceData, uint64_t in_Length, COI_COPY_TYPE in_Type, uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored COIEVENT *out_pCompletion) { COITRACE ("COIBufferWrite"); /* Features of liboffloadmic. */ assert (in_DestBuffer != NULL); assert (in_pSourceData != NULL); assert (in_Type == COI_COPY_UNSPECIFIED); assert (in_NumDependencies == 0); /* Convert input arguments. */ Buffer *buf = (Buffer *) in_DestBuffer; start_critical_section (); /* Map buffers if needed. */ if (buf->data == 0 && buf->type == BUFFER_NORMAL) if (COIBufferMap (in_DestBuffer, 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 + in_Offset), in_pSourceData, in_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 (out_pCompletion) out_pCompletion->opaque[0] = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIEngineGetCount, 1) (COI_ISA_TYPE isa, uint32_t *count) { COITRACE ("COIEngineGetCount"); /* Features of liboffloadmic. */ assert (isa == COI_ISA_MIC); assert (count != NULL); /* Prepare output arguments. */ *count = num_engines; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIEngineGetHandle, 1) (COI_ISA_TYPE in_ISA, uint32_t in_EngineIndex, COIENGINE *out_pEngineHandle) { COITRACE ("COIEngineGetHandle"); /* Features of liboffloadmic. */ assert (in_ISA == COI_ISA_MIC); assert (out_pEngineHandle != NULL); /* Check engine index. */ if (in_EngineIndex >= num_engines) COIERROR ("Wrong engine index."); /* Create engine handle. */ Engine *engine = new Engine; engine->dir = NULL; engine->index = in_EngineIndex; engine->type = in_ISA; /* Prepare output argument. */ *out_pEngineHandle = (COIENGINE) engine; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIEventWait, 1) (uint16_t in_NumEvents, const COIEVENT *in_pEvents, int32_t in_TimeoutMilliseconds, uint8_t in_WaitForAll, uint32_t *out_pNumSignaled, uint32_t *out_pSignaledIndices) { COITRACE ("COIEventWait"); /* Features of liboffloadmic. */ assert (in_pEvents != NULL); assert (in_TimeoutMilliseconds == 0 || in_TimeoutMilliseconds == -1); assert (in_WaitForAll == 1); assert (out_pNumSignaled == NULL); assert (out_pSignaledIndices == NULL); if (in_TimeoutMilliseconds == 0) { /* If some event is not signalled, return timeout error. */ for (uint16_t i = 0; i < in_NumEvents; i++) if (non_signalled_events.count (in_pEvents[i].opaque[0]) > 0) return COI_TIME_OUT_REACHED; else { /* If the event signalled with an error, return that error. */ start_critical_section (); COIRESULT res = get_event_result (in_pEvents[i]); finish_critical_section (); if (res != COI_SUCCESS) return res; } } else { /* Wait indefinitely for all events. */ for (uint16_t i = 0; i < in_NumEvents; i++) { while (non_signalled_events.count (in_pEvents[i].opaque[0]) > 0) usleep (1000); /* If the event signalled with an error, return that error. */ start_critical_section (); COIRESULT res = get_event_result (in_pEvents[i]); finish_critical_section (); if (res != COI_SUCCESS) return res; } } return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIEventRegisterCallback, 1) (const COIEVENT in_Event, COI_EVENT_CALLBACK in_Callback, const void *in_UserData, const uint64_t in_Flags) { COITRACE ("COIEventRegisterCallback"); /* Features of liboffloadmic. */ assert (in_Callback != NULL); assert (in_UserData != NULL); assert (in_Flags == 0); start_critical_section (); if (non_signalled_events.count (in_Event.opaque[0]) == 0) { /* If the event is already signalled, invoke the callback immediately. */ COIRESULT res = get_event_result (in_Event); in_Callback (in_Event, res, in_UserData); } else { Callback callback; callback.ptr = in_Callback; callback.data = in_UserData; callbacks.insert (std::pair (in_Event.opaque[0], callback)); } finish_critical_section (); return COI_SUCCESS; } /* The start routine for the COI pipeline thread. */ static void * pipeline_thread_routine (void *in_Pipeline) { /* Convert input arguments. */ Pipeline *pipeline = (Pipeline *) in_Pipeline; /* Open pipes. */ pipeline->pipe_host2tgt = open (pipeline->pipe_host2tgt_path, O_CLOEXEC | O_WRONLY); if (pipeline->pipe_host2tgt < 0) COIERRORN ("Cannot open host-to-target pipe."); pipeline->pipe_tgt2host = open (pipeline->pipe_tgt2host_path, O_CLOEXEC | O_RDONLY); if (pipeline->pipe_tgt2host < 0) COIERRORN ("Cannot open target-to-host pipe."); free (pipeline->pipe_host2tgt_path); free (pipeline->pipe_tgt2host_path); pipeline->pipe_host2tgt_path = NULL; pipeline->pipe_tgt2host_path = NULL; while (!pipeline->destroy) if (pipeline->queue.empty ()) usleep (1000); else { Function func = pipeline->queue.front (); start_critical_section (); pipeline->queue.pop (); finish_critical_section (); /* Send data to target. */ cmd_t cmd = CMD_PIPELINE_RUN_FUNCTION; WRITEN (pipeline->pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITEN (pipeline->pipe_host2tgt, &func.ptr, sizeof (void *)); WRITEN (pipeline->pipe_host2tgt, &func.num_buffers, sizeof (uint32_t)); for (uint32_t i = 0; i < func.num_buffers; i++) { WRITEN (pipeline->pipe_host2tgt, &func.bufs_size[i], sizeof (uint64_t)); WRITEN (pipeline->pipe_host2tgt, &func.bufs_data_target[i], sizeof (void *)); } WRITEN (pipeline->pipe_host2tgt, &func.misc_data_len, sizeof (uint16_t)); if (func.misc_data_len > 0) WRITEN (pipeline->pipe_host2tgt, func.misc_data, func.misc_data_len); WRITEN (pipeline->pipe_host2tgt, &func.return_value_len, sizeof (uint16_t)); delete [] func.bufs_size; delete [] func.bufs_data_target; /* Receive data from target. Wait for target function to complete, whether it has any data to return or not. */ bool has_return_value = func.return_value_len > 0; int ret_len = read (pipeline->pipe_tgt2host, has_return_value ? func.return_value : &cmd, has_return_value ? func.return_value_len : sizeof (cmd_t)); if (ret_len == 0) { start_critical_section (); signal_event (func.completion_event, COI_PROCESS_DIED); pipeline->is_destroyed = true; finish_critical_section (); return NULL; } else if (ret_len != (has_return_value ? func.return_value_len : sizeof (cmd_t))) COIERRORN ("Cannot read from pipe."); start_critical_section (); signal_event (func.completion_event, COI_SUCCESS); finish_critical_section (); } /* Send data to target. */ const cmd_t cmd = CMD_PIPELINE_DESTROY; WRITEN (pipeline->pipe_host2tgt, &cmd, sizeof (cmd_t)); /* Close pipes. */ if (close (pipeline->pipe_host2tgt) < 0) COIERRORN ("Cannot close host-to-target pipe."); if (close (pipeline->pipe_tgt2host) < 0) COIERRORN ("Cannot close target-to-host pipe."); start_critical_section (); pipeline->is_destroyed = true; finish_critical_section (); return NULL; } COIRESULT SYMBOL_VERSION (COIPipelineCreate, 1) (COIPROCESS in_Process, COI_CPU_MASK in_Mask, uint32_t in_StackSize, // Ignored COIPIPELINE *out_pPipeline) { COITRACE ("COIPipelineCreate"); /* Features of liboffloadmic. */ assert (in_Process != NULL); assert (in_Mask == 0); assert (out_pPipeline != NULL); /* Convert input arguments. */ Process *proc = (Process *) in_Process; start_critical_section (); /* Create pipeline handle. */ Pipeline *pipeline = new Pipeline; pipeline->destroy = false; pipeline->is_destroyed = false; pipeline->process = proc; pipelines.insert (pipeline); /* Create pipes. */ uint32_t pipeline_num = max_pipeline_num++; char *eng_dir = pipeline->process->engine->dir; MALLOC (char *, pipeline->pipe_host2tgt_path, strlen (eng_dir) + sizeof (PIPE_HOST2TGT_NAME "0000000000")); MALLOC (char *, pipeline->pipe_tgt2host_path, strlen (eng_dir) + sizeof (PIPE_TGT2HOST_NAME "0000000000")); sprintf (pipeline->pipe_host2tgt_path, "%s" PIPE_HOST2TGT_NAME "%010d", eng_dir, pipeline_num); sprintf (pipeline->pipe_tgt2host_path, "%s" PIPE_TGT2HOST_NAME "%010d", eng_dir, pipeline_num); if (mkfifo (pipeline->pipe_host2tgt_path, S_IRUSR | S_IWUSR) < 0) COIERROR ("Cannot create pipe %s.", pipeline->pipe_host2tgt_path); if (mkfifo (pipeline->pipe_tgt2host_path, S_IRUSR | S_IWUSR) < 0) COIERROR ("Cannot create pipe %s.", pipeline->pipe_tgt2host_path); /* Send data to target. */ const cmd_t cmd = CMD_PIPELINE_CREATE; WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (proc->pipe_host2tgt, &pipeline_num, sizeof (pipeline_num)); /* Create a new thread for the pipeline. */ if (pthread_create (&pipeline->thread, NULL, pipeline_thread_routine, pipeline)) COIERROR ("Cannot create new thread."); finish_critical_section (); /* Prepare output arguments. */ *out_pPipeline = (COIPIPELINE) pipeline; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIPipelineDestroy, 1) (COIPIPELINE in_Pipeline) { COITRACE ("COIPipelineDestroy"); assert (in_Pipeline != NULL); /* Convert input arguments. */ Pipeline *pipeline = (Pipeline *) in_Pipeline; start_critical_section (); /* Remove pipeline from the set of undestroyed pipelines. */ pipelines.erase (pipeline); /* Exit pipeline thread. */ pipeline->destroy = true; finish_critical_section (); while (!pipeline_is_destroyed (pipeline)) usleep (1000); /* Join with a destroyed thread. */ if (pthread_join (pipeline->thread, NULL)) COIERROR ("Cannot join with a thread."); delete pipeline; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIPipelineRunFunction, 1) (COIPIPELINE in_Pipeline, COIFUNCTION in_Function, uint32_t in_NumBuffers, const COIBUFFER *in_Buffers, const COI_ACCESS_FLAGS *in_pBufferAccessFlags, // Ignored uint32_t in_NumDependencies, const COIEVENT *in_pDependencies, // Ignored const void *in_pMiscData, uint16_t in_MiscDataLen, void *out_pAsyncReturnValue, uint16_t in_AsyncReturnValueLen, COIEVENT *out_pCompletion) { COITRACE ("COIPipelineRunFunction"); /* Features of liboffloadmic. */ assert (in_Pipeline != NULL); assert (in_Function != NULL); assert (in_NumDependencies == 0); Function func; func.ptr = (void *) in_Function; func.num_buffers = in_NumBuffers; func.bufs_size = new uint64_t [in_NumBuffers]; func.bufs_data_target = new void * [in_NumBuffers]; for (uint32_t i = 0; i < in_NumBuffers; i++) { Buffer **bufs = (Buffer **) in_Buffers; func.bufs_size[i] = bufs[i]->size; func.bufs_data_target[i] = bufs[i]->data_target; } func.misc_data = (void *) in_pMiscData; func.misc_data_len = in_MiscDataLen; func.return_value = out_pAsyncReturnValue; func.return_value_len = in_AsyncReturnValueLen; start_critical_section (); func.completion_event.opaque[0] = max_event_num++; non_signalled_events.insert (func.completion_event.opaque[0]); ((Pipeline *) in_Pipeline)->queue.push (func); finish_critical_section (); /* In case of synchronous execution we have to wait for target. */ if (out_pCompletion == NULL) COIEventWait (1, &func.completion_event, -1, 1, NULL, NULL); else *out_pCompletion = func.completion_event; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIProcessCreateFromMemory, 1) (COIENGINE in_Engine, const char *in_pBinaryName, const void *in_pBinaryBuffer, uint64_t in_BinaryBufferLength, int in_Argc, const char **in_ppArgv, uint8_t in_DupEnv, const char **in_ppAdditionalEnv, uint8_t in_ProxyActive, // Ignored const char *in_Reserved, // Ignored uint64_t in_InitialBufferSpace, // Ignored const char *in_LibrarySearchPath, const char *in_FileOfOrigin, // Ignored uint64_t in_FileOfOriginOffset, // Ignored COIPROCESS *out_pProcess) { COITRACE ("COIProcessCreateFromMemory"); const int run_max_args_num = 128; char *run_argv[run_max_args_num]; char *emul_run = getenv (OFFLOAD_EMUL_RUN_ENV); const int uint_max_len = 11; /* Features of liboffloadmic. */ assert (in_Engine != NULL); assert (in_pBinaryName != NULL); assert (in_pBinaryBuffer != NULL); assert (in_Argc == 0); assert (in_ppArgv == NULL); assert (in_ppAdditionalEnv == NULL); assert (in_LibrarySearchPath != NULL); assert (out_pProcess != NULL); /* Convert input arguments. */ Engine *eng = (Engine *) in_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. */ char *target_exe; MALLOC (char *, target_exe, strlen (eng->dir) + strlen (in_pBinaryName) + 2); sprintf (target_exe, "%s/%s", eng->dir, in_pBinaryName); int 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 *file = fdopen (fd, "wb"); if (file == NULL) COIERROR ("Cannot associate stream with file descriptor."); if (fwrite (in_pBinaryBuffer, 1, in_BinaryBufferLength, file) != in_BinaryBufferLength) 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. */ char *pipes_path; MALLOC (char *, pipes_path, strlen (eng->dir) + sizeof (PIPES_PATH)); sprintf (pipes_path, "%s" PIPES_PATH, eng->dir); if (mkdir (pipes_path, S_IRWXU) < 0) COIERROR ("Cannot create folder %s.", pipes_path); /* Create 2 main pipes for inter-process communication. */ char *pipe_host2tgt_path, *pipe_tgt2host_path; MALLOC (char *, pipe_host2tgt_path, strlen (eng->dir) + sizeof (PIPE_HOST2TGT_NAME "mainpipe")); MALLOC (char *, pipe_tgt2host_path, strlen (eng->dir) + sizeof (PIPE_TGT2HOST_NAME "mainpipe")); sprintf (pipe_host2tgt_path, "%s" PIPE_HOST2TGT_NAME "mainpipe", eng->dir); sprintf (pipe_tgt2host_path, "%s" PIPE_TGT2HOST_NAME "mainpipe", eng->dir); if (mkfifo (pipe_host2tgt_path, S_IRUSR | S_IWUSR) < 0) COIERROR ("Cannot create main pipe %s.", pipe_host2tgt_path); if (mkfifo (pipe_tgt2host_path, S_IRUSR | S_IWUSR) < 0) COIERROR ("Cannot create main pipe %s.", pipe_tgt2host_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; int i = 0; STRDUP (tmp, emul_run); char *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. */ int env_num = 0; if (in_DupEnv == true) while (environ[env_num++]); env_num += 4; // LD_LIBRARY_PATH, MIC_DIR, MIC_INDEX, NULL char **envp; MALLOC (char **, envp, env_num * sizeof (char *)); int env_i = 0; if (in_DupEnv == true) for (unsigned i = 0; environ[i] != NULL; i++) { unsigned j; char *env_name; 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 (in_LibrarySearchPath) + 1); sprintf (envp[env_i + 2], "LD_LIBRARY_PATH=%s", in_LibrarySearchPath); envp[env_i + 3] = (char *) NULL; /* Create target process. */ pid_t 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 main pipes. */ int pipe_host2tgt = open (pipe_host2tgt_path, O_CLOEXEC | O_WRONLY); if (pipe_host2tgt < 0) COIERROR ("Cannot open host-to-target main pipe."); int pipe_tgt2host = open (pipe_tgt2host_path, O_CLOEXEC | O_RDONLY); if (pipe_tgt2host < 0) COIERROR ("Cannot open target-to-host main pipe."); /* Create process handle. */ Process *proc = new Process; proc->pid = pid; proc->pipe_host2tgt = pipe_host2tgt; proc->pipe_tgt2host = pipe_tgt2host; proc->engine = eng; proc->functions = NULL; /* Prepare output arguments. */ *out_pProcess = (COIPROCESS) proc; /* Clean up. */ for (unsigned i = 0; run_argv[i] != NULL; i++) free (run_argv[i]); for (unsigned i = 0; envp[i] != NULL; i++) free (envp[i]); free (envp); free (pipe_host2tgt_path); free (pipe_tgt2host_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 in_Process, int32_t in_WaitForMainTimeout, // Ignored uint8_t in_ForceDestroy, int8_t *out_pProcessReturn, uint32_t *out_pTerminationCode) { COITRACE ("COIProcessDestroy"); assert (in_Process != NULL); assert (out_pProcessReturn != NULL); assert (out_pTerminationCode != NULL); /* Convert input arguments. */ Process *proc = (Process *) in_Process; /* Destroy all undestroyed pipelines. */ while (!pipelines.empty ()) { std::set::iterator p = pipelines.begin (); COIPipelineDestroy ((COIPIPELINE) *p); } /* Close main pipes. */ if (close (proc->pipe_host2tgt) < 0) COIERROR ("Cannot close host-to-target main pipe."); if (close (proc->pipe_tgt2host) < 0) COIERROR ("Cannot close target-to-host main pipe."); /* Shutdown target process by force. */ if (in_ForceDestroy) kill (proc->pid, SIGTERM); /* Clean up. */ free (proc->engine->dir); free (proc->functions); delete proc->engine; delete proc; /* Prepare output arguments. */ *out_pProcessReturn = 0; *out_pTerminationCode = 0; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIProcessGetFunctionHandles, 1) (COIPROCESS in_Process, uint32_t in_NumFunctions, const char **in_ppFunctionNameArray, COIFUNCTION *out_pFunctionHandleArray) { COITRACE ("COIProcessGetFunctionHandles"); assert (in_Process != NULL); assert (in_ppFunctionNameArray != NULL); assert (out_pFunctionHandleArray != NULL); /* Convert input arguments. */ Process *proc = (Process *) in_Process; /* This function should be called once for the process. */ assert (proc->functions == NULL); /* 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 = (void **) calloc (in_NumFunctions + 1, sizeof (void *)); if (proc->functions == NULL) COIERROR ("Cannot allocate memory."); /* Get handles for functions. */ for (uint32_t i = 0; i < in_NumFunctions; i++) { size_t len = strlen (in_ppFunctionNameArray[i]) + 1; start_critical_section (); /* Send data to target. */ const cmd_t cmd = CMD_GET_FUNCTION_HANDLE; WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (proc->pipe_host2tgt, &len, sizeof (size_t)); WRITE (proc->pipe_host2tgt, in_ppFunctionNameArray[i], len); /* Receive data from target. */ void *fn_ptr; READ (proc->pipe_tgt2host, &fn_ptr, sizeof (void *)); finish_critical_section (); /* Save function pointer. */ proc->functions[i] = fn_ptr; /* Prepare output arguments. */ out_pFunctionHandleArray[i] = (COIFUNCTION) fn_ptr; } 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"); assert (in_Process != NULL); assert (in_pLibraryBuffer != NULL); assert (out_pLibrary != NULL); /* Convert input arguments. */ Process *proc = (Process *) in_Process; /* Create target library file. */ char *lib_path; size_t len = strlen (proc->engine->dir) + strlen (in_pLibraryName) + 2; MALLOC (char *, lib_path, len); sprintf (lib_path, "%s/%s", proc->engine->dir, in_pLibraryName); int 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 *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); start_critical_section (); /* Make target open library. */ const cmd_t cmd = CMD_OPEN_LIBRARY; WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (proc->pipe_host2tgt, &len, sizeof (size_t)); WRITE (proc->pipe_host2tgt, lib_path, len); /* Receive data from target. */ void *handle; READ (proc->pipe_tgt2host, &handle, sizeof (void *)); finish_critical_section (); /* Clean up. */ free (lib_path); *out_pLibrary = (COILIBRARY) handle; return COI_SUCCESS; } COIRESULT SYMBOL_VERSION (COIProcessRegisterLibraries, 1) (uint32_t in_NumLibraries, // Ignored const void **in_ppLibraryArray, // Ignored const uint64_t *in_pLibrarySizeArray, // Ignored const char **in_ppFileOfOriginArray, // Ignored const uint64_t *in_pFileOfOriginOffSetArray) // Ignored { 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"); assert (in_Process != NULL); assert (in_Library != NULL); const cmd_t cmd = CMD_CLOSE_LIBRARY; /* Convert input arguments. */ Process *proc = (Process *) in_Process; start_critical_section (); /* Make target close library. */ WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); WRITE (proc->pipe_host2tgt, &in_Library, sizeof (void *)); finish_critical_section (); 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, // Ignored uint32_t in_EngineInfoSize, // Ignored COI_ENGINE_INFO *out_pEngineInfo) { COITRACE ("COIEngineGetInfo"); assert (out_pEngineInfo != NULL); 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"