[Refactor/Utils] Revise utility/conf features
authorDongju Chae <dongju.chae@samsung.com>
Thu, 2 Apr 2020 10:06:47 +0000 (19:06 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Tue, 7 Apr 2020 06:15:07 +0000 (15:15 +0900)
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>
12 files changed:
meson.build
meson_options.txt
npu-engine.ini.in
src/core/ne-conf.c [deleted file]
src/core/ne-conf.h [deleted file]
src/core/ne-utils.c [deleted file]
src/core/ne-utils.h [deleted file]
src/core/utils/meson.build [new file with mode: 0644]
src/core/utils/ne-conf.cc [new file with mode: 0644]
src/core/utils/ne-conf.h [new file with mode: 0644]
src/core/utils/ne-utils.cc [new file with mode: 0644]
src/core/utils/ne-utils.h [new file with mode: 0644]

index 27990a7..6ac4f65 100644 (file)
@@ -1,6 +1,3 @@
-# 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'],
@@ -82,7 +79,7 @@ ne_install_conf.set('LIB_INSTALL_DIR', ne_libdir)
 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
index feac9da..290ec3c 100644 (file)
@@ -1,6 +1,6 @@
 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)
index 6c3f981..30487d8 100644 (file)
@@ -1,4 +1,4 @@
 [main]
 resv_mem_size=@RESV_MEM_SIZE@
-working_dir=@WORKING_DIR@
+num_threads=@NUM_THREADS@
 log_dir=@LOG_DIR@
diff --git a/src/core/ne-conf.c b/src/core/ne-conf.c
deleted file mode 100644 (file)
index f2354b6..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * 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;
-}
diff --git a/src/core/ne-conf.h b/src/core/ne-conf.h
deleted file mode 100644 (file)
index 7d04cd3..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * 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__ */
diff --git a/src/core/ne-utils.c b/src/core/ne-utils.c
deleted file mode 100644 (file)
index c91e441..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-/**
- * 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(&ltime));
-  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;
-}
diff --git a/src/core/ne-utils.h b/src/core/ne-utils.h
deleted file mode 100644 (file)
index 9d151af..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/**
- * 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__ */
diff --git a/src/core/utils/meson.build b/src/core/utils/meson.build
new file mode 100644 (file)
index 0000000..e23d4d7
--- /dev/null
@@ -0,0 +1,11 @@
+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
+)
diff --git a/src/core/utils/ne-conf.cc b/src/core/utils/ne-conf.cc
new file mode 100644 (file)
index 0000000..3462e99
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * 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);
+}
diff --git a/src/core/utils/ne-conf.h b/src/core/utils/ne-conf.h
new file mode 100644 (file)
index 0000000..76d7b98
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * 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__ */
diff --git a/src/core/utils/ne-utils.cc b/src/core/utils/ne-utils.cc
new file mode 100644 (file)
index 0000000..7fcba4b
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * 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 (&ltime));
+
+  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_);
+}
diff --git a/src/core/utils/ne-utils.h b/src/core/utils/ne-utils.h
new file mode 100644 (file)
index 0000000..103f2b4
--- /dev/null
@@ -0,0 +1,152 @@
+/**
+ * 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__ */