1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

move more code to VILLAScommon repo

This commit is contained in:
Steffen Vogel 2018-08-21 13:26:32 +02:00
parent 5e8c602c8d
commit 76b1695586
20 changed files with 15 additions and 3649 deletions

@ -1 +1 @@
Subproject commit 1783a79c1a39f9f4f20f97a3c6adfb80b8cf760e
Subproject commit dd7d75d0aab3801d65f9ff757d82f47f705514af

View file

@ -1,71 +0,0 @@
/** Static server configuration
*
* This file contains some compiled-in settings.
* This settings are not part of the configuration file.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#ifndef V
#define V 2
#endif
/* Paths */
#define PLUGIN_PATH PREFIX "/share/villas/node/plugins"
#define WEB_PATH PREFIX "/share/villas/node/web"
#define SYSFS_PATH "/sys"
#define PROCFS_PATH "/proc"
/** Default number of values in a sample */
#define DEFAULT_SAMPLELEN 64
#define DEFAULT_QUEUELEN 1024
/** Number of hugepages which are requested from the the kernel.
* @see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt */
#define DEFAULT_NR_HUGEPAGES 100
/** Width of log output in characters */
#define LOG_WIDTH 80
#define LOG_HEIGHT 25
/** Socket priority */
#define SOCKET_PRIO 7
/* Protocol numbers */
#define IPPROTO_VILLAS 137
#define ETH_P_VILLAS 0xBABE
#define USER_AGENT "VILLASfpga (" BUILDID ")"
/* Required kernel version */
#define KERNEL_VERSION_MAJ 3
#define KERNEL_VERSION_MIN 6
/** PCIe BAR number of VILLASfpga registers */
#define FPGA_PCI_BAR 0
#define FPGA_PCI_VID_XILINX 0x10ee
#define FPGA_PCI_PID_VFPGA 0x7022
/** AXI Bus frequency for all components
* except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */
#define FPGA_AXI_HZ 125000000 // 125 MHz

View file

@ -36,12 +36,12 @@
#include <jansson.h>
#include <villas/plugin.hpp>
#include <villas/config.h>
#include <villas/memory.hpp>
#include <villas/kernel/pci.h>
#include <villas/kernel/vfio.hpp>
#include <villas/fpga/config.h>
#include <villas/fpga/ip.hpp>
#define PCI_FILTER_DEFAULT_FPGA { \

View file

@ -1,4 +1,7 @@
/** Logging routines that depend on jansson.
/** Compile time configuration
*
* This file contains some compiled-in settings.
* This settings are not part of the configuration file.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
@ -23,15 +26,11 @@
#pragma once
struct log;
/** PCIe BAR number of VILLASfpga registers */
#define FPGA_PCI_BAR 0
#define FPGA_PCI_VID_XILINX 0x10ee
#define FPGA_PCI_PID_VFPGA 0x7022
#include <jansson.h>
#include <villas/log.h>
/** Parse logging configuration. */
int log_parse(struct log *l, json_t *cfg);
/** Print configuration error and exit. */
void jerror(json_error_t *err, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
/** AXI Bus frequency for all components
* except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */
#define FPGA_AXI_HZ 125000000 // 125 MHz

View file

@ -32,8 +32,7 @@
#include <cstdint>
#include <xilinx/xtmrctr.h>
#include <villas/config.h>
#include <villas/fpga/config.h>
#include <villas/fpga/ip.hpp>
namespace villas {

View file

@ -1,98 +0,0 @@
/** Linux kernel related functions.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/** @addtogroup fpga Kernel @{ */
#pragma once
#include <string.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Forward declarations */
struct version;
//#include <sys/capability.h>
/** Check if current process has capability \p cap.
*
* @retval 0 If capabilty is present.
* @retval <0 If capability is not present.
*/
//int kernel_check_cap(cap_value_t cap);
/** Get number of reserved hugepages. */
int kernel_get_nr_hugepages();
/** Set number of reserved hugepages. */
int kernel_set_nr_hugepages(int nr);
/** Get kernel cmdline parameter
*
* See https://www.kernel.org/doc/Documentation/kernel-parameters.txt
*
* @param param The cmdline parameter to look for.
* @param buf The string buffer to which the parameter value will be copied to.
* @param len The length of the buffer \p value
* @retval 0 Parameter \p key was found and value was copied to \p value
* @reval <>0 Kernel was not booted with parameter \p key
*/
int kernel_get_cmdline_param(const char *param, char *buf, size_t len);
/** Get the version of the kernel. */
int kernel_get_version(struct version *v);
/** Checks if a kernel module is loaded
*
* @param module the name of the module
* @retval 0 Module is loaded.
* @reval <>0 Module is not loaded.
*/
int kernel_module_loaded(const char *module);
/** Load kernel module via modprobe */
int kernel_module_load(const char *module);
/** Set parameter of loaded kernel module */
int kernel_module_set_param(const char *module, const char *param, const char *value);
/** Get cacheline size in bytes */
int kernel_get_cacheline_size();
/** Get the size of a standard page in bytes. */
int kernel_get_page_size();
/** Get the size of a huge page in bytes. */
int kernel_get_hugepage_size();
/** Set SMP affinity of IRQ */
int kernel_irq_setaffinity(unsigned irq, uintmax_t affinity, uintmax_t *old);
#ifdef __cplusplus
}
#endif
/** @} */

View file

@ -1,84 +0,0 @@
/** Linux PCI helpers
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Steffen Vogel
**********************************************************************************/
/** @addtogroup fpga Kernel @{ */
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <villas/list.h>
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
#define PCI_FUNC(devfn) ((devfn) & 0x07)
#ifdef __cplusplus
extern "C" {
#endif
struct pci_device {
struct {
int vendor;
int device;
int class_code;
} id;
struct {
int domain;
int bus;
int device;
int function;
} slot; /**< Bus, Device, Function (BDF) */
};
struct pci_region {
int num;
uintptr_t start;
uintptr_t end;
unsigned long long flags;
};
struct pci {
struct list devices; /**< List of available PCI devices in the system (struct pci_device) */
};
/** Initialize Linux PCI handle.
*
* This search for all available PCI devices under /sys/bus/pci
*
* @retval 0 Success. Everything went well.
* @retval <0 Error. Something went wrong.
*/
int pci_init(struct pci *p);
/** Destroy handle. */
int pci_destroy(struct pci *p);
int pci_device_parse_slot(struct pci_device *f, const char *str, const char **error);
int pci_device_parse_id(struct pci_device *f, const char *str, const char **error);
int pci_device_compare(const struct pci_device *d, const struct pci_device *f);
struct pci_device * pci_lookup_device(struct pci *p, struct pci_device *filter);
/** Get currently loaded driver for device */
int pci_get_driver(const struct pci_device *d, char *buf, size_t buflen);
/** Bind a new LKM to the PCI device */
int pci_attach_driver(const struct pci_device *d, const char *driver);
/** Return the IOMMU group of this PCI device or -1 if the device is not in a group. */
int pci_get_iommu_group(const struct pci_device *d);
size_t pci_get_regions(const struct pci_device *d, struct pci_region** regions);
#ifdef __cplusplus
}
#endif
/** @} */

View file

@ -1,161 +0,0 @@
/** Virtual Function IO wrapper around kernel API
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017-2018, Steffen Vogel
* @copyright 2018, Daniel Krebs
*********************************************************************************/
/** @addtogroup fpga Kernel @{ */
#pragma once
#include <list>
#include <vector>
#include <memory>
#include <linux/vfio.h>
#include <sys/mman.h>
#define VFIO_PATH "/dev/vfio/"
#define VFIO_DEV VFIO_PATH "vfio"
/* Forward declarations */
struct pci_device;
namespace villas {
class VfioContainer;
class VfioGroup;
class VfioDevice {
friend class VfioContainer;
public:
VfioDevice(const std::string& name, VfioGroup& group) :
name(name), group(group) {}
~VfioDevice();
bool reset();
/** Map a device memory region to the application address space (e.g. PCI BARs) */
void* regionMap(size_t index);
/** munmap() a region which has been mapped by vfio_map_region() */
bool regionUnmap(size_t index);
/** Get the size of a device memory region */
size_t regionGetSize(size_t index);
/** Enable memory accesses and bus mastering for PCI device */
bool pciEnable();
bool pciHotReset();
int pciMsiInit(int efds[32]);
int pciMsiDeinit(int efds[32]);
bool pciMsiFind(int nos[32]);
bool isVfioPciDevice() const;
private:
/// Name of the device as listed under
/// /sys/kernel/iommu_groups/[vfio_group::index]/devices/
std::string name;
/// VFIO device file descriptor
int fd;
struct vfio_device_info info;
std::vector<struct vfio_irq_info> irqs;
std::vector<struct vfio_region_info> regions;
std::vector<void*> mappings;
/**< libpci handle of the device */
const struct pci_device *pci_device;
VfioGroup& group; /**< The VFIO group this device belongs to */
};
class VfioGroup {
friend class VfioContainer;
friend VfioDevice;
private:
VfioGroup(int index) : fd(-1), index(index) {}
public:
~VfioGroup();
static std::unique_ptr<VfioGroup>
attach(VfioContainer& container, int groupIndex);
private:
/// VFIO group file descriptor
int fd;
/// Index of the IOMMU group as listed under /sys/kernel/iommu_groups/
int index;
/// Status of group
struct vfio_group_status status;
/// All devices owned by this group
std::list<std::unique_ptr<VfioDevice>> devices;
VfioContainer* container; /**< The VFIO container to which this group is belonging */
};
class VfioContainer {
private:
VfioContainer();
public:
~VfioContainer();
static std::shared_ptr<VfioContainer>
create();
void dump();
VfioDevice& attachDevice(const char *name, int groupIndex);
VfioDevice& attachDevice(const struct pci_device *pdev);
/**
* @brief Map VM to an IOVA, which is accessible by devices in the container
* @param virt virtual address of memory
* @param phys IOVA where to map @p virt, -1 to use VFIO internal allocator
* @param length size of memory region in bytes
* @return IOVA address, UINTPTR_MAX on failure
*/
uintptr_t memoryMap(uintptr_t virt, uintptr_t phys, size_t length);
/** munmap() a region which has been mapped by vfio_map_region() */
bool memoryUnmap(uintptr_t phys, size_t length);
bool isIommuEnabled() const
{ return this->hasIommu; }
const int& getFd() const
{ return fd; }
private:
VfioGroup& getOrAttachGroup(int index);
private:
int fd;
int version;
int extensions;
uint64_t iova_next; /**< Next free IOVA address */
bool hasIommu;
/// All groups bound to this container
std::list<std::unique_ptr<VfioGroup>> groups;
};
/** @} */
} // namespace villas

View file

@ -1,120 +0,0 @@
/** A generic list implementation.
*
* This is a generic implementation of a list which can store void pointers to
* arbitrary data. The data itself is not stored or managed by the list.
*
* Internally, an array of pointers is used to store the pointers.
* If needed, this array will grow by realloc().
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
*********************************************************************************/
#pragma once
#include <stdbool.h>
#include <pthread.h>
#include <villas/common.h>
#define LIST_CHUNKSIZE 16
/** Static list initialization */
#define LIST_INIT() { \
.array = NULL, \
.length = 0, \
.capacity = 0, \
.lock = PTHREAD_MUTEX_INITIALIZER, \
.state = STATE_INITIALIZED \
}
#define LIST_INIT_STATIC(l) \
__attribute__((constructor(105))) static void UNIQUE(__ctor)() {\
if ((l)->state == STATE_DESTROYED) \
list_init(l); \
} \
__attribute__((destructor(105))) static void UNIQUE(__dtor)() { \
list_destroy(l, NULL, false); \
}
#define list_length(list) ((list)->length)
#define list_at_safe(list, index) ((list)->length > index ? (list)->array[index] : NULL)
#define list_at(list, index) ((list)->array[index])
#define list_first(list) list_at(list, 0)
#define list_last(list) list_at(list, (list)->length-1)
#ifdef __cplusplus
extern "C" {
#endif
/** Callback to destroy list elements.
*
* @param data A pointer to the data which should be freed.
*/
typedef int (*dtor_cb_t)(void *);
/** Callback to search or sort a list. */
typedef int (*cmp_cb_t)(const void *, const void *);
/* The list data structure. */
struct list {
void **array; /**< Array of pointers to list elements */
size_t capacity; /**< Size of list::array in elements */
size_t length; /**< Number of elements of list::array which are in use */
pthread_mutex_t lock; /**< A mutex to allow thread-safe accesses */
enum state state; /**< The state of this list. */
};
/** Initialize a list.
*
* @param l A pointer to the list data structure.
*/
int list_init(struct list *l);
/** Destroy a list and call destructors for all list elements
*
* @param free free() all list members during when calling list_destroy()
* @param dtor A function pointer to a desctructor which will be called for every list item when the list is destroyed.
* @param l A pointer to the list data structure.
*/
int list_destroy(struct list *l, dtor_cb_t dtor, bool free);
/** Append an element to the end of the list */
void list_push(struct list *l, void *p);
/** Remove all occurences of a list item */
void list_remove(struct list *l, void *p);
/** Return the first list element which is identified by a string in its first member variable.
*
* List elements are pointers to structures of the following form:
*
* struct obj {
* char *name;
* // more members
* }
*
* @see Only possible because of §1424 of http://c0x.coding-guidelines.com/6.7.2.1.html
*/
void * list_lookup(struct list *l, const char *name);
/** Return the first element of the list for which cmp returns zero */
void * list_search(struct list *l, cmp_cb_t cmp, void *ctx);
/** Returns the number of occurences for which cmp returns zero when called on all list elements. */
int list_count(struct list *l, cmp_cb_t cmp, void *ctx);
/** Return 0 if list contains pointer p */
int list_contains(struct list *l, void *p);
/** Sort the list using the quicksort algorithm of libc */
void list_sort(struct list *l, cmp_cb_t cmp);
/** Set single element in list */
int list_set(struct list *l, int index, void *value);
#ifdef __cplusplus
}
#endif

