This patch revises utility/conf features in npu-engine.
1) Move the related sources to ./utils folder
- To remove the Module Cycle Dependency (MCD) in SAM analysis.
2) Replace C-based library implementation to standard library
3) Remove 'working_dir' and add 'num_threads' in configuration.
Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
-# Do NOT enable C in core
-# C++ is only allowed inside test directory for gtest.
-
project('npu-engine', ['c', 'cpp'],
version: '1.2.0',
license: ['Proprietary'],
ne_install_conf.set('INCLUDE_INSTALL_DIR', ne_includedir)
ne_install_conf.set('RESV_MEM_SIZE', get_option('resv_mem_size'))
-ne_install_conf.set('WORKING_DIR', get_option('working_dir'))
+ne_install_conf.set('NUM_THREADS', get_option('num_threads'))
ne_install_conf.set('LOG_DIR', get_option('log_dir'))
# Install .ini
option('comm_opt', type : 'string', value : 'ip')
option('resv_mem_size', type : 'string', value : '0')
-option('working_dir', type : 'string', value : '/tmp/')
+option('num_threads', type : 'string', value : '8')
option('log_dir', type : 'string', value : '/tmp/')
option('enable_npu_emul', type : 'boolean', value : false)
option('enable_data_manip', type : 'boolean', value : false)
[main]
resv_mem_size=@RESV_MEM_SIZE@
-working_dir=@WORKING_DIR@
+num_threads=@NUM_THREADS@
log_dir=@LOG_DIR@
+++ /dev/null
-/**
- * Proprietary
- * Copyright (C) 2019 Samsung Electronics
- * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
- */
-/**
- * @file ne-conf.c
- * @date 26 Jun 2019
- * @brief Allow to access global configuration of NPU Engine.
- * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
- * @author MyungJoo Ham <myungjoo.ham@samsung.com>
- * @bug No known bugs except for NYI items
- */
-
-#include <linux/limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-#include <errno.h>
-
-/** @todo add BuildRequires: pkgconfig(iniparser) */
-#include <iniparser.h>
-
-#include "ne-conf.h"
-
-#define DEFAULT_RESV_MEM_SIZE 0 /* default mode is the CMA allocation */
-#define DEFAULT_WORKING_DIR "/tmp/"
-#define DEFAULT_LOG_DIR "/tmp/"
-
-#define ENV_RESV_MEM_SIZE "NE_RESV_MEM_SIZE"
-#define ENV_WORKING_DIR "NE_WORKING_DIR"
-#define ENV_LOG_DIR "NE_LOG_DIR"
-
-static conf_t config;
-const conf_t *conf = &config;
-
-/**
- * @brief Load configuration for each config parameter
- * @param[in] resv_mem_size config string for reserved mem size
- * @param[in] working_dir config string for working dir
- * @param[in] log_dir config string for log dir
- * @param[out] c The configuration object to be updated.
- * @note if a config param is NULL, it is not set. so use default one.
- */
-static void
-load_conf_each (const char *resv_mem_size, const char *working_dir,
- const char *log_dir, conf_t *c)
-{
- if (resv_mem_size) {
- uint64_t val;
- char *unit = NULL;
-
- /** the calling program should set errno to 0 before the call */
- errno = 0;
- /** strtoull() convert the initial part of the string to uint64_t */
- val = strtoull (resv_mem_size, &unit, 10);
- if (errno == 0) {
- if (unit) {
- /** the address of the first invalid character was stored, which might be a unit */
- if (*unit == 'K' || *unit == 'k') {
- val *= 1024;
- } else if (*unit == 'M' || *unit == 'm') {
- val *= 1024 * 1024;
- } else if (*unit == 'G' || *unit == 'g') {
- val *= 1024 * 1024 * 1024;
- }
- }
- /** zero size is now allowed to indicate the KERNEL_CMA mode */
- c->reserved_mem_size = val;
- }
- }
-
- if (working_dir && strlen(working_dir) < MAX_DIR_LEN) {
- memcpy (c->working_dir, working_dir, strlen(working_dir));
- c->working_dir[strlen(working_dir)] = '\x00';
- }
-
- if (log_dir && strlen(log_dir) < MAX_DIR_LEN) {
- memcpy (c->log_dir, log_dir, strlen(log_dir));
- c->log_dir[strlen(log_dir)] = '\x00';
- }
-}
-
-/**
- * @brief Load configuration with .ini file.
- * @param[in] ini The parsed .ini file.
- * @param[out] c The configuration object to be updated.
- */
-static void load_conf_ini(dictionary *ini, conf_t *c)
-{
- const char *resv_mem_size = iniparser_getstring(ini, "main:resv_mem_size", NULL);
- const char *working_dir = iniparser_getstring(ini, "main:working_dir", NULL);
- const char *log_dir = iniparser_getstring(ini, "main:log_dir", NULL);
-
- load_conf_each (resv_mem_size, working_dir, log_dir, c);
-}
-
-/**
- * @brief Load configuration with env-vars
- * @param[out] c The configuration object to be updated.
- */
-static void load_conf_envvar(conf_t *c)
-{
- const char *resv_mem_size = getenv (ENV_RESV_MEM_SIZE);
- const char *working_dir = getenv (ENV_WORKING_DIR);
- const char *log_dir = getenv (ENV_LOG_DIR);
-
- load_conf_each (resv_mem_size, working_dir, log_dir, c);
-}
-
-/**
- * @brief Load configuration with default values
- */
-static void load_conf_default(void)
-{
- static int default_loaded = 0;
-
- if (default_loaded) return;
-
- default_loaded = 1;
-
- config.reserved_mem_size = DEFAULT_RESV_MEM_SIZE;
-
- memcpy (config.working_dir, DEFAULT_WORKING_DIR, strlen(DEFAULT_WORKING_DIR));
- config.working_dir[strlen(DEFAULT_WORKING_DIR)] = '\x00';
-
- memcpy (config.log_dir, DEFAULT_LOG_DIR, strlen(DEFAULT_LOG_DIR));
- config.log_dir[strlen(DEFAULT_LOG_DIR)] = '\x00';
-}
-
-/**
- * @brief Load the configuration from /etc/npu-engine.ini and env-vars.
- * @detail Refer to the header file for more detail.
- */
-int load_conf(const char *inipath)
-{
- dictionary *ini;
- static const char INI_FILE_NAME[] = "npu-engine.ini";
- char default_ini_path[PATH_MAX];
-
- snprintf(default_ini_path, PATH_MAX, "%s/%s", NE_INIDIR, INI_FILE_NAME);
-
- /* Load up config from default */
- load_conf_default();
-
- /* Load up config from ini if available */
- ini = iniparser_load(inipath != NULL ? inipath : default_ini_path);
- if (ini) {
- load_conf_ini(ini, &config);
- iniparser_freedict(ini);
- }
-
- /* Load up config from envvar if available */
- load_conf_envvar(&config);
-
- return 0;
-}
+++ /dev/null
-/**
- * Proprietary
- * Copyright (C) 2019 Samsung Electronics
- * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
- */
-/**
- * @file ne-conf.h
- * @date 15 Apr 2019
- * @brief Internal API to access global configuration of NPU Engine.
- * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
- * @author MyungJoo Ham <myungjoo.ham@samsung.com>
- * @bug No known bugs except for NYI items
- *
- * To packagers: this is internal header for NE. Don't export this.
- */
-
-#ifndef NE_CONF_H__
-#define NE_CONF_H__
-
-#include <stdint.h>
-
-#define MAX_DIR_LEN 256
-
-/**
- * @brief The global environmental and configuration values
- * @detail The list may keep grow.
- * Users (except for ne-conf.c) must NEVER write values.
- */
-typedef struct {
- uint64_t reserved_mem_size; /**< NE_RESV_MEM_SIZE, [main] resv_mem_size, size of memory reserved for NPU cores */
- enum {
- COMM_USB,
- COMM_FILESYS,
- COMM_SOCIP,
- COMM_PCIE,
- } communication_method; /**< Determined at build-time. Not configurable at runtime. The activated N1x plugin. */
- char working_dir[MAX_DIR_LEN]; /**< NE_WORKING_DIR, [main] working_dir, the path where the daemon is executed */
- char log_dir[MAX_DIR_LEN]; /**< NE_LOG_DIR, [main] log_dir, the path where log files are created */
-} conf_t;
-
-extern const conf_t *conf; /** Users cannot modify this. */
-
-/**
- * @brief Load the configuration from /etc/npu-engine.ini and env-vars>
- * @param[in] inipath If given (not null), default .ini path is overriden.
- * @return 0 if success, otherwise negative error number.
- *
- * @detail Only main.c is supposed to call this.
- */
-int load_conf(const char *inipath);
-
-#endif /* NE_CONF_H__ */
+++ /dev/null
-/**
- * Proprietary
- * Copyright (C) 2019 Samsung Electronics
- * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
- */
-/**
- * @file NE-utils.c
- * @date 13 June 2019
- * @brief Utility libraries for NPU Engine and other related components.
- * @author Dongju Chae <dongju.chae@samsung.com>
- * @bug No known bugs except for NYI items
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-#include <linux/dma-buf.h>
-#include <sys/ioctl.h>
-#include <sys/timeb.h>
-
-#include "ne-utils.h"
-#include "ne-conf.h"
-
-void fini_logwrite (void) __attribute__ ((destructor));
-
-/* double-linked list */
-
-/**
- * @brief internal function to initialize a list head
- * @param[in] head the list head
- */
-static void
-list_head_init (list_node *head)
-{
- head->prev = head->next = head;
-}
-
-/**
- * @brief initialize list
- * @param[in] list the list instance
- */
-void
-list_init (list *list)
-{
- list_head_init (&list->head);
- list->num = 0;
-}
-
-/**
- * @brief check whether the node is the first element or not
- * @param[in] list the list instance
- * @param[in] node the node to be checked
- * @return true if the first node
- */
-bool
-list_is_first (list *list, list_node *node)
-{
- return list->head.next == node;
-}
-
-/**
- * @brief check whether the node is the last element or not
- * @param[in] list the list instance
- * @param[in] node the node to be checked
- * @return true if the last node
- */
-bool
-list_is_last (list *list, list_node *node)
-{
- return list->head.prev == node;
-}
-
-/**
- * @brief internal function to connect list node between adjacent nodes
- * @param[in] prev previous list node
- * @param[in] next next list node
- */
-static void
-_list_add (list_node *node, list_node *prev, list_node *next)
-{
- next->prev = node;
- node->next = next;
- node->prev = prev;
- prev->next = node;
-}
-
-/**
- * @brief add the node into the first position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- */
-void
-list_add (list *list, list_node *node)
-{
- list_node *head = &list->head;
-
- _list_add (node, head, head->next);
-
- list->num++;
-}
-
-/**
- * @brief add the node into the last position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- */
-void
-list_add_tail (list *list, list_node *node)
-{
- list_node *head = &list->head;
-
- _list_add (node, head->prev, head);
-
- list->num++;
-}
-
-/**
- * @brief add the node into the specific position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- * @param[in] next the node indicating a position.
- */
-void
-list_add_next (list *list, list_node *node, list_node *next)
-{
- _list_add (node, next->prev, next);
-
- list->num++;
-}
-
-/**
- * @brief internal function to delete list node (connect adjacent nodes)
- * @param[in] prev previous list node
- * @param[in] next next list node
- */
-static void
-_list_del (list_node *prev, list_node *next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-/**
- * @brief delete the node from the list
- * @param[in] list the list instance
- * @param[in] node the node to be deleted
- */
-void
-list_del (list *list, list_node *node)
-{
- assert (list->num > 0);
-
- _list_del (node->prev, node->next);
-
- list->num--;
-}
-
-/* hash table */
-
-/**
- * @brief initialize hash table
- * @param[in] ht the instance of hash table
- * @param[in] size the size of bucket (should be a power of 2)
- * @return 0 if ok. otherwise a negative error value
- */
-int
-hash_table_init (hash_table *ht, uint32_t size)
-{
- int i;
-
- assert (size % 2 == 0);
-
- ht->num = 0;
- ht->size = size;
- ht->list = (list *) malloc (sizeof (list) * size);
-
- if (ht->list == NULL)
- return -ENOMEM;
-
- for (i = 0; i < size; i++)
- list_init (&ht->list[i]);
-
- return 0;
-}
-
-/**
- * @brief destroy hash table
- * @param[in] ht the instance of hash table
- * @return 0 if ok. otherwise a negative error value
- */
-int
-hash_table_destroy (hash_table *ht)
-{
- if (!(ht && ht->list))
- return -EINVAL;
-
- /**
- * there're hash table nodes which was not allocated yet
- * the caller should deallocate them before the table destruction
- */
- if (ht->num > 0)
- return -EBUSY;
-
- free (ht->list);
-
- return 0;
-}
-
-/**
- * @brief add the node with key into the hash table
- * @param[in] ht the instance of hash table
- * @param[in] node the node to be added
- * @param[in] key key
- */
-void
-hash_table_add (hash_table *ht, hash_node *node, uint32_t key)
-{
- uint32_t index = HASH_INDEX (ht, key);
-
- list_add (&ht->list[index], &node->list);
-
- node->key = key;
-
- ht->num++;
-}
-
-/**
- * @brief delete the node from the hash table
- * @param[in] ht the instance of hash table
- * @param[in] node the node to be deleted
- */
-void
-hash_table_del (hash_table *ht, hash_node *node)
-{
- uint32_t index = HASH_INDEX (ht, node->key);
-
- list_del (&ht->list[index], &node->list);
-
- ht->num--;
-}
-
-/********************************************************************
- * Logging utilities *
- ********************************************************************/
-#define logfilename "npu-engine.log"
-
-static pthread_mutex_t priv_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/**
- * @brief Log Level String in 5 characters
- */
-static const char *loglevelstr[] = {
- [LOG_INFO] = "INFO ",
- [LOG_WARN] = "WARN ",
- [LOG_ERROR] = "ERROR",
-};
-
-/**
- * @brief Module Name String in 8 characters
- */
-static const char *modulestr[] = {
- [_N1] = "N1/Comm ",
- [_N11] = "N11/IP ",
- [_N12] = "N12/FS ",
- [_N13] = "N13/USB ",
- [_N2] = "N2/HostH",
- [_N3] = "N3/Sched",
- [_N4] = "N4/InfCt",
- [_N41] = "N41/iCAM",
- [_N42] = "N42/Host",
- [_N43] = "N43/iMIC",
- [_N44] = "N44/ARMp",
- [_N45] = "N45/iHW ",
- [_N50] = "N50/Pool",
- [_N7] = "N7/Mem ",
- [_N71] = "N71/CMA ",
- [_N72] = "N72/IOMM",
- [_N9] = "N9/GEM ",
- [_N95] = "N95/TLB ",
-};
-
-static FILE *fp = NULL;
-
-/**
- * @brief Write a log, to a logfile designated by conf
- * @param[in] l Loglevel
- * @param[in] m Module designation
- */
-void logwrite(loglevel l, module m, const char *format, ...) {
- time_t ltime = time (NULL);
- char* time_str;
- va_list args;
-
- pthread_mutex_lock (&priv_mutex);
- if (fp == NULL) {
- char filename[FILENAME_MAX];
-
- snprintf(filename, FILENAME_MAX, "%s%s", conf->log_dir, logfilename);
- fp = fopen(filename, "a");
- }
- pthread_mutex_unlock (&priv_mutex);
-
- assert(fp != NULL);
-
- /**
- * localtime() and asctime() are not MT-safe. There are alternatives,
- * localtime_r() and asctime_r(), when __USE_POSIX is set. Without them, we
- * need critical section here.
- */
- pthread_mutex_lock (&priv_mutex);
- time_str = asctime(localtime(<ime));
- pthread_mutex_unlock (&priv_mutex);
-
- time_str[strcspn(time_str, "\n")] = '\x00';
- fprintf(fp, "[%s][%s][%s] ", loglevelstr[l], modulestr[m], time_str);
- va_start (args, format);
- vfprintf(fp, format, args);
- va_end (args);
-
- fflush(fp);
-}
-
-void fini_logwrite (void) {
- pthread_mutex_lock (&priv_mutex);
- if (fp != NULL) {
- fclose(fp);
- fp = NULL;
- }
- pthread_mutex_unlock (&priv_mutex);
-
- /* Harmless statement */
- pthread_mutex_init (&priv_mutex, NULL);
-}
-
-/**
- * @brief The start of dmabuf sync
- * @param[in] dmabuf_fd dma-buf fd handle
- * @param[in] write read/write or read-only access
- * @return 0 if no error, otherwise a negative errno
- * @note From this call, all updates on dmabuf will be flushed with sync_end
- */
-int
-sync_start (int dmabuf_fd, bool write)
-{
- struct dma_buf_sync sync = {
- .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ
- };
-
- if (write)
- sync.flags |= DMA_BUF_SYNC_WRITE;
-
- return ioctl (dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync);
-}
-
-/**
- * @brief The end of dmabuf sync.
- * @param[in] dmabuf_fd dma-buf fd handle
- * @param[in] write read/write or read-only access
- * @return 0 if no error, otherwise a negative errno
- * @note It flushs the all updates from caches
- */
-int
-sync_end (int dmabuf_fd, bool write)
-{
- struct dma_buf_sync sync = {
- .flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ
- };
-
- if (write)
- sync.flags |= DMA_BUF_SYNC_WRITE;
-
- return ioctl (dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync);
-}
-
-/**
- * @brief Get the timestamp of the current time
- * @param[in] extra [OPTIONAL] extra milliseconds to be appended
- * @return the current timestamp (in ms)
- */
-uint64_t
-get_timestamp (uint32_t extra)
-{
- uint64_t timestamp;
- struct timeb timer;
-
- ftime (&timer);
-
- timestamp = ((uint64_t) timer.time) * 1000LL + (uint64_t) timer.millitm;
-
- return timestamp + (uint64_t) extra;
-}
+++ /dev/null
-/**
- * Proprietary
- * Copyright (C) 2019 Samsung Electronics
- * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
- */
-/**
- * @file NE-utils.h
- * @date 13 June 2019
- * @brief Utility libraries for NPU Engine and other related components.
- * @author Dongju Chae <dongju.chae@samsung.com>
- * @bug No known bugs except for NYI items
- */
-
-#ifndef __NPU_ENGINE_UTILS_H__
-#define __NPU_ENGINE_UTILS_H__
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <math.h>
-
-/* common */
-#define LOG2(x) ((uint32_t) log2(x))
-#define CONTAINER_OF(ptr, type, member) \
- (type *)((char *)(ptr) - (char *) &((type *)0)->member)
-
-/* double-linked list */
-
-/**
- * @brief macros for list iteration
- */
-#define list_first_entry(ptr, type, member) \
- CONTAINER_OF((ptr)->next, type, member)
-
-#define list_last_entry(ptr, type, member) \
- CONTAINER_OF((ptr)->prev, type, member)
-
-#define list_prev_entry(pos, member) \
- CONTAINER_OF((pos)->member.prev, __typeof__(*(pos)), member)
-
-#define list_next_entry(pos, member) \
- CONTAINER_OF((pos)->member.next, __typeof__(*(pos)), member)
-
-#define list_for_each_entry(pos, list, member) \
- for (pos = list_first_entry (&(list).head, __typeof__(*pos), member); \
- &pos->member != &(list).head || (pos = NULL) != NULL; \
- pos = list_next_entry (pos, member))
-
-#define list_for_each_entry_reverse(pos, list, member) \
- for (pos = list_last_entry (&(list).head, __typeof__(*pos), member); \
- &pos->member != &(list).head || (pos = NULL) != NULL; \
- pos = list_prev_entry (pos, member))
-
-#define list_for_each_entry_safe(pos, tmp, list, member) \
- for (pos = list_first_entry (&(list).head, __typeof__(*pos), member), \
- tmp = list_next_entry (pos, member); \
- &pos->member != &(list).head || (pos = NULL) != NULL; \
- pos = tmp, tmp = list_next_entry (pos, member))
-
-#define list_for_each_entry_reverse_safe(pos, tmp, list, member) \
- for (pos = list_last_entry (&(list).head, __typeof__(*pos), member), \
- tmp = list_prev_entry (pos, member); \
- &pos->member != &(list).head || (pos = NULL) != NULL; \
- pos = tmp, tmp = list_prev_entry (pos, member))
-
-/**
- * @brief structure for list node
- */
-typedef struct _list_node {
- struct _list_node *next, *prev;
-} list_node;
-
-/**
- * @brief structure for list
- */
-typedef struct _list {
- uint32_t num;
- list_node head;
-} list;
-
-/**
- * @brief initialize list
- * @param[in] list the list instance
- */
-void list_init (list *list);
-
-/**
- * @brief check whether the node is the first element or not
- * @param[in] list the list instance
- * @param[in] node the node to be checked
- * @return true if the first node
- */
-bool list_is_first (list *list, list_node *node);
-
-/**
- * @brief check whether the node is the last element or not
- * @param[in] list the list instance
- * @param[in] node the node to be checked
- * @return true if the last node
- */
-bool list_is_last (list *list, list_node *node);
-
-/**
- * @brief add the node into the first position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- */
-void list_add (list *list, list_node *node);
-
-/**
- * @brief add the node into the last position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- */
-void list_add_tail (list *list, list_node *node);
-
-/**
- * @brief add the node into the specific position in the list
- * @param[in] list the list instance
- * @param[in] node the node to be added
- * @param[in] next the node indicating a position.
- */
-void list_add_next (list *list, list_node *node, list_node *next);
-
-/**
- * @brief delete the node from the list
- * @param[in] list the list instance
- * @param[in] node the node to be deleted
- */
-void list_del (list *list, list_node *node);
-
-/* hash table */
-#define GOLDEN_RATIO_32 0x61C88647u /* 32-bits hash */
-#define HASH_BITS(size) LOG2(size)
-#define HASH_INDEX(ht, key) ((key * GOLDEN_RATIO_32) >> (32 - HASH_BITS ((ht)->size)))
-
-/**
- * @brief structure for hash node
- */
-typedef struct _hash_node {
- uint32_t key;
- list_node list; /* use a double-linked list */
-} hash_node;
-
-/**
- * @brief structure for hash table
- */
-typedef struct _hash_table {
- uint32_t num;
- uint32_t size; /* should be a power of 2 */
- list *list; /* in each bucket, there is a list */
-} hash_table;
-
-/**
- * @brief initialize hash table
- * @param[in] ht the instance of hash table
- * @param[in] size the size of bucket (should be a power of 2)
- * @return 0 if ok. otherwise a negative error value
- */
-int hash_table_init (hash_table *ht, uint32_t size);
-
-/**
- * @brief destroy hash table
- * @param[in] ht the instance of hash table
- * @return 0 if ok. otherwise a negative error value
- */
-int hash_table_destroy (hash_table *ht);
-
-/**
- * @brief add the node with key into the hash table
- * @param[in] ht the instance of hash table
- * @param[in] node the node to be added
- * @param[in] key key
- */
-void hash_table_add (hash_table *ht, hash_node *node, uint32_t key);
-
-/**
- * @brief delete the node from the hash table
- * @param[in] ht the instance of hash table
- * @param[in] node the node to be deleted
- */
-void hash_table_del (hash_table *ht, hash_node *node);
-
-/**
- * @brief macros for hash table iteration
- */
-#define hash_for_each(obj, ht, bkt, member) \
- for ((bkt) = 0; (bkt) < ht.size; (bkt)++) \
- list_for_each_entry(obj, ht.list[bkt], member.list)
-
-#define hash_for_each_safe(obj, tmp, ht, bkt, member) \
- for ((bkt) = 0; (bkt) < ht.size; (bkt)++) \
- list_for_each_entry_safe(obj, tmp, ht.list[bkt], member.list)
-
-#define hash_for_each_possible(obj, ht, member, key) \
- list_for_each_entry(obj, ht.list[HASH_INDEX (&ht, key)], member.list)
-
-#define hash_for_each_possible_safe(obj, tmp, ht, member, key) \
- list_for_each_entry_safe(obj, tmp, ht.list[HASH_INDEX (&ht, key)], member.list)
-
-/********************************************************************
- * Logging utilities *
- ********************************************************************/
-
-typedef enum {
- LOG_INFO = 0,
- LOG_WARN = 4,
- LOG_ERROR = 8,
-} loglevel;
-
-/**
- * @brief Module definitions
- * @detail
- * Refer to http://suprem.sec.samsung.net/confluence/pages/viewpage.action?pageId=126187176
- * Modules out of ne-engine are not described here.
- */
-typedef enum {
- _N1 = 0,
- _N11,
- _N12,
- _N13,
- _N2,
- _N3,
- _N4,
- _N41,
- _N42,
- _N43,
- _N44,
- _N45,
- _N50,
- _N7,
- _N71,
- _N72,
- _N9,
- _N95,
-} module;
-
-/**
- * @brief Write a log, to a logfile designated by conf
- * @param[in] l Loglevel
- * @param[in] m Module designation
- */
-void logwrite(loglevel l, module m, const char *format, ...);
-#define logerr(m, fmt, ...) logwrite(LOG_ERROR, m, fmt, ##__VA_ARGS__)
-#define logwarn(m, fmt, ...) logwrite(LOG_WARN, m, fmt, ##__VA_ARGS__)
-#define loginfo(m, fmt, ...) logwrite(LOG_INFO, m, fmt, ##__VA_ARGS__)
-
-/**
- * @brief The start of dmabuf sync
- * @param[in] dmabuf_fd dma-buf fd handle
- * @param[in] write read/write or read-only access
- * @return 0 if no error, otherwise a negative errno
- * @note From this call, all updates on dmabuf will be flushed with sync_end
- */
-int
-sync_start (int dmabuf_fd, bool write);
-
-/**
- * @brief The end of dmabuf sync.
- * @param[in] dmabuf_fd dma-buf fd handle
- * @param[in] write read/write or read-only access
- * @return 0 if no error, otherwise a negative errno
- * @note It flushs the all updates from caches
- */
-int
-sync_end (int dmabuf_fd, bool write);
-
-/**
- * @brief Get the timestamp of the current time
- * @param[in] extra [OPTIONAL] extra milliseconds to be appended
- * @return the current timestamp (in ms)
- */
-uint64_t
-get_timestamp (uint32_t extra);
-
-#endif /* __NPU_ENGINE_UTILS_H__ */
--- /dev/null
+ne_core_utils_inc = include_directories('.')
+ne_core_utils_src = [
+ 'ne-utils.cc',
+ 'ne-conf.cc'
+]
+
+ne_core_utils_dep = declare_dependency (
+ sources : ne_core_utils_src,
+ dependencies : [iniparser_dep],
+ include_directories : ne_core_utils_inc
+)
--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
+ * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file ne-conf.cc
+ * @date 26 Jun 2019
+ * @brief Allow to access global configuration of NPU Engine.
+ * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
+ * @author MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <linux/limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <string>
+#include "ne-conf.h"
+
+/** @brief singleton instance for configuration */
+Conf Conf::instance_;
+
+/**
+ * @brief get the instance of configuration after reloading .ini file
+ * @param[in] [OPTIONAL] inipath the path of .ini file
+ * @return the conf instance
+ */
+Conf *
+Conf::getConf (const char *inipath)
+{
+ instance_.loadConf (inipath);
+ return &instance_;
+}
+
+/**
+ * @breif error callback of iniparser, used to ignore its error message
+ */
+static int error_callback (const char*, ...)
+{
+ return 0;
+}
+
+/**
+ * @brief Load the configuration from .ini file and env-vars.
+ * @param[in] inipath the path of .ini file (e.g., /etc/npu-engine.ini)
+ * @detail Refer to the header file for more detail.
+ */
+void
+Conf::loadConf (const char *inipath)
+{
+ static const char INI_FILE_NAME[] = "npu-engine.ini";
+ char default_ini_path[PATH_MAX];
+
+ snprintf(default_ini_path, PATH_MAX, "%s/%s", NE_INIDIR, INI_FILE_NAME);
+
+ /** Ignore error messages */
+ iniparser_set_error_callback (error_callback);
+
+ loadConfDefault();
+
+ /** Load up config from ini if available */
+ dictionary *ini = iniparser_load (inipath != nullptr ? inipath : default_ini_path);
+ if (ini) {
+ loadConfIni(ini);
+ iniparser_freedict(ini);
+ }
+
+ /** Load up config from envvar if available */
+ loadConfEnvvar();
+}
+
+/**
+ * @brief load the default configuration
+ */
+void
+Conf::loadConfDefault ()
+{
+ static bool default_loaded = false;
+
+ if (default_loaded) return;
+
+ default_loaded = true;
+ reset ();
+}
+
+void
+Conf::reset ()
+{
+ setResvMemSize (std::to_string (DEFAULT_RESV_MEM_SIZE).c_str());
+ setNumThreads (std::to_string (DEFAULT_NUM_THREADS).c_str());
+ setLogDir (DEFAULT_LOG_DIR);
+}
+
+/**
+ * @brief Set the configuration of reserved memory size
+ * @param[in] resv_mem_size config string for reserved mem size
+ * @param[in] working_dir config string for working dir
+ * @param[in] log_dir config string for log dir
+ */
+void
+Conf::setResvMemSize (const char *resv_mem_size)
+{
+ if (resv_mem_size) {
+ unsigned long val;
+ char *unit = NULL;
+
+ /** the calling program should set errno to 0 before the call */
+ errno = 0;
+ /** strtoul() convert the initial part of the string to unsigned long */
+ val = strtoul (resv_mem_size, &unit, 10);
+ if (errno == 0) {
+ if (unit) {
+ /** the address of the first invalid character was stored, which might be a unit */
+ if (*unit == 'K' || *unit == 'k') {
+ val *= 1024;
+ } else if (*unit == 'M' || *unit == 'm') {
+ val *= 1024 * 1024;
+ } else if (*unit == 'G' || *unit == 'g') {
+ val *= 1024 * 1024 * 1024;
+ }
+ }
+ if (val <= SIZE_MAX) {
+ /** zero size is now allowed to indicate the KERNEL_CMA mode */
+ reserved_mem_size_ = static_cast<size_t> (val);
+ }
+ }
+ }
+}
+
+/**
+ * @brief Set the configuration of log directory
+ * @param[in] log_dir config string for log dir
+ */
+void
+Conf::setLogDir (const char *log_dir)
+{
+ if (log_dir && strlen(log_dir) < MAX_DIR_LEN) {
+ memcpy (log_dir_, log_dir, strlen(log_dir));
+ log_dir_[strlen(log_dir)] = '\x00';
+ }
+}
+
+/**
+ * @brief Set the configuration of # threads in thread pool
+ * @param[in] num_threads config string for a number of threads
+ */
+void
+Conf::setNumThreads (const char *num_threads)
+{
+ if (num_threads) {
+ unsigned long val;
+
+ errno = 0;
+ val = strtoul (num_threads, nullptr, 10);
+ if (errno == 0 && val <= UINT32_MAX)
+ num_threads_ = static_cast<uint32_t> (val);
+ }
+}
+
+/**
+ * @brief Load configuration with .ini file.
+ * @param[in] ini The parsed .ini file.
+ * @note if a config param is nullptr, it is not set. so use default one.
+*/
+void
+Conf::loadConfIni(dictionary *ini)
+{
+ const char *log_dir = iniparser_getstring(ini, "main:log_dir", nullptr);
+ const char *resv_mem_size = iniparser_getstring(ini, "main:resv_mem_size", nullptr);
+ const char *num_threads = iniparser_getstring (ini, "main:num_threads", nullptr);
+
+ setLogDir (log_dir);
+ setResvMemSize (resv_mem_size);
+ setNumThreads (num_threads);
+}
+
+/**
+ * @brief Load configuration with env-vars
+ */
+void
+Conf::loadConfEnvvar()
+{
+ const char *log_dir = getenv (ENV_LOG_DIR);
+ const char *resv_mem_size = getenv (ENV_RESV_MEM_SIZE);
+ const char *num_threads = getenv (ENV_NUM_THREADS);
+
+ setLogDir (log_dir);
+ setResvMemSize (resv_mem_size);
+ setNumThreads (num_threads);
+}
--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
+ * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file ne-conf.h
+ * @date 15 Apr 2019
+ * @brief Internal API to access global configuration of NPU Engine.
+ * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
+ * @author MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#ifndef __NE_CONF_H__
+#define __NE_CONF_H__
+
+#include <stdint.h>
+#include <iniparser.h>
+
+#define MAX_DIR_LEN 256
+
+#define DEFAULT_LOG_DIR "/tmp/"
+#define DEFAULT_RESV_MEM_SIZE 0 /* default mode is the CMA allocation */
+#define DEFAULT_NUM_THREADS 8
+
+#define ENV_LOG_DIR "NE_LOG_DIR"
+#define ENV_RESV_MEM_SIZE "NE_RESV_MEM_SIZE"
+#define ENV_NUM_THREADS "NE_NUM_THREADS"
+
+/** @brief Configuration for NPU-Engine components */
+class Conf {
+ public:
+ static Conf* getConf (const char *inipath = nullptr);
+
+ size_t getResvMemSize() { return reserved_mem_size_; }
+ uint32_t getNumThreads() { return num_threads_; }
+ const char * getLogDir() { return log_dir_; }
+
+ void reset();
+
+ private:
+ static Conf instance_;
+
+ void loadConf (const char *inipath);
+ void loadConfIni (dictionary *ini);
+ void loadConfDefault ();
+ void loadConfEnvvar();
+
+ void setResvMemSize (const char *str);
+ void setNumThreads (const char *str);
+ void setLogDir (const char *str);
+
+ size_t reserved_mem_size_;
+ /**< NE_RESV_MEM_SIZE, [main] resv_mem_size, the size of memory reserved */
+ uint32_t num_threads_;
+ /**< NE_NUM_THREADS, [main] num_threads, the number of threads in thread pool */
+ char log_dir_[MAX_DIR_LEN];
+ /**< NE_LOG_DIR, [main] log_dir, the path where log files are created */
+};
+
+#endif /* __NE_CONF_H__ */
--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file NE-utils.cc
+ * @date 13 June 2019
+ * @brief Utility libraries for NPU Engine and other related components.
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <sys/timeb.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "ne-utils.h"
+#include "ne-conf.h"
+
+/********************************************************************
+ * Logging utilities *
+ ********************************************************************/
+#define logfilename "npu-engine.log"
+
+/**
+ * @brief Log Level String in 5 characters
+ */
+static const char *loglevelstr[LOG_END] = {
+ [LOG_INFO] = "INFO ",
+ [LOG_WARN] = "WARN ",
+ [LOG_ERROR] = "ERROR",
+};
+
+/**
+ * @brief Module Name String in 8 characters
+ */
+static const char *modulestr[_NEND] = {
+ [_N1] = "N1/Comm ",
+ [_N11] = "N11/IP ",
+ [_N12] = "N12/FS ",
+ [_N13] = "N13/USB ",
+ [_N2] = "N2/HostH",
+ [_N3] = "N3/Sched",
+ [_N4] = "N4/InfCt",
+ [_N41] = "N41/iCAM",
+ [_N42] = "N42/Host",
+ [_N43] = "N43/iMIC",
+ [_N44] = "N44/ARMp",
+ [_N45] = "N45/iHW ",
+ [_N50] = "N50/Pool",
+ [_N7] = "N7/Mem ",
+ [_N71] = "N71/CMA ",
+ [_N72] = "N72/IOMM",
+ [_N9] = "N9/GEM ",
+ [_N95] = "N95/TLB ",
+};
+
+/** @brief singleton instance */
+std::unique_ptr<Logger> Logger::instance_;
+std::once_flag Logger::once_flag_;
+
+/** @brief logger destructor. close the file pointer if opened before */
+Logger::~Logger ()
+{
+ std::unique_lock<std::mutex> lock (m_);
+ if (fp_ != nullptr)
+ fclose (fp_);
+}
+
+Logger &
+Logger::getInstance ()
+{
+ call_once (once_flag_, []() {
+ instance_.reset (new Logger);
+ });
+ return *(instance_.get ());
+}
+
+/**
+ * @brief Write a log, to a logfile designated by conf
+ * @param[in] l Loglevel
+ * @param[in] m Module designation
+ */
+void
+Logger::logwrite (loglevel l, module m, const char *format, ...)
+{
+ std::unique_lock<std::mutex> lock (m_);
+
+ time_t ltime = time (NULL);
+ char* time_str;
+ va_list args;
+
+ if (fp_ == NULL) {
+ char filename[FILENAME_MAX];
+
+ snprintf(filename, FILENAME_MAX, "%s%s", Conf::getConf()->getLogDir(), logfilename);
+ fp_ = fopen (filename, "a");
+ }
+
+ assert (fp_ != NULL);
+
+ /**
+ * localtime() and asctime() are not MT-safe. There are alternatives,
+ * localtime_r() and asctime_r(), when __USE_POSIX is set. Without them, we
+ * need critical section here.
+ */
+ time_str = asctime (localtime (<ime));
+
+ time_str[strcspn (time_str, "\n")] = '\x00';
+ fprintf (fp_, "[%s][%s][%s] ", loglevelstr[l], modulestr[m], time_str);
+ va_start (args, format);
+ vfprintf (fp_, format, args);
+ va_end (args);
+
+ fflush (fp_);
+}
--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file NE-utils.h
+ * @date 13 June 2019
+ * @brief Utility libraries for NPU Engine and other related components.
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#ifndef __NPU_ENGINE_UTILS_H__
+#define __NPU_ENGINE_UTILS_H__
+
+#include <string.h>
+#include <map>
+#include <mutex>
+#include <memory>
+
+/********************************************************************
+ * Logging utilities *
+ ********************************************************************/
+
+typedef enum {
+ LOG_INFO = 0,
+ LOG_WARN,
+ LOG_ERROR,
+ LOG_END,
+} loglevel;
+
+/**
+ * @brief Module definitions
+ * @detail
+ * Refer to http://suprem.sec.samsung.net/confluence/pages/viewpage.action?pageId=126187176
+ * Modules out of ne-engine are not described here.
+ * @todo need to fix this after revising the class diagram
+ */
+typedef enum {
+ _N1 = 0,
+ _N11,
+ _N12,
+ _N13,
+ _N2,
+ _N3,
+ _N4,
+ _N41,
+ _N42,
+ _N43,
+ _N44,
+ _N45,
+ _N50,
+ _N7,
+ _N71,
+ _N72,
+ _N9,
+ _N95,
+ _NEND,
+} module;
+
+/**
+ * @brief class for logging (singleton)
+ */
+class Logger {
+ public:
+ static Logger& getInstance ();
+
+ void logwrite (loglevel l, module m, const char *format, ...);
+
+ ~Logger ();
+
+ private:
+ Logger () { fp_ = nullptr; }
+
+ static std::unique_ptr<Logger> instance_;
+ static std::once_flag once_flag_;
+
+ FILE *fp_; /**< file pointer of the log file */
+ std::mutex m_; /**< mutex for synchronous writing */
+};
+
+/** @brief log macros with different log levels */
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#define logerr(m, fmt, ...) Logger::getInstance().logwrite(LOG_ERROR, m, "(%s:%d) " fmt, \
+ __FILENAME__, __LINE__, ##__VA_ARGS__)
+#define logwarn(m, fmt, ...) Logger::getInstance().logwrite(LOG_WARN, m, "(%s:%d) " fmt, \
+ __FILENAME__, __LINE__, ##__VA_ARGS__)
+#define loginfo(m, fmt, ...) Logger::getInstance().logwrite(LOG_INFO, m, "(%s:%d) " fmt, \
+ __FILENAME__, __LINE__, ##__VA_ARGS__)
+
+/** @brief thread-safe map */
+template<typename K, typename V>
+class ThreadSafeMap
+{
+ public:
+ ThreadSafeMap () {}
+ ~ThreadSafeMap () {}
+
+ /** @brief find the target element */
+ V * find (K key) {
+ typename std::map<K, std::unique_ptr<V>>::iterator it;
+ std::unique_lock<std::mutex> lock(m_);
+
+ it = map_.find (key);
+ if (it == map_.end())
+ return nullptr;
+
+ return it->second.get();
+ }
+
+ /** @brief insert element to the map */
+ int insert (K key, V * value) {
+ std::unique_lock<std::mutex> lock(m_);
+ auto status = map_.insert (std::make_pair (key, std::unique_ptr<V>(value)));
+
+ if (status.second == false)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /** @brief remove the target element */
+ int remove (K key) {
+ typename std::map<K, std::unique_ptr<V>>::iterator it;
+ std::unique_lock<std::mutex> lock(m_);
+
+ it = map_.find (key);
+ if (it == map_.end())
+ return -ENOENT;
+
+ map_.erase (it);
+
+ return 0;
+ }
+
+ /** @brief remove all elements */
+ void clear() {
+ typename std::map<K, std::unique_ptr<V>>::iterator it;
+ std::unique_lock<std::mutex> lock(m_);
+
+ it = map_.begin ();
+ while (it != map_.end())
+ it = map_.erase (it);
+ }
+
+ private:
+ std::map<K, std::unique_ptr<V>> map_; /**< map internal instance */
+ std::mutex m_; /**< mutex for sync */
+};
+
+#endif /* __NPU_ENGINE_UTILS_H__ */