View file

@ -1,194 +0,0 @@
/** Logging and debugging routines
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <time.h>
#include <sys/ioctl.h>
#include <villas/common.h>
#include <villas/log_config.h>
#ifdef __GNUC__
#define INDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_indent(1);
#define NOINDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_noindent();
#else
#define INDENT ;
#define NOINDENT ;
#endif
/* The log level which is passed as first argument to print() */
#define LOG_LVL_DEBUG CLR_GRY("Debug")
#define LOG_LVL_INFO CLR_WHT("Info ")
#define LOG_LVL_WARN CLR_YEL("Warn ")
#define LOG_LVL_ERROR CLR_RED("Error")
#define LOG_LVL_STATS CLR_MAG("Stats")
/** Debug facilities.
*
* To be or-ed with the debug level
*/
enum log_facilities {
LOG_POOL = (1L << 8),
LOG_QUEUE = (1L << 9),
LOG_CONFIG = (1L << 10),
LOG_HOOK = (1L << 11),
LOG_PATH = (1L << 12),
LOG_NODE = (1L << 13),
LOG_MEM = (1L << 14),
LOG_WEB = (1L << 15),
LOG_API = (1L << 16),
LOG_LOG = (1L << 17),
LOG_VFIO = (1L << 18),
LOG_PCI = (1L << 19),
LOG_XIL = (1L << 20),
LOG_TC = (1L << 21),
LOG_IF = (1L << 22),
LOG_ADVIO = (1L << 23),
/* Node-types */
LOG_SOCKET = (1L << 24),
LOG_FILE = (1L << 25),
LOG_FPGA = (1L << 26),
LOG_NGSI = (1L << 27),
LOG_WEBSOCKET = (1L << 28),
LOG_OPAL = (1L << 30),
/* Classes */
LOG_NODES = LOG_NODE | LOG_SOCKET | LOG_FILE | LOG_FPGA | LOG_NGSI | LOG_WEBSOCKET | LOG_OPAL,
LOG_KERNEL = LOG_VFIO | LOG_PCI | LOG_TC | LOG_IF,
LOG_ALL = ~0xFF
};
struct log {
enum state state;
struct timespec epoch; /**< A global clock used to prefix the log messages. */
struct winsize window; /**< Size of the terminal window. */
int width; /**< The real usable log output width which fits into one line. */
/** Debug level used by the debug() macro.
* It defaults to V (defined by the Makefile) and can be
* overwritten by the 'debug' setting in the configuration file. */
int level;
long facilities; /**< Debug facilities used by the debug() macro. */
const char *path; /**< Path of the log file. */
char *prefix; /**< Prefix each line with this string. */
int syslog; /**< Whether or not to log to syslogd. */
FILE *file; /**< Send all log output to this file / stdout / stderr. */
};
/** The global log instance. */
extern struct log *global_log;
extern struct log default_log;
/** Initialize log object */
int log_init(struct log *l, int level, long faciltities);
int log_start(struct log *l);
int log_stop(struct log *l);
/** Destroy log object */
int log_destroy(struct log *l);
/** Change log indention for current thread.
*
* The argument level can be negative!
*/
int log_indent(int levels);
/** Disable log indention of current thread. */
int log_noindent();
/** A helper function the restore the previous log indention level.
*
* This function is usually called by a __cleanup__ handler (GCC C Extension).
* See INDENT macro.
*/
void log_outdent(int *);
/** Set logging facilities based on expression.
*
* Currently we support two types of expressions:
* 1. A comma seperated list of logging facilities
* 2. A comma seperated list of logging facilities which is prefixes with an exclamation mark '!'
*
* The first case enables only faciltities which are in the list.
* The second case enables all faciltities with exception of those which are in the list.
*
* @param expression The expression
* @return The new facilties mask (see enum log_faciltities)
*/
int log_set_facility_expression(struct log *l, const char *expression);
/** Logs variadic messages to stdout.
*
* @param lvl The log level
* @param fmt The format string (printf alike)
*/
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
__attribute__ ((format(printf, 3, 4)));
/** Logs variadic messages to stdout.
*
* @param lvl The log level
* @param fmt The format string (printf alike)
* @param va The variadic argument list (see stdarg.h)
*/
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list va);
/** Printf alike debug message with level. */
void debug(long lvl, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
/** Printf alike info message. */
void info(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
/** Printf alike warning message. */
void warn(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
/** Printf alike statistics message. */
void stats(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
/** Print error and exit. */
void error(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
/** Print error and strerror(errno). */
void serror(const char *fmt, ...)
__attribute__ ((format(printf, 1, 2)));
#ifdef __cplusplus
}
#endif

View file

@ -1,284 +0,0 @@
/** Various helper functions.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <sched.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <villas/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x),1)
#define UNLIKELY(x) __builtin_expect((x),0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
/* Some color escape codes for pretty log messages */
#define CLR(clr, str) "\e[" XSTR(clr) "m" str "\e[0m"
#define CLR_GRY(str) CLR(30, str) /**< Print str in gray */
#define CLR_RED(str) CLR(31, str) /**< Print str in red */
#define CLR_GRN(str) CLR(32, str) /**< Print str in green */
#define CLR_YEL(str) CLR(33, str) /**< Print str in yellow */
#define CLR_BLU(str) CLR(34, str) /**< Print str in blue */
#define CLR_MAG(str) CLR(35, str) /**< Print str in magenta */
#define CLR_CYN(str) CLR(36, str) /**< Print str in cyan */
#define CLR_WHT(str) CLR(37, str) /**< Print str in white */
#define CLR_BLD(str) CLR( 1, str) /**< Print str in bold */
/* Alternate character set
*
* The suffixed of the BOX_ macro a constructed by
* combining the following letters in the written order:
* - U for a line facing upwards
* - D for a line facing downwards
* - L for a line facing leftwards
* - R for a line facing rightwards
*
* E.g. a cross can be constructed by combining all line fragments:
* BOX_UDLR
*/
#define BOX(chr) "\e(0" chr "\e(B"
#define BOX_LR BOX("\x71") /**< Boxdrawing: ─ */
#define BOX_UD BOX("\x78") /**< Boxdrawing: │ */
#define BOX_UDR BOX("\x74") /**< Boxdrawing: ├ */
#define BOX_UDLR BOX("\x6E") /**< Boxdrawing: ┼ */
#define BOX_UDL BOX("\x75") /**< Boxdrawing: ┤ */
#define BOX_ULR BOX("\x76") /**< Boxdrawing: ┴ */
#define BOX_UL BOX("\x6A") /**< Boxdrawing: ┘ */
#define BOX_DLR BOX("\x77") /**< Boxdrawing: ┘ */
#define BOX_DL BOX("\x6B") /**< Boxdrawing: ┘ */
/* CPP stringification */
#define XSTR(x) STR(x)
#define STR(x) #x
#define CONCAT_DETAIL(x, y) x##y
#define CONCAT(x, y) CONCAT_DETAIL(x, y)
#define UNIQUE(x) CONCAT(x, __COUNTER__)
#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t) (a) - 1)
#define ALIGN_MASK(x, m) (((uintptr_t) (x) + (m)) & ~(m))
#define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t) x)
#define SWAP(x,y) do { \
__auto_type _x = x; \
__auto_type _y = y; \
x = _y; \
y = _x; \
} while(0)
/** Round-up integer division */
#define CEIL(x, y) (((x) + (y) - 1) / (y))
/** Get nearest up-rounded power of 2 */
#define LOG2_CEIL(x) (1 << (log2i((x) - 1) + 1))
/** Check if the number is a power of 2 */
#define IS_POW2(x) (((x) != 0) && !((x) & ((x) - 1)))
/** Calculate the number of elements in an array. */
#define ARRAY_LEN(a) ( sizeof (a) / sizeof (a)[0] )
/* Return the bigger value */
#define MAX(a, b) ({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
/* Return the smaller value */
#define MIN(a, b) ({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#ifndef offsetof
#define offsetof(type, member) __builtin_offsetof(type, member)
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({ const typeof( ((type *) 0)->member ) *__mptr = (ptr); \
(type *) ( (char *) __mptr - offsetof(type, member) ); \
})
#endif
#define BITS_PER_LONGLONG (sizeof(long long) * 8)
/* Some helper macros */
#define BITMASK(h, l) (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONGLONG - 1 - (h))))
#define BIT(nr) (1UL << (nr))
/* Forward declarations */
struct timespec;
/** Print copyright message to stdout. */
void print_copyright();
/** Print version to stdout. */
void print_version();
/** Normal random variate generator using the Box-Muller method
*
* @param m Mean
* @param s Standard deviation
* @return Normal variate random variable (Gaussian)
*/
double box_muller(float m, float s);
/** Double precission uniform random variable */
double randf();
/** Concat formatted string to an existing string.
*
* This function uses realloc() to resize the destination.
* Please make sure to only on dynamic allocated destionations!!!
*
* @param dest A pointer to a malloc() allocated memory region
* @param fmt A format string like for printf()
* @param ... Optional parameters like for printf()
* @retval The the new value of the dest buffer.
*/
char * strcatf(char **dest, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
/** Variadic version of strcatf() */
char * vstrcatf(char **dest, const char *fmt, va_list va)
__attribute__ ((format(printf, 2, 0)));
/** Format string like strcatf() just starting with empty string */
#define strf(fmt, ...) strcatf(&(char *) { NULL }, fmt, ##__VA_ARGS__)
#define vstrf(fmt, va) vstrcatf(&(char *) { NULL }, fmt, va)
#ifdef __linux__
/** Convert integer to cpu_set_t.
*
* @param set An integer number which is used as the mask
* @param cset A pointer to the cpu_set_t datastructure
*/
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset);
/** Convert cpu_set_t to an integer. */
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set);
/** Parses string with list of CPU ranges.
*
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
*
* @retval 0 On success.
* @retval 1 On error.
* @retval 2 If fail is set and a cpu number passed in the list doesn't fit
* into the cpu_set. If fail is not set cpu numbers that do not fit are
* ignored and 0 is returned instead.
*/
int cpulist_parse(const char *str, cpu_set_t *set, int fail);
/** Returns human readable representation of the cpuset.
*
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
*
* The output format is a list of CPUs with ranges (for example, "0,1,3-9").
*/
char * cpulist_create(char *str, size_t len, cpu_set_t *set);
#endif
/** Allocate and initialize memory. */
void * alloc(size_t bytes);
/** Allocate and copy memory. */
void * memdup(const void *src, size_t bytes);
/** Call quit() in the main thread. */
void die();
/** Used by version_parse(), version_compare() */
struct version {
int major;
int minor;
};
/** Compare two versions. */
int version_cmp(struct version *a, struct version *b);
/** Parse a dotted version string. */
int version_parse(const char *s, struct version *v);
/** Check assertion and exit if failed. */
#ifndef assert
#define assert(exp) do { \
if (!EXPECT(exp, 0)) \
error("Assertion failed: '%s' in %s(), %s:%d", \
XSTR(exp), __FUNCTION__, __BASE_FILE__, __LINE__); \
} while (0)
#endif
/** Fill buffer with random data */
size_t read_random(char *buf, size_t len);
/** Get CPU timestep counter */
__attribute__((always_inline)) static inline uint64_t rdtsc()
{
uint64_t tsc;
__asm__ ("rdtsc;"
"shl $32, %%rdx;"
"or %%rdx,%%rax"
: "=a" (tsc)
:
: "%rcx", "%rdx", "memory");
return tsc;
}
/** Get log2 of long long integers */
static inline int log2i(long long x) {
if (x == 0)
return 1;
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
}
/** Sleep with rdtsc */
void rdtsc_sleep(uint64_t nanosecs, uint64_t start);
/** Register a exit callback for program termination: SIGINT, SIGKILL & SIGALRM. */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx));
/** Send signal \p sig to main thread. */
void killme(int sig);
pid_t spawn(const char *name, char *const argv[]);
/** Determines the string length as printed on the screen (ignores escable sequences). */
size_t strlenp(const char *str);
#ifdef __cplusplus
}
#endif

View file

@ -34,16 +34,6 @@ set(SOURCES
ips/dma.cpp
ips/bram.cpp
ips/rtds.cpp
kernel/kernel.c
kernel/pci.c
kernel/vfio.cpp
utils.c
list.c
log.c
log_config.c
log_helper.c
)
include(FindPkgConfig)
@ -60,6 +50,7 @@ target_link_libraries(villas-fpga PUBLIC villas-common)
# GPU library is optional, check for CUDA presence
include(CheckLanguage)
check_language(CUDA)
if(CMAKE_CUDA_COMPILER)
add_subdirectory(gpu)
target_link_libraries(villas-fpga PUBLIC villas-gpu)

View file

@ -1,283 +0,0 @@
/** Linux kernel related functions.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <villas/utils.h>
#include <villas/config.h>
#include <villas/kernel/kernel.h>
int kernel_get_cacheline_size()
{
#ifdef __linux__
return sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
#else
return 64; /** @todo fixme */
#endif
}
#ifdef __linux__
int kernel_module_set_param(const char *module, const char *param, const char *value)
{
FILE *f;
char fn[256];
snprintf(fn, sizeof(fn), "%s/module/%s/parameters/%s", SYSFS_PATH, module, param);
f = fopen(fn, "w");
if (!f)
serror("Failed set parameter %s for kernel module %s to %s", module, param, value);
debug(LOG_KERNEL | 5, "Set parameter %s of kernel module %s to %s", module, param, value);
fprintf(f, "%s", value);
fclose(f);
return 0;
}
int kernel_module_load(const char *module)
{
int ret;
ret = kernel_module_loaded(module);
if (!ret) {
debug(LOG_KERNEL | 5, "Kernel module %s already loaded...", module);
return 0;
}
pid_t pid = fork();
switch (pid) {
case -1: // error
return -1;
case 0: // child
execlp("modprobe", "modprobe", module, (char *) 0);
exit(EXIT_FAILURE); // exec never returns
default:
wait(&ret);
return kernel_module_loaded(module);
}
}
int kernel_module_loaded(const char *module)
{
FILE *f;
int ret = -1;
char *line = NULL;
size_t len = 0;
f = fopen(PROCFS_PATH "/modules", "r");
if (!f)
return -1;
while (getline(&line, &len, f) >= 0) {
if (strstr(line, module) == line) {
ret = 0;
break;
}
}
free(line);
fclose(f);
return ret;
}
int kernel_get_version(struct version *v)
{
struct utsname uts;
if (uname(&uts) < 0)
return -1;
if (version_parse(uts.release, v))
return -1;
return 0;
}
int kernel_get_cmdline_param(const char *param, char *buf, size_t len)
{
int ret;
char cmdline[512];
FILE *f = fopen(PROCFS_PATH "/cmdline", "r");
if (!f)
return -1;
if (!fgets(cmdline, sizeof(cmdline), f))
goto out;
char *tok = strtok(cmdline, " \t");
do {
char key[128], value[128];
ret = sscanf(tok, "%127[^=]=%127s", key, value);
if (ret >= 1) {
if (ret >= 2)
debug(30, "Found kernel param: %s=%s", key, value);
else
debug(30, "Found kernel param: %s", key);
if (strcmp(param, key) == 0) {
if (ret >= 2 && buf)
strncpy(buf, value, len);
return 0; /* found */
}
}
} while((tok = strtok(NULL, " \t")));
out:
fclose(f);
return -1; /* not found or error */
}
int kernel_get_page_size()
{
return sysconf(_SC_PAGESIZE);
}
/* There is no sysconf interface to get the hugepage size */
int kernel_get_hugepage_size()
{
char *key, *value, *unit, *line = NULL;
int sz = -1;
size_t len = 0;
FILE *f;
f = fopen(PROCFS_PATH "/meminfo", "r");
if (!f)
return -1;
while (getline(&line, &len, f) != -1) {
key = strtok(line, ": ");
value = strtok(NULL, " ");
unit = strtok(NULL, "\n");
if (!strcmp(key, "Hugepagesize") && !strcmp(unit, "kB")) {
sz = strtoul(value, NULL, 10) * 1024;
break;
}
}
free(line);
fclose(f);
return sz;
}
int kernel_get_nr_hugepages()
{
FILE *f;
int nr, ret;
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "r");
if (!f)
serror("Failed to open %s", PROCFS_PATH "/sys/vm/nr_hugepages");
ret = fscanf(f, "%d", &nr);
if (ret != 1)
nr = -1;
fclose(f);
return nr;
}
int kernel_set_nr_hugepages(int nr)
{
FILE *f;
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
if (!f)
serror("Failed to open %s", PROCFS_PATH "/sys/vm/nr_hugepages");
fprintf(f, "%d\n", nr);
fclose(f);
return 0;
}
#if 0
int kernel_has_cap(cap_value_t cap)
{
int ret;
cap_t caps;
cap_flag_value_t value;
caps = cap_get_proc();
if (caps == NULL)
return -1;
ret = cap_get_proc(caps);
if (ret == -1)
return -1;
ret = cap_get_flag(caps, cap, CAP_EFFECTIVE, &value);
if (ret == -1)
return -1;
ret = cap_free(caps);
if (ret)
return -1;
return value == CAP_SET ? 0 : -1;
}
#endif
int kernel_irq_setaffinity(unsigned irq, uintmax_t affinity, uintmax_t *old)
{
char fn[64];
FILE *f;
int ret = 0;
snprintf(fn, sizeof(fn), "/proc/irq/%u/smp_affinity", irq);
f = fopen(fn, "w+");
if (!f)
return errno;
if (old)
ret = fscanf(f, "%jx", old);
fprintf(f, "%jx", affinity);
fclose(f);
return ret;
}
#endif /* __linux__ */

View file

@ -1,388 +0,0 @@
/** Linux PCI helpers
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Steffen Vogel
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <dirent.h>
#include <libgen.h>
#include <string.h>
#include <unistd.h>
#include <linux/limits.h>
#include <villas/log.h>
#include <villas/utils.h>
#include <villas/kernel/pci.h>
#include <villas/config.h>
int pci_init(struct pci *p)
{
struct dirent *e;
DIR *dp;
FILE *f;
char path[PATH_MAX];
int ret;
list_init(&p->devices);
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
dp = opendir(path);
if (dp == NULL) {
serror("Failed to detect PCI devices");
return -1;
}
while ((e = readdir(dp))) {
// ignore special entries
if ((strcmp(e->d_name, ".") == 0) ||
(strcmp(e->d_name, "..") == 0) )
continue;
struct pci_device *d = (struct pci_device *) alloc(sizeof(struct pci_device));
struct { const char *s; int *p; } map[] = {
{ "vendor", &d->id.vendor },
{ "device", &d->id.device }
};
/* Read vendor & device id */
for (int i = 0; i < 2; i++) {
snprintf(path, sizeof(path), "%s/bus/pci/devices/%s/%s", SYSFS_PATH, e->d_name, map[i].s);
f = fopen(path, "r");
if (!f)
serror("Failed to open '%s'", path);
ret = fscanf(f, "%x", map[i].p);
if (ret != 1)
error("Failed to parse %s ID from: %s", map[i].s, path);
fclose(f);
}
/* Get slot id */
ret = sscanf(e->d_name, "%4x:%2x:%2x.%u", &d->slot.domain, &d->slot.bus, &d->slot.device, &d->slot.function);
if (ret != 4)
error("Failed to parse PCI slot number: %s", e->d_name);
list_push(&p->devices, d);
}
closedir(dp);
return 0;
}
int pci_destroy(struct pci *p)
{
list_destroy(&p->devices, NULL, true);
return 0;
}
int pci_device_parse_slot(struct pci_device *f, const char *s, const char **error)
{
char *str = strdup(s);
char *colon = strrchr(str, ':');
char *dot = strchr((colon ? colon + 1 : str), '.');
char *mid = str;
char *e, *bus, *colon2;
if (colon) {
*colon++ = 0;
mid = colon;
colon2 = strchr(str, ':');
if (colon2) {
*colon2++ = 0;
bus = colon2;
if (str[0] && strcmp(str, "*")) {
long int x = strtol(str, &e, 16);
if ((e && *e) || (x < 0 || x > 0x7fffffff)) {
*error = "Invalid domain number";
goto fail;
}
f->slot.domain = x;
}
}
else
bus = str;
if (bus[0] && strcmp(bus, "*")) {
long int x = strtol(bus, &e, 16);
if ((e && *e) || (x < 0 || x > 0xff)) {
*error = "Invalid bus number";
goto fail;
}
f->slot.bus = x;
}
}
if (dot)
*dot++ = 0;
if (mid[0] && strcmp(mid, "*")) {
long int x = strtol(mid, &e, 16);
if ((e && *e) || (x < 0 || x > 0x1f)) {
*error = "Invalid slot number";
goto fail;
}
f->slot.device = x;
}
if (dot && dot[0] && strcmp(dot, "*")) {
long int x = strtol(dot, &e, 16);
if ((e && *e) || (x < 0 || x > 7)) {
*error = "Invalid function number";
goto fail;
}
f->slot.function = x;
}
free(str);
return 0;
fail:
free(str);
return -1;
}
/* ID filter syntax: [vendor]:[device][:class] */
int pci_device_parse_id(struct pci_device *f, const char *str, const char **error)
{
char *s, *c, *e;
if (!*str)
return 0;
s = strchr(str, ':');
if (!s) {
*error = "':' expected";
goto fail;
}
*s++ = 0;
if (str[0] && strcmp(str, "*")) {
long int x = strtol(str, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
*error = "Invalid vendor ID";
goto fail;
}
f->id.vendor = x;
}
c = strchr(s, ':');
if (c)
*c++ = 0;
if (s[0] && strcmp(s, "*")) {
long int x = strtol(s, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
*error = "Invalid device ID";
goto fail;
}
f->id.device = x;
}
if (c && c[0] && strcmp(s, "*")) {
long int x = strtol(c, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
*error = "Invalid class code";
goto fail;
}
f->id.class_code = x;
}
return 0;
fail:
return -1;
}
int pci_device_compare(const struct pci_device *d, const struct pci_device *f)
{
if ((f->slot.domain != 0 && f->slot.domain != d->slot.domain) ||
(f->slot.bus != 0 && f->slot.bus != d->slot.bus) ||
(f->slot.device != 0 && f->slot.device != d->slot.device) ||
(f->slot.function != 0 && f->slot.function != d->slot.function))
return 1;
if ((f->id.device != 0 && f->id.device != d->id.device) ||
(f->id.vendor != 0 && f->id.vendor != d->id.vendor))
return 1;
if ((f->id.class_code != 0) || (f->id.class_code != d->id.class_code))
return 1;
// found
return 0;
}
struct pci_device * pci_lookup_device(struct pci *p, struct pci_device *f)
{
return list_search(&p->devices, (cmp_cb_t) pci_device_compare, (void *) f);
}
size_t pci_get_regions(const struct pci_device *d, struct pci_region** regions)
{
FILE* f;
char sysfs[1024];
assert(regions != NULL);
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/resource",
SYSFS_PATH, d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
f = fopen(sysfs, "r");
if (!f)
serror("Failed to open resource mapping %s", sysfs);
struct pci_region _regions[8];
struct pci_region* cur_region = _regions;
size_t valid_regions = 0;
ssize_t bytesRead;
char* line = NULL;
size_t len = 0;
int region = 0;
// cap to 8 regions, just because we don't know how many may exist
while(region < 8 && (bytesRead = getline(&line, &len, f)) != -1) {
unsigned long long tokens[3];
char* s = line;
for(int i = 0; i < 3; i++) {
char* end;
tokens[i] = strtoull(s, &end, 16);
if(s == end) {
printf("Error parsing line %d of %s\n", region + 1, sysfs);
tokens[0] = tokens[1] = 0; // mark invalid
break;
}
s = end;
}
free(line);
// required for getline() to allocate a new buffer on the next iteration
line = NULL;
len = 0;
if(tokens[0] != tokens[1]) {
// this is a valid region
cur_region->num = region;
cur_region->start = tokens[0];
cur_region->end = tokens[1];
cur_region->flags = tokens[2];
cur_region++;
valid_regions++;
}
region++;
}
if(valid_regions > 0) {
const size_t len = valid_regions * sizeof (struct pci_region);
*regions = malloc(len);
memcpy(*regions, _regions, len);
}
return valid_regions;
}
int pci_get_driver(const struct pci_device *d, char *buf, size_t buflen)
{
int ret;
char sysfs[1024], syml[1024];
memset(syml, 0, sizeof(syml));
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/driver", SYSFS_PATH,
d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
ret = readlink(sysfs, syml, sizeof(syml));
if (ret < 0)
return ret;
char *driver = basename(syml);
strncpy(buf, driver, buflen);
return 0;
}
int pci_attach_driver(const struct pci_device *d, const char *driver)
{
FILE *f;
char fn[1024];
/* Add new ID to driver */
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH, driver);
f = fopen(fn, "w");
if (!f)
serror("Failed to add PCI id to %s driver (%s)", driver, fn);
info("Adding ID to %s module: %04x %04x", driver, d->id.vendor, d->id.device);
fprintf(f, "%04x %04x", d->id.vendor, d->id.device);
fclose(f);
/* Bind to driver */
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/bind", SYSFS_PATH, driver);
f = fopen(fn, "w");
if (!f)
serror("Failed to bind PCI device to %s driver (%s)", driver, fn);
info("Bind device to %s driver", driver);
fprintf(f, "%04x:%02x:%02x.%x\n", d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
fclose(f);
return 0;
}
int pci_get_iommu_group(const struct pci_device *d)
{
int ret;
char *group, link[1024], sysfs[1024];
memset(link, 0, sizeof(link));
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
ret = readlink(sysfs, link, sizeof(link));
if (ret < 0)
return -1;
group = basename(link);
return atoi(group);
}

View file

@ -1,803 +0,0 @@
/** Virtual Function IO wrapper around kernel API
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017-2018, Steffen Vogel
* @copyright 2018, Daniel Krebs
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#define _DEFAULT_SOURCE
#include <algorithm>
#include <string>
#include <sstream>
#include <limits>
#include <cstdlib>
#include <cstring>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/eventfd.h>
#include <linux/pci_regs.h>
#include <villas/log.hpp>
#include <villas/kernel/pci.h>
#include <villas/kernel/kernel.h>
#include <villas/kernel/vfio.hpp>
static auto logger = loggerGetOrCreate("Vfio");
static const char *vfio_pci_region_names[] = {
"PCI_BAR0", // VFIO_PCI_BAR0_REGION_INDEX,
"PCI_BAR1", // VFIO_PCI_BAR1_REGION_INDEX,
"PCI_BAR2", // VFIO_PCI_BAR2_REGION_INDEX,
"PCI_BAR3", // VFIO_PCI_BAR3_REGION_INDEX,
"PCI_BAR4", // VFIO_PCI_BAR4_REGION_INDEX,
"PCI_BAR5", // VFIO_PCI_BAR5_REGION_INDEX,
"PCI_ROM", // VFIO_PCI_ROM_REGION_INDEX,
"PCI_CONFIG", // VFIO_PCI_CONFIG_REGION_INDEX,
"PCI_VGA" // VFIO_PCI_INTX_IRQ_INDEX,
};
static const char *vfio_pci_irq_names[] = {
"PCI_INTX", // VFIO_PCI_INTX_IRQ_INDEX,
"PCI_MSI", // VFIO_PCI_MSI_IRQ_INDEX,
"PCI_MSIX", // VFIO_PCI_MSIX_IRQ_INDEX,
"PCI_ERR", // VFIO_PCI_ERR_IRQ_INDEX,
"PCI_REQ" // VFIO_PCI_REQ_IRQ_INDEX,
};
namespace villas {
VfioContainer::VfioContainer()
: iova_next(0)
{
static constexpr const char* requiredKernelModules[] = {
"vfio", "vfio_pci", "vfio_iommu_type1"
};
for(const char* module : requiredKernelModules) {
if(kernel_module_loaded(module) != 0) {
logger->error("Kernel module '{}' required but not loaded. "
"Please load manually!", module);
throw std::exception();
}
}
/* Open VFIO API */
fd = open(VFIO_DEV, O_RDWR);
if (fd < 0) {
logger->error("Failed to open VFIO container");
throw std::exception();
}
/* Check VFIO API version */
version = ioctl(fd, VFIO_GET_API_VERSION);
if (version < 0 || version != VFIO_API_VERSION) {
logger->error("Failed to get VFIO version");
throw std::exception();
}
/* Check available VFIO extensions (IOMMU types) */
extensions = 0;
for (unsigned int i = VFIO_TYPE1_IOMMU; i <= VFIO_NOIOMMU_IOMMU; i++) {
int ret = ioctl(fd, VFIO_CHECK_EXTENSION, i);
if (ret < 0) {
logger->error("Failed to get VFIO extensions");
throw std::exception();
}
else if (ret > 0) {
extensions |= (1 << i);
}
}
hasIommu = false;
if(not (extensions & (1 << VFIO_NOIOMMU_IOMMU))) {
if(not (extensions & (1 << VFIO_TYPE1_IOMMU))) {
logger->error("No supported IOMMU extension found");
throw std::exception();
} else {
hasIommu = true;
}
}
logger->debug("Version: {:#x}", version);
logger->debug("Extensions: {:#x}", extensions);
logger->debug("IOMMU: {}", hasIommu ? "yes" : "no");
}
VfioContainer::~VfioContainer()
{
logger->debug("Clean up container with fd {}", fd);
/* Release memory and close fds */
groups.clear();
/* Close container */
int ret = close(fd);
if (ret < 0) {
logger->error("Cannot close vfio container fd {}", fd);
}
}
std::shared_ptr<VfioContainer>
VfioContainer::create()
{
std::shared_ptr<VfioContainer> container { new VfioContainer };
return container;
}
void
VfioContainer::dump()
{
logger->info("File descriptor: {}", fd);
logger->info("Version: {}", version);
logger->info("Extensions: 0x{:x}", extensions);
for(auto& group : groups) {
logger->info("VFIO Group {}, viable={}, container={}",
group->index,
(group->status.flags & VFIO_GROUP_FLAGS_VIABLE) > 0,
(group->status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET) > 0
);
for(auto& device : group->devices) {
logger->info("Device {}: regions={}, irqs={}, flags={}",
device->name,
device->info.num_regions,
device->info.num_irqs,
device->info.flags
);
for (size_t i = 0; i < device->info.num_regions && i < 8; i++) {
struct vfio_region_info *region = &device->regions[i];
if (region->size > 0)
logger->info("Region {} {}: size={}, offset={}, flags={}",
region->index,
(device->info.flags & VFIO_DEVICE_FLAGS_PCI) ?
vfio_pci_region_names[i] : "",
region->size,
region->offset,
region->flags
);
}
for (size_t i = 0; i < device->info.num_irqs; i++) {
struct vfio_irq_info *irq = &device->irqs[i];
if (irq->count > 0)
logger->info("IRQ {} {}: count={}, flags={}",
irq->index,
(device->info.flags & VFIO_DEVICE_FLAGS_PCI ) ?
vfio_pci_irq_names[i] : "",
irq->count,
irq->flags
);
}
}
}
}
VfioDevice&
VfioContainer::attachDevice(const char* name, int index)
{
VfioGroup& group = getOrAttachGroup(index);
auto device = std::make_unique<VfioDevice>(name, group);
/* Open device fd */
device->fd = ioctl(group.fd, VFIO_GROUP_GET_DEVICE_FD, name);
if (device->fd < 0) {
logger->error("Failed to open VFIO device: {}", device->name);
throw std::exception();
}
/* Get device info */
device->info.argsz = sizeof(device->info);
int ret = ioctl(device->fd, VFIO_DEVICE_GET_INFO, &device->info);
if (ret < 0) {
logger->error("Failed to get VFIO device info for: {}", device->name);
throw std::exception();
}
logger->debug("Device has {} regions", device->info.num_regions);
logger->debug("Device has {} IRQs", device->info.num_irqs);
// reserve slots already so that we can use the []-operator for access
device->irqs.resize(device->info.num_irqs);
device->regions.resize(device->info.num_regions);
device->mappings.resize(device->info.num_regions);
/* Get device regions */
for (size_t i = 0; i < device->info.num_regions && i < 8; i++) {
struct vfio_region_info region;
memset(&region, 0, sizeof (region));
region.argsz = sizeof(region);
region.index = i;
ret = ioctl(device->fd, VFIO_DEVICE_GET_REGION_INFO, &region);
if (ret < 0) {
logger->error("Failed to get region of VFIO device: {}", device->name);
throw std::exception();
}
device->regions[i] = region;
}
/* Get device irqs */
for (size_t i = 0; i < device->info.num_irqs; i++) {
struct vfio_irq_info irq;
memset(&irq, 0, sizeof (irq));
irq.argsz = sizeof(irq);
irq.index = i;
ret = ioctl(device->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
if (ret < 0) {
logger->error("Failed to get IRQs of VFIO device: {}", device->name);
throw std::exception();
}
device->irqs[i] = irq;
}
group.devices.push_back(std::move(device));
return *group.devices.back().get();
}
VfioDevice&
VfioContainer::attachDevice(const pci_device* pdev)
{
int ret;
char name[32];
static constexpr const char* kernelDriver = "vfio-pci";
/* Load PCI bus driver for VFIO */
if (kernel_module_load("vfio_pci")) {
logger->error("Failed to load kernel driver: vfio_pci");
throw std::exception();
}
/* Bind PCI card to vfio-pci driver if not already bound */
ret = pci_get_driver(pdev, name, sizeof(name));
if (ret || strcmp(name, kernelDriver)) {
logger->debug("Bind PCI card to kernel driver '{}'", kernelDriver);
ret = pci_attach_driver(pdev, kernelDriver);
if (ret) {
logger->error("Failed to attach device to driver");
throw std::exception();
}
}
/* Get IOMMU group of device */
int index = isIommuEnabled() ? pci_get_iommu_group(pdev) : 0;
if (index < 0) {
logger->error("Failed to get IOMMU group of device");
throw std::exception();
}
/* VFIO device name consists of PCI BDF */
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev->slot.domain,
pdev->slot.bus, pdev->slot.device, pdev->slot.function);
logger->info("Attach to device {} with index {}", std::string(name), index);
auto& device = attachDevice(name, index);
device.pci_device = pdev;
/* Check if this is really a vfio-pci device */
if(not device.isVfioPciDevice()) {
logger->error("Device is not a vfio-pci device");
throw std::exception();
}
return device;
}
uintptr_t
VfioContainer::memoryMap(uintptr_t virt, uintptr_t phys, size_t length)
{
int ret;
if(not hasIommu) {
logger->error("DMA mapping not supported without IOMMU");
return UINTPTR_MAX;
}
if (length & 0xFFF) {
length += 0x1000;
length &= ~0xFFF;
}
/* Super stupid allocator */
size_t iovaIncrement = 0;
if (phys == UINTPTR_MAX) {
phys = this->iova_next;
iovaIncrement = length;
}
struct vfio_iommu_type1_dma_map dmaMap;
memset(&dmaMap, 0, sizeof(dmaMap));
dmaMap.argsz = sizeof(dmaMap);
dmaMap.vaddr = virt;
dmaMap.iova = phys;
dmaMap.size = length;
dmaMap.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
ret = ioctl(this->fd, VFIO_IOMMU_MAP_DMA, &dmaMap);
if (ret) {
logger->error("Failed to create DMA mapping: {}", ret);
return UINTPTR_MAX;
}
logger->debug("DMA map size={:#x}, iova={:#x}, vaddr={:#x}",
dmaMap.size, dmaMap.iova, dmaMap.vaddr);
// mapping successful, advance IOVA allocator
this->iova_next += iovaIncrement;
// we intentionally don't return the actual mapped length, the users are
// only guaranteed to have their demanded memory mapped correctly
return dmaMap.iova;
}
bool
VfioContainer::memoryUnmap(uintptr_t phys, size_t length)
{
int ret;
if(not hasIommu) {
return true;
}
struct vfio_iommu_type1_dma_unmap dmaUnmap;
dmaUnmap.argsz = sizeof(struct vfio_iommu_type1_dma_unmap);
dmaUnmap.flags = 0;
dmaUnmap.iova = phys;
dmaUnmap.size = length;
ret = ioctl(this->fd, VFIO_IOMMU_UNMAP_DMA, &dmaUnmap);
if (ret) {
logger->error("Failed to unmap DMA mapping");
return false;
}
return true;
}
VfioGroup&
VfioContainer::getOrAttachGroup(int index)
{
// search if group with index already exists
for(auto& group : groups) {
if(group->index == index) {
return *group;
}
}
// group not yet part of this container, so acquire ownership
auto group = VfioGroup::attach(*this, index);
if(not group) {
logger->error("Failed to attach to IOMMU group: {}", index);
throw std::exception();
} else {
logger->debug("Attached new group {} to VFIO container", index);
}
// push to our list
groups.push_back(std::move(group));
return *groups.back();
}
VfioDevice::~VfioDevice()
{
logger->debug("Clean up device {} with fd {}", this->name, this->fd);
for(auto& region : regions) {
regionUnmap(region.index);
}
int ret = close(fd);
if (ret != 0) {
logger->error("Closing device fd {} failed", fd);
}
}
bool
VfioDevice::reset()
{
if (this->info.flags & VFIO_DEVICE_FLAGS_RESET)
return ioctl(this->fd, VFIO_DEVICE_RESET) == 0;
else
return false; /* not supported by this device */
}
void*
VfioDevice::regionMap(size_t index)
{
struct vfio_region_info *r = &regions[index];
if (!(r->flags & VFIO_REGION_INFO_FLAG_MMAP))
return MAP_FAILED;
mappings[index] = mmap(nullptr, r->size,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_32BIT,
fd, r->offset);
return mappings[index];
}
bool
VfioDevice::regionUnmap(size_t index)
{
int ret;
struct vfio_region_info *r = &regions[index];
if (!mappings[index])
return false; /* was not mapped */
logger->debug("Unmap region {} from device {}", index, name);
ret = munmap(mappings[index], r->size);
if (ret)
return false;
mappings[index] = nullptr;
return true;
}
size_t
VfioDevice::regionGetSize(size_t index)
{
if(index >= regions.size()) {
logger->error("Index out of range: {} >= {}", index, regions.size());
throw std::out_of_range("Index out of range");
}
return regions[index].size;
}
bool
VfioDevice::pciEnable()
{
int ret;
uint32_t reg;
const off_t offset = PCI_COMMAND +
(static_cast<off_t>(VFIO_PCI_CONFIG_REGION_INDEX) << 40);
/* Check if this is really a vfio-pci device */
if (!(this->info.flags & VFIO_DEVICE_FLAGS_PCI))
return false;
ret = pread(this->fd, &reg, sizeof(reg), offset);
if (ret != sizeof(reg))
return false;
/* Enable memory access and PCI bus mastering which is required for DMA */
reg |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
ret = pwrite(this->fd, &reg, sizeof(reg), offset);
if (ret != sizeof(reg))
return false;
return true;
}
bool
VfioDevice::pciHotReset()
{
/* Check if this is really a vfio-pci device */
if (not isVfioPciDevice())
return false;
const size_t reset_infolen = sizeof(struct vfio_pci_hot_reset_info) +
sizeof(struct vfio_pci_dependent_device) * 64;
auto reset_info = reinterpret_cast<struct vfio_pci_hot_reset_info*>
(calloc(1, reset_infolen));
reset_info->argsz = reset_infolen;
if (ioctl(this->fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info) != 0) {
free(reset_info);
return false;
}
logger->debug("Dependent devices for hot-reset:");
for (size_t i = 0; i < reset_info->count; i++) {
struct vfio_pci_dependent_device *dd = &reset_info->devices[i];
logger->debug(" {:04x}:{:02x}:{:02x}.{:01x}: iommu_group={}",
dd->segment, dd->bus,
PCI_SLOT(dd->devfn), PCI_FUNC(dd->devfn), dd->group_id);
if (static_cast<int>(dd->group_id) != this->group.index) {
free(reset_info);
return false;
}
}
free(reset_info);
const size_t resetlen = sizeof(struct vfio_pci_hot_reset) +
sizeof(int32_t) * 1;
auto reset = reinterpret_cast<struct vfio_pci_hot_reset*>
(calloc(1, resetlen));
reset->argsz = resetlen;
reset->count = 1;
reset->group_fds[0] = this->group.fd;
int ret = ioctl(this->fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
const bool success = (ret == 0);
free(reset);
if(not success and not group.container->isIommuEnabled()) {
logger->info("PCI hot reset failed, but this is expected without IOMMU");
return true;
}
return success;
}
int
VfioDevice::pciMsiInit(int efds[])
{
/* Check if this is really a vfio-pci device */
if(not isVfioPciDevice())
return -1;
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqSetSize = sizeof(struct vfio_irq_set) +
sizeof(int) * irqCount;
auto irqSet = reinterpret_cast<struct vfio_irq_set*>(calloc(1, irqSetSize));
if(irqSet == nullptr)
return -1;
irqSet->argsz = irqSetSize;
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
irqSet->start = 0;
irqSet->count = irqCount;
/* Now set the new eventfds */
for (size_t i = 0; i < irqCount; i++) {
efds[i] = eventfd(0, 0);
if (efds[i] < 0) {
free(irqSet);
return -1;
}
}
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
if(ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
free(irqSet);
return -1;
}
free(irqSet);
return irqCount;
}
int
VfioDevice::pciMsiDeinit(int efds[])
{
/* Check if this is really a vfio-pci device */
if(not isVfioPciDevice())
return -1;
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
const size_t irqSetSize = sizeof(struct vfio_irq_set) +
sizeof(int) * irqCount;
auto irqSet = reinterpret_cast<struct vfio_irq_set*>(calloc(1, irqSetSize));
if(irqSet == nullptr)
return -1;
irqSet->argsz = irqSetSize;
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
irqSet->count = irqCount;
irqSet->start = 0;
for (size_t i = 0; i < irqCount; i++) {
close(efds[i]);
efds[i] = -1;
}
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
free(irqSet);
return -1;
}
free(irqSet);
return irqCount;
}
bool
VfioDevice::pciMsiFind(int nos[])
{
int ret, idx, irq;
char *end, *col, *last, line[1024], name[13];
FILE *f;
f = fopen("/proc/interrupts", "r");
if (!f)
return false;
for (int i = 0; i < 32; i++)
nos[i] = -1;
/* For each line in /proc/interrupts */
while (fgets(line, sizeof(line), f)) {
col = strtok(line, " ");
/* IRQ number is in first column */
irq = strtol(col, &end, 10);
if (col == end)
continue;
/* Find last column of line */
do {
last = col;
} while ((col = strtok(nullptr, " ")));
ret = sscanf(last, "vfio-msi[%u](%12[0-9:])", &idx, name);
if (ret == 2) {
if (strstr(this->name.c_str(), name) == this->name.c_str())
nos[idx] = irq;
}
}
fclose(f);
return true;
}
bool
VfioDevice::isVfioPciDevice() const
{
return info.flags & VFIO_DEVICE_FLAGS_PCI;
}
VfioGroup::~VfioGroup()
{
logger->debug("Clean up group {} with fd {}", this->index, this->fd);
/* Release memory and close fds */
devices.clear();
if(fd < 0) {
logger->debug("Destructing group that has not been attached");
} else {
int ret = ioctl(fd, VFIO_GROUP_UNSET_CONTAINER);
if (ret != 0) {
logger->error("Cannot unset container for group fd {}", fd);
}
ret = close(fd);
if (ret != 0) {
logger->error("Cannot close group fd {}", fd);
}
}
}
std::unique_ptr<VfioGroup>
VfioGroup::attach(VfioContainer& container, int groupIndex)
{
std::unique_ptr<VfioGroup> group { new VfioGroup(groupIndex) };
group->container = &container;
/* Open group fd */
std::stringstream groupPath;
groupPath << VFIO_PATH
<< (container.isIommuEnabled() ? "" : "noiommu-")
<< groupIndex;
group->fd = open(groupPath.str().c_str(), O_RDWR);
if (group->fd < 0) {
logger->error("Failed to open VFIO group {}", group->index);
return nullptr;
}
logger->debug("VFIO group {} (fd {}) has path {}",
groupIndex, group->fd, groupPath.str());
/* Claim group ownership */
int ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container.getFd());
if (ret < 0) {
logger->error("Failed to attach VFIO group {} to container fd {} (error {})",
group->index, container.getFd(), ret);
return nullptr;
}
/* Set IOMMU type */
int iommu_type = container.isIommuEnabled() ?
VFIO_TYPE1_IOMMU :
VFIO_NOIOMMU_IOMMU;
ret = ioctl(container.getFd(), VFIO_SET_IOMMU, iommu_type);
if (ret < 0) {
logger->error("Failed to set IOMMU type of container: {}", ret);
return nullptr;
}
/* Check group viability and features */
group->status.argsz = sizeof(group->status);
ret = ioctl(group->fd, VFIO_GROUP_GET_STATUS, &group->status);
if (ret < 0) {
logger->error("Failed to get VFIO group status");
return nullptr;
}
if (!(group->status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
logger->error("VFIO group is not available: bind all devices to the VFIO driver!");
return nullptr;
}
return group;
}
} // namespace villas

View file

@ -1,203 +0,0 @@
/** A generic linked list
*
* Linked lists a used for several data structures in the code.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <villas/list.h>
#include <villas/utils.h>
/* Compare functions */
static int cmp_lookup(const void *a, const void *b) {
const struct {
char *name;
} *obj = a;
return strcmp(obj->name, b);
}
static int cmp_contains(const void *a, const void *b) {
return a == b ? 0 : 1;
}
static int cmp_sort(const void *a, const void *b, void *thunk) {
cmp_cb_t cmp = (cmp_cb_t) thunk;
return cmp(*(void **) a, *(void **) b);
}
int list_init(struct list *l)
{
assert(l->state == STATE_DESTROYED);
pthread_mutex_init(&l->lock, NULL);
l->length = 0;
l->capacity = 0;
l->array = NULL;
l->state = STATE_INITIALIZED;
return 0;
}
int list_destroy(struct list *l, dtor_cb_t destructor, bool release)
{
pthread_mutex_lock(&l->lock);
assert(l->state != STATE_DESTROYED);
for (size_t i = 0; i < list_length(l); i++) {
void *p = list_at(l, i);
if (destructor)
destructor(p);
if (release)
free(p);
}
free(l->array);
l->array = NULL;
l->length = -1;
l->capacity = 0;
pthread_mutex_unlock(&l->lock);
pthread_mutex_destroy(&l->lock);
l->state = STATE_DESTROYED;
return 0;
}
void list_push(struct list *l, void *p)
{
pthread_mutex_lock(&l->lock);
assert(l->state == STATE_INITIALIZED);
/* Resize array if out of capacity */
if (l->length >= l->capacity) {
l->capacity += LIST_CHUNKSIZE;
l->array = realloc(l->array, l->capacity * sizeof(void *));
}
l->array[l->length] = p;
l->length++;
pthread_mutex_unlock(&l->lock);
}
void list_remove(struct list *l, void *p)
{
int removed = 0;
pthread_mutex_lock(&l->lock);
assert(l->state == STATE_INITIALIZED);
for (size_t i = 0; i < list_length(l); i++) {
if (l->array[i] == p)
removed++;
else
l->array[i - removed] = l->array[i];
}
l->length -= removed;
pthread_mutex_unlock(&l->lock);
}
void * list_lookup(struct list *l, const char *name)
{
return list_search(l, cmp_lookup, (void *) name);
}
int list_contains(struct list *l, void *p)
{
return list_count(l, cmp_contains, p);
}
int list_count(struct list *l, cmp_cb_t cmp, void *ctx)
{
int c = 0;
pthread_mutex_lock(&l->lock);
assert(l->state == STATE_INITIALIZED);
for (size_t i = 0; i < list_length(l); i++) {
void *e = list_at(l, i);
if (cmp(e, ctx) == 0)
c++;
}
pthread_mutex_unlock(&l->lock);
return c;
}
void * list_search(struct list *l, cmp_cb_t cmp, void *ctx)
{
void *e;
pthread_mutex_lock(&l->lock);
assert(l->state == STATE_INITIALIZED);
for (size_t i = 0; i < list_length(l); i++) {
e = list_at(l, i);
if (cmp(e, ctx) == 0)
goto out;
}
e = NULL; /* not found */
out: pthread_mutex_unlock(&l->lock);
return e;
}
void list_sort(struct list *l, cmp_cb_t cmp)
{
pthread_mutex_lock(&l->lock);
assert(l->state == STATE_INITIALIZED);
qsort_r(l->array, l->length, sizeof(void *), cmp_sort, (void *) cmp);
pthread_mutex_unlock(&l->lock);
}
int list_set(struct list *l, int index, void *value)
{
if (index >= l->length)
return -1;
l->array[index] = value;
return 0;
}

View file

@ -1,310 +0,0 @@
/** Logging and debugging routines
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <villas/config.h>
#include <villas/log.h>
#include <villas/utils.h>
#ifdef ENABLE_OPAL_ASYNC
/* Define RTLAB before including OpalPrint.h for messages to be sent
* to the OpalDisplay. Otherwise stdout will be used. */
#define RTLAB
#include "OpalPrint.h"
#endif
struct log *global_log;
struct log default_log;
/* We register a default log instance */
__attribute__((constructor))
void register_default_log()
{
int ret;
static struct log default_log;
ret = log_init(&default_log, V, LOG_ALL);
if (ret)
error("Failed to initalize log");
ret = log_start(&default_log);
if (ret)
error("Failed to start log");
}
/** List of debug facilities as strings */
static const char *facilities_strs[] = {
"pool", /* LOG_POOL */
"queue", /* LOG_QUEUE */
"config", /* LOG_CONFIG */
"hook", /* LOG_HOOK */
"path", /* LOG_PATH */
"node", /* LOG_NODE */
"mem", /* LOG_MEM */
"web", /* LOG_WEB */
"api", /* LOG_API */
"log", /* LOG_LOG */
"vfio", /* LOG_VFIO */
"pci", /* LOG_PCI */
"xil", /* LOG_XIL */
"tc", /* LOG_TC */
"if", /* LOG_IF */
"advio", /* LOG_ADVIO */
/* Node-types */
"socket", /* LOG_SOCKET */
"file", /* LOG_FILE */
"fpga", /* LOG_FPGA */
"ngsi", /* LOG_NGSI */
"websocket", /* LOG_WEBSOCKET */
"opal", /* LOG_OPAL */
};
#ifdef __GNUC__
/** The current log indention level (per thread!). */
static __thread int indent = 0;
int log_indent(int levels)
{
int old = indent;
indent += levels;
return old;
}
int log_noindent()
{
int old = indent;
indent = 0;
return old;
}
void log_outdent(int *old)
{
indent = *old;
}
#endif
static void log_resize(int signal, siginfo_t *sinfo, void *ctx)
{
int ret;
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &global_log->window);
if (ret)
return;
global_log->width = global_log->window.ws_col - 25;
if (global_log->prefix)
global_log->width -= strlenp(global_log->prefix);
debug(LOG_LOG | 15, "New terminal size: %dx%x", global_log->window.ws_row, global_log->window.ws_col);
}
int log_init(struct log *l, int level, long facilitites)
{
int ret;
/* Register this log instance globally */
global_log = l;
l->level = level;
l->syslog = 0;
l->facilities = facilitites;
l->file = stderr;
l->path = NULL;
l->prefix = getenv("VILLAS_LOG_PREFIX");
/* Register signal handler which is called whenever the
* terminal size changes. */
if (l->file == stderr) {
struct sigaction sa_resize = {
.sa_flags = SA_SIGINFO,
.sa_sigaction = log_resize
};
sigemptyset(&sa_resize.sa_mask);
ret = sigaction(SIGWINCH, &sa_resize, NULL);
if (ret)
return ret;
/* Try to get initial window size */
ioctl(STDERR_FILENO, TIOCGWINSZ, &global_log->window);
}
else {
l->window.ws_col = LOG_WIDTH;
l->window.ws_row = LOG_HEIGHT;
}
l->width = l->window.ws_col - 25;
if (l->prefix)
l->width -= strlenp(l->prefix);
l->state = STATE_INITIALIZED;
return 0;
}
int log_start(struct log *l)
{
if (l->path) {
l->file = fopen(l->path, "a+");;
if (!l->file) {
l->file = stderr;
error("Failed to open log file '%s'", l->path);
}
}
else
l->file = stderr;
l->state = STATE_STARTED;
if (l->syslog) {
openlog(NULL, LOG_PID, LOG_DAEMON);
}
debug(LOG_LOG | 5, "Log sub-system started: level=%d, faciltities=%#lx, path=%s", l->level, l->facilities, l->path);
return 0;
}
int log_stop(struct log *l)
{
if (l->state != STATE_STARTED)
return 0;
if (l->file != stderr && l->file != stdout) {
fclose(l->file);
}
if (l->syslog) {
closelog();
}
l->state = STATE_STOPPED;
return 0;
}
int log_destroy(struct log *l)
{
default_log.epoch = l->epoch;
global_log = &default_log;
l->state = STATE_DESTROYED;
return 0;
}
int log_set_facility_expression(struct log *l, const char *expression)
{
bool negate;
char *copy, *token;
long mask = 0, facilities = 0;
copy = strdup(expression);
token = strtok(copy, ",");
while (token != NULL) {
if (token[0] == '!') {
token++;
negate = true;
}
else
negate = false;
/* Check for some classes */
if (!strcmp(token, "all"))
mask = LOG_ALL;
else if (!strcmp(token, "nodes"))
mask = LOG_NODES;
else if (!strcmp(token, "kernel"))
mask = LOG_KERNEL;
else {
for (int ind = 0; ind < ARRAY_LEN(facilities_strs); ind++) {
if (!strcmp(token, facilities_strs[ind])) {
mask = (1 << (ind+8));
goto found;
}
}
error("Invalid log class '%s'", token);
}
found: if (negate)
facilities &= ~mask;
else
facilities |= mask;
token = strtok(NULL, ",");
}
l->facilities = facilities;
free(copy);
return l->facilities;
}
void log_print(struct log *l, const char *lvl, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_vprint(l, lvl, fmt, ap);
va_end(ap);
}
void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap)
{
char *buf = alloc(512);
/* Optional prefix */
if (l->prefix)
strcatf(&buf, "%s", l->prefix);
/* Indention */
#ifdef __GNUC__
for (int i = 0; i < indent; i++)
strcatf(&buf, "%s ", BOX_UD);
strcatf(&buf, "%s ", BOX_UDR);
#endif
/* Format String */
vstrcatf(&buf, fmt, ap);
/* Output */
#ifdef ENABLE_OPAL_ASYNC
OpalPrint("VILLASfpga: %s\n", buf);
#endif
fprintf(l->file ? l->file : stderr, "%s\n", buf);
free(buf);
}

View file

@ -1,85 +0,0 @@
/** Logging routines that depend on jansson.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <villas/config.h>
#include <villas/log.h>
#include <villas/log_config.h>
#include <villas/utils.h>
int log_parse(struct log *l, json_t *cfg)
{
const char *facilities = NULL;
const char *path = NULL;
int ret;
json_error_t err;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i, s?: s, s?: s, s?: b }",
"level", &l->level,
"file", &path,
"facilities", &facilities,
"syslog", &l->syslog
);
if (ret)
jerror(&err, "Failed to parse logging configuration");
if (path)
l->path = strdup(path);
if (facilities)
log_set_facility_expression(l, facilities);
l->state = STATE_PARSED;
return 0;
}
void jerror(json_error_t *err, const char *fmt, ...)
{
va_list ap;
char *buf = NULL;
struct log *l = global_log ? global_log : &default_log;
va_start(ap, fmt);
vstrcatf(&buf, fmt, ap);
va_end(ap);
log_print(l, LOG_LVL_ERROR, "%s:", buf);
{ INDENT
log_print(l, LOG_LVL_ERROR, "%s in %s:%d:%d", err->text, err->source, err->line, err->column);
if (l->syslog)
syslog(LOG_ERR, "%s in %s:%d:%d", err->text, err->source, err->line, err->column);
}
free(buf);
killme(SIGABRT);
pause();
}

View file

@ -1,138 +0,0 @@
/** Logging and debugging routines
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <villas/utils.h>
#include <villas/log.h>
void debug(long class, const char *fmt, ...)
{
va_list ap;
struct log *l = global_log;
int lvl = class & 0xFF;
int fac = class & ~0xFF;
if (((fac == 0) || (fac & l->facilities)) && (lvl <= l->level)) {
va_start(ap, fmt);
log_vprint(l, LOG_LVL_DEBUG, fmt, ap);
if (l->syslog)
syslog(LOG_DEBUG, fmt, ap);
va_end(ap);
}
}
void info(const char *fmt, ...)
{
va_list ap;
struct log *l = global_log;
va_start(ap, fmt);
log_vprint(l, LOG_LVL_INFO, fmt, ap);
if (l->syslog)
syslog(LOG_INFO, fmt, ap);
va_end(ap);
}
void warn(const char *fmt, ...)
{
va_list ap;
struct log *l = global_log;
va_start(ap, fmt);
log_vprint(l, LOG_LVL_WARN, fmt, ap);
if (l->syslog)
syslog(LOG_WARNING, fmt, ap);
va_end(ap);
}
void stats(const char *fmt, ...)
{
va_list ap;
struct log *l = global_log;
va_start(ap, fmt);
log_vprint(l, LOG_LVL_STATS, fmt, ap);
if (l->syslog)
syslog(LOG_INFO, fmt, ap);
va_end(ap);
}
void error(const char *fmt, ...)
{
va_list ap;
struct log *l = global_log;
va_start(ap, fmt);
log_vprint(l, LOG_LVL_ERROR, fmt, ap);
if (l->syslog)
syslog(LOG_ERR, fmt, ap);
va_end(ap);
killme(SIGABRT);
pause();
}
void serror(const char *fmt, ...)
{
va_list ap;
char *buf = NULL;
struct log *l = global_log;
va_start(ap, fmt);
vstrcatf(&buf, fmt, ap);
va_end(ap);
log_print(l, LOG_LVL_ERROR, "%s: %m (%u)", buf, errno);
if (l->syslog)
syslog(LOG_ERR, "%s: %m (%u)", buf, errno);
free(buf);
killme(SIGABRT);
pause();
}

View file

@ -1,401 +0,0 @@
/** General purpose helper functions.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <pthread.h>
#include <fcntl.h>
#include <ctype.h>
#include <villas/config.h>
#include <villas/utils.h>
pthread_t main_thread;
void print_copyright()
{
printf("VILLASfpga %s (built on %s %s)\n",
CLR_BLU(BUILDID), CLR_MAG(__DATE__), CLR_MAG(__TIME__));
printf(" Copyright 2014-2017, Institute for Automation of Complex Power Systems, EONERC\n");
printf(" Steffen Vogel <StVogel@eonerc.rwth-aachen.de>\n");
}
void print_version()
{
printf("%s\n", BUILDID);
}
int version_parse(const char *s, struct version *v)
{
return sscanf(s, "%u.%u", &v->major, &v->minor) != 2;
}
int version_cmp(struct version *a, struct version *b) {
int major = a->major - b->major;
int minor = a->minor - b->minor;
return major ? major : minor;
}
double box_muller(float m, float s)
{
double x1, x2, y1;
static double y2;
static int use_last = 0;
if (use_last) { /* use value from previous call */
y1 = y2;
use_last = 0;
}
else {
double w;
do {
x1 = 2.0 * randf() - 1.0;
x2 = 2.0 * randf() - 1.0;
w = x1*x1 + x2*x2;
} while (w >= 1.0);
w = sqrt(-2.0 * log(w) / w);
y1 = x1 * w;
y2 = x2 * w;
use_last = 1;
}
return m + y1 * s;
}
double randf()
{
return (double) random() / RAND_MAX;
}
char * strcatf(char **dest, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vstrcatf(dest, fmt, ap);
va_end(ap);
return *dest;
}
char * vstrcatf(char **dest, const char *fmt, va_list ap)
{
char *tmp;
int n = *dest ? strlen(*dest) : 0;
int i = vasprintf(&tmp, fmt, ap);
*dest = (char *)(realloc(*dest, n + i + 1));
if (*dest != NULL)
strncpy(*dest+n, tmp, i + 1);
free(tmp);
return *dest;
}
#ifdef __linux__
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set)
{
*set = 0;
for (int i = 0; i < MIN(sizeof(*set) * 8, CPU_SETSIZE); i++) {
if (CPU_ISSET(i, cset))
*set |= 1ULL << i;
}
}
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset)
{
CPU_ZERO(cset);
for (int i = 0; i < MIN(sizeof(set) * 8, CPU_SETSIZE); i++) {
if (set & (1L << i))
CPU_SET(i, cset);
}
}
/* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c */
static const char *nexttoken(const char *q, int sep)
{
if (q)
q = strchr(q, sep);
if (q)
q++;
return q;
}
int cpulist_parse(const char *str, cpu_set_t *set, int fail)
{
const char *p, *q;
int r = 0;
q = str;
CPU_ZERO(set);
while (p = q, q = nexttoken(q, ','), p) {
unsigned int a; /* beginning of range */
unsigned int b; /* end of range */
unsigned int s; /* stride */
const char *c1, *c2;
char c;
if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
return 1;
b = a;
s = 1;
c1 = nexttoken(p, '-');
c2 = nexttoken(p, ',');
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
return 1;
c1 = nexttoken(c1, ':');
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
return 1;
if (s == 0)
return 1;
}
}
if (!(a <= b))
return 1;
while (a <= b) {
if (fail && (a >= CPU_SETSIZE))
return 2;
CPU_SET(a, set);
a += s;
}
}
if (r == 2)
return 1;
return 0;
}
char *cpulist_create(char *str, size_t len, cpu_set_t *set)
{
size_t i;
char *ptr = str;
int entry_made = 0;
for (i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, set)) {
int rlen;
size_t j, run = 0;
entry_made = 1;
for (j = i + 1; j < CPU_SETSIZE; j++) {
if (CPU_ISSET(j, set))
run++;
else
break;
}
if (!run)
rlen = snprintf(ptr, len, "%zd,", i);
else if (run == 1) {
rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
i++;
} else {
rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
i += run;
}
if (rlen < 0 || (size_t) rlen + 1 > len)
return NULL;
ptr += rlen;
if (rlen > 0 && len > (size_t) rlen)
len -= rlen;
else
len = 0;
}
}
ptr -= entry_made;
*ptr = '\0';
return str;
}
#endif /* __linux__ */
void * alloc(size_t bytes)
{
void *p = malloc(bytes);
if (!p)
error("Failed to allocate memory");
memset(p, 0, bytes);
return p;
}
void * memdup(const void *src, size_t bytes)
{
void *dst = alloc(bytes);
memcpy(dst, src, bytes);
return dst;
}
size_t read_random(char *buf, size_t len)
{
int fd;
ssize_t bytes, total;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return 0;
bytes = 0;
total = 0;
while (total < len) {
bytes = read(fd, buf + total, len - total);
if (bytes < 0)
break;
total += bytes;
}
close(fd);
return bytes;
}
void rdtsc_sleep(uint64_t nanosecs, uint64_t start)
{
uint64_t cycles;
/** @todo Replace the hard coded CPU clock frequency */
cycles = (double) nanosecs / (1e9 / 3392389000);
if (start == 0)
start = rdtsc();
do {
__asm__("nop");
} while (rdtsc() - start < cycles);
}
/* Setup exit handler */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx))
{
int ret;
info("Initialize signals");
struct sigaction sa_quit = {
.sa_flags = SA_SIGINFO | SA_NODEFER,
.sa_sigaction = cb
};
struct sigaction sa_chld = {
.sa_flags = 0,
.sa_handler = SIG_IGN
};
main_thread = pthread_self();
sigemptyset(&sa_quit.sa_mask);
ret = sigaction(SIGINT, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGTERM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGALRM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGCHLD, &sa_chld, NULL);
if (ret)
return ret;
return 0;
}
void killme(int sig)
{
/* Send only to main thread in case the ID was initilized by signals_init() */
if (main_thread)
pthread_kill(main_thread, sig);
else
kill(0, sig);
}
pid_t spawn(const char* name, char *const argv[])
{
pid_t pid;
pid = fork();
switch (pid) {
case -1: return -1;
case 0: return execvp(name, (char * const*) argv);
}
return pid;
}
size_t strlenp(const char *str)
{
size_t sz = 0;
for (const char *d = str; *d; d++) {
const unsigned char *c = (const unsigned char *) d;
if (isprint(*c))
sz++;
else if (c[0] == '\b')
sz--;
else if (c[0] == '\t')
sz += 4; /* tab width == 4 */
/* CSI sequence */
else if (c[0] == '\e' && c[1] == '[') {
c += 2;
while (*c && *c != 'm')
c++;
}
/* UTF-8 */
else if (c[0] >= 0xc2 && c[0] <= 0xdf) {
sz++;
c += 1;
}
else if (c[0] >= 0xe0 && c[0] <= 0xef) {
sz++;
c += 2;
}
else if (c[0] >= 0xf0 && c[0] <= 0xf4) {
sz++;
c += 3;
}
d = (const char *) c;
}
return sz;
}