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

Merge branch 'feature-tests' into 'develop'

Unit-tests, Coverage & Out-of-source Build-system

This merge request:

* [ ] implements unit tests for the basic data structures and helper utilities #38 
* [ ] improves the build system
* [ ] adds test coverage reports using glover
* [ ] do all of that automatically with the CI

I would be very happy if there's somebody to review that code. @umar.farooq?

See merge request !4
This commit is contained in:
Steffen Vogel 2016-10-16 06:11:58 +02:00
commit 7e5f656567
402 changed files with 1852 additions and 1126 deletions

View file

@ -1 +1,4 @@
*
!thirdparty/libxil/
!thirdparty/criterion/
!thirdparty/libwebsockets/

10
.gitignore vendored
View file

@ -1,10 +1,2 @@
*.o
*.d
*.so
/build/
*~
/node
/pipe
/test
/signal
/fpga

View file

@ -1,87 +1,120 @@
image: acs-public:5000/villas:latest
variables:
# This registry is a linked docker container running on the same host
# and configured in gitlab-ci-runner config.toml as a linked service
DOCKER_REGISTRY: acs-public:5000
DOCKER_IMAGE: villas:latest
PREFIX: /usr/
stages:
- prepare
- dependencies
- build
- test
- deploy
# Stage: prepare
##############################################################################
# Build docker image which is used to build & test VILLASnode
docker-image:
stage: prepare
image: stackbrew/docker:1.8.2
# Must match the docker version on the build machine!
before_script:
- git submodule update --init --recursive
- docker info
script:
- docker build -t $DOCKER_REGISTRY/$DOCKER_IMAGE .
- docker push $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- shell
- linux
libwebsockets:
stage: dependencies
script:
- make -C thirdparty libwebsockets-2.0.2
artifacts:
paths:
- thirdparty/libwebsockets-2.0.2/
libxil:
stage: dependencies
script:
- make -C thirdparty/xilinx
artifacts:
paths:
- thirdparty/xilinx/libxil.so
# Stage: build
##############################################################################
build:
stage: build
variables:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig/
before_script:
- make -C thirdparty/libwebsockets-2.0.2/build install
- make -C thirdparty/xilinx install
script:
- make
- make install
artifacts:
name: "${CI_PROJECT_NAME}-${CI_BUILD_REF}"
paths:
- libvillas.so
- fpga
- node
- pipe
- signal
- test
- build/release/
image: $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- docker
docs:
stage: build
artifacts:
name: "${CI_PROJECT_NAME}-doc-${CI_BUILD_REF}"
paths:
- doc/html/
- build/release/doc/
script:
- make doc
image: $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- docker
.ssh: &ssh
before_script:
- mkdir -p $HOME/.ssh
- echo -e "$SSH_SECRET_KEY" > $HOME/.ssh/id_rsa
- chmod 600 $HOME/.ssh/id_rsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > $HOME/.ssh/config
- ssh-keyscan -H $DEPLOY_HOST >> $HOME/.ssh/known_hosts
# Stage: test
##############################################################################
coverage:
stage: test
script:
- make coverage COVERAGE=1
artifacts:
name: "${CI_PROJECT_NAME}-coverage-${CI_BUILD_REF}"
paths:
- build/release-coverage/coverage/
- build/release-coverage/coverage.txt
- build/release-coverage/coverage.xml
image: $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- docker
unit:
stage: test
dependencies:
- build
script:
- make tests
- build/release/testsuite
image: $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- docker
integration:
stage: test
dependencies:
- build
script:
- "build/release/villas-node || true"
image: $DOCKER_REGISTRY/$DOCKER_IMAGE
tags:
- docker
# Stage: deliver
##############################################################################
website:
stage: deploy
script:
- rsync web/ $DEPLOY_PATH/
only:
- develop
tags:
- villas-deploy
deliver:
stage: deploy
<<: *ssh
script:
- '[ "$CI_BUILD_REF_NAME" == "develop" ] && scp -pr web/landing.html $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/index.html'
- scp -pr doc/html/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/doc/$CI_BUILD_REF_NAME/
only:
- develop
- rsync -r build/release/doc/html/ $DEPLOY_PATH/doc/$CI_BUILD_REF_NAME/
- rsync -r build/release-coverage/coverage/ $DEPLOY_PATH/coverage/$CI_BUILD_REF_NAME/
dependencies:
- docs
- coverage
tags:
- villas-deploy

21
.gitmodules vendored
View file

@ -0,0 +1,21 @@
[submodule "web/socket/flot"]
path = web/socket/flot
url = https://github.com/flot/flot.git
[submodule "thirdparty/libconfig"]
path = thirdparty/libconfig
url = https://github.com/hyperrealm/libconfig.git
[submodule "thirdparty/libwebsockets"]
path = thirdparty/libwebsockets
url = https://github.com/warmcat/libwebsockets
[submodule "thirdparty/criterion"]
path = thirdparty/criterion
url = https://github.com/stv0g/Criterion
[submodule "thirdparty/libnl"]
path = thirdparty/libnl
url = https://github.com/thom311/libnl.git
[submodule "thirdparty/jansson"]
path = thirdparty/jansson
url = https://github.com/akheron/jansson.git
[submodule "thirdparty/libcurl"]
path = thirdparty/libcurl
url = https://github.com/curl/curl.git

View file

@ -46,4 +46,44 @@ RUN dnf -y update && \
rpmdevtools \
rpm-build
ENTRYPOINT /bin/bash
# Tools to build dependencies
RUN dnf -y update && \
dnf -y install \
git \
gcc-c++ \
autoconf \
automake \
autogen \
libtool \
flex \
bison \
texinfo
# Tools for coverage and profiling
RUN dnf -y update && \
dnf -y install \
python-pip && \
pip install \
gcovr
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV LD_LIBRARY_PATH /usr/local/lib
# Build & Install libxil
COPY thirdparty/libxil /tmp/libxil
RUN mkdir -p /tmp/libxil/build && cd /tmp/libxil/build && cmake .. && make install
# Build & Install Criterion
COPY thirdparty/criterion /tmp/criterion
RUN mkdir -p /tmp/criterion/build && cd /tmp/criterion/build && cmake .. && make install
# Build & Install libwebsockets
COPY thirdparty/libwebsockets /tmp/libwebsockets
RUN mkdir -p /tmp/libwebsockets/build && cd /tmp/libwebsockets/build && cmake .. && make install
# Cleanup intermediate files from builds
RUN rm -rf /tmp
WORKDIR /villas
ENTRYPOINT make clean && make install && villas node; bash

View file

@ -58,7 +58,7 @@ PROJECT_LOGO = doc/pictures/acs_eonerc_logo.svg
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = doc/
OUTPUT_DIRECTORY = build/doc/
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and

243
Makefile
View file

@ -1,40 +1,39 @@
# Executables
TARGETS = node pipe signal test
## Main project Makefile
#
# The build system of this project is based on GNU Make and pkg-config
#
# To retain maintainability, the project is divided into multiple modules.
# Each module has its own Makefile which gets included.
#
# Please read "Recursive Make Considered Harmful" from Peter Miller
# to understand the motivation for this structure.
#
# [1] http://aegis.sourceforge.net/auug97.pdf
#
# @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
# @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
# This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
# Unauthorized copying of this file, via any medium is strictly prohibited.
#################################################################################
# Libraries
LIBS = libvillas.so
# Plugins
PLUGINS = simple_circuit.so example_hook.so
# Object files for libvillas
LIB_SRCS = $(wildcard lib/hooks/*.c) \
$(addprefix lib/kernel/, kernel.c rt.c) \
$(addprefix lib/, \
sample.c path.c node.c hooks.c \
log.c utils.c cfg.c hist.c timing.c \
pool.c list.c queue.c lstack.c \
) \
# Project modules
MODULES = lib plugins src tests thirdparty tools
# Default prefix for install target
PREFIX ?= /usr/local
# Default debug level
# Default out-of-source build path
BUILDDIR ?= build
# Default debug level for executables
V ?= 2
# Compiler and linker flags
LDLIBS = -pthread -lm -lvillas
PLUGIN_CFLAGS = -fPIC -DVILLAS -I../include/villas
LIB_CFLAGS = -fPIC
LIB_LDFLAGS = -shared
LIB_LDLIBS = -ldl -lrt
# Common flags
LDLIBS =
CFLAGS += -std=c11 -Iinclude -Iinclude/villas -I. -MMD -mcx16
CFLAGS += -Wall -Werror -fdiagnostics-color=auto
CFLAGS += -D_POSIX_C_SOURCE=200809L -D_GNU_SOURCE=1 -DV=$(V)
LDFLAGS += -pthread -L. -Wl,-rpath,'$$ORIGIN'
LDFLAGS += -L$(BUILDDIR)
ifdef CI
CFLAGS += -D_GIT_REV='"${CI_BUILD_REF:0:7}~ci"'
@ -42,148 +41,76 @@ else ifdef GIT
CFLAGS += -D_GIT_REV='"$(shell git rev-parse --short HEAD)"'
endif
# We must compile without optimizations for gcov!
ifdef DEBUG
CFLAGS += -O0 -g
VARIANTS += debug
else
CFLAGS += -O3
VARIANTS += release
endif
ifdef PROFILE
CFLAGS += -pg
LDFLAGS += -pg
VARIANTS += profile
endif
ifdef COVERAGE
CFLAGS += -fprofile-arcs -ftest-coverage
LDFLAGS += --coverage
LDLIBS += -lgcov
LIB_LDFLAGS += -coverage
LIB_LDLIBS += -gcov
VARIANTS += coverage
endif
SPACE :=
SPACE +=
BUILDDIR := $(BUILDDIR)/$(subst $(SPACE),-,$(strip $(VARIANTS)))
SRCDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# pkg-config dependencies
PKGS = libconfig
DOCKEROPTS = -p 80:80 -p 443:443 -p 1234:1234 --privileged --cap-add sys_nic --ulimit memlock=1073741824 --security-opt seccomp:unconfined
# Add more compiler flags
ifdef DEBUG
CFLAGS += -O0 -g
else
CFLAGS += -O3
endif
######## Node types ########
# file node-type is always supported
LIB_SRCS += $(addprefix lib/nodes/, file.c cbuilder.c)
# Enable Socket node type when libnl3 is available
ifeq ($(shell pkg-config libnl-route-3.0; echo $$?),0)
LIB_SRCS += $(addprefix lib/nodes/, socket.c)
LIB_SRCS += $(addprefix lib/kernel/, nl.c tc.c if.c)
LIB_SRCS += $(addprefix lib/, msg.c)
PKGS += libnl-route-3.0
endif
# Enable VILLASfpga support when libpci is available
ifeq ($(shell pkg-config libpci; echo $$?),0)
LIB_SRCS += $(addprefix lib/nodes/, fpga.c)
LIB_SRCS += $(addprefix lib/kernel/, pci.c vfio.c)
LIB_SRCS += $(wildcard lib/fpga/*.c)
LDLIBS += -lxil
LDFLAGS += -Lthirdparty/xilinx -Wl,-rpath,'$$ORIGIN/thirdparty/xilinx'
CFLAGS += -Ithirdparty/xilinx/include
PKGS += libpci
TARGETS += fpga
endif
# Enable NGSI support
ifeq ($(shell pkg-config libcurl jansson uuid; echo $$?),0)
LIB_SRCS += lib/nodes/ngsi.c
PKGS += libcurl jansson uuid
endif
# Enable WebSocket support
ifeq ($(shell pkg-config libwebsockets jansson; echo $$?),0)
LIB_SRCS += lib/nodes/websocket.c
PKGS += libwebsockets jansson
endif
## Add support for LAPACK / BLAS benchmarks / solvers
ifeq ($(shell pkg-config blas lapack; echo $$?),0)
PKGS += blas lapack
BENCH_OBJS += fpga-bench-overruns.o
endif
# Enable OPAL-RT Asynchronous Process support (will result in 32bit binary!!!)
ifdef WITH_OPAL
ifneq (,$(wildcard thirdparty/opal/include/AsyncApi.h))
LIB_OBJS += opal.o
CFLAGS += -m32
LDFLAGS += -m32
LIB_CFLAGS += -m32 -I thirdparty/opal/include
LIB_LDFLAGS += -m32 -L/lib/i386-linux-gnu/ -L/usr/lib/i386-linux-gnu/ -Lthirdparty/opal/lib/redhawk/
LIB_LDLIBS += -lOpalAsyncApiCore -lOpalCore -lOpalUtils -lirc
endif
endif
# Add flags by pkg-config
LIB_CFLAGS += $(addprefix -DWITH_, $(shell echo ${PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))
LIB_CFLAGS += $(shell pkg-config --cflags ${PKGS})
LIB_LDLIBS += $(shell pkg-config --libs ${PKGS})
LIB_OBJS = $(patsubst lib/%.c, obj/lib/%.o, $(LIB_SRCS))
######## Targets ########
.PHONY: all clean install docker doc
.SECONDARY:
.SECONDEXPANSION:
# Add flags by pkg-config
CFLAGS += $(addprefix -DWITH_, $(shell echo ${PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))
CFLAGS += $(shell pkg-config --cflags ${PKGS})
LDLIBS += $(shell pkg-config --libs ${PKGS})
# Default target: build everything
all: $(LIBS) $(TARGETS) $(PLUGINS)
all: src plugins | lib
# Dependencies for individual binaries
fpga: $(addprefix obj/src/,fpga.o fpga-tests.o fpga-bench.o $(BENCH_OBJS))
node: $(addprefix obj/src/,node.o)
pipe: $(addprefix obj/src/,pipe.o)
test: $(addprefix obj/src/,test.o)
signal: $(addprefix obj/src/,signal.o)
everything:
$(MAKE) DEBUG=1
$(MAKE) COVERAGE=1
$(MAKE) PROFILE=1
$(MAKE) doc
$(MAKE) tests
# Dependencies for plugins
example_hook.so: obj/plugins/hooks/example_hook.o
simple_circuit.so: obj/plugins/models/simple_circuit.o
install: $(addprefix install-,$(MODULES))
libvillas.so: $(LIB_OBJS)
# Create directories
%/:
mkdir -p $@
# Compile executable objects
obj/src/%.o: src/%.c | $$(dir $$@)
$(CC) $(CFLAGS) -c $< -o $@
# Compile library objects
obj/lib/%.o: lib/%.c | $$(dir $$@)
$(CC) $(CFLAGS) $(LIB_CFLAGS) -c $< -o $@
obj/plugins/%.o: plugins/%.c | $$(dir $$@)
$(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
# Link target executables
$(TARGETS):
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
# Link Libraries & Plugins
$(LIBS) $(PLUGINS):
$(CC) $(LIB_LDFLAGS) -o $@ $^ $(LIB_LDLIBS)
# Common targets
install: $(TARGETS) $(LIBS)
install -m 0644 $(LIBS) $(PREFIX)/lib
install -m 0755 node -T $(PREFIX)/bin/villas-node
install -m 0755 fpga -T $(PREFIX)/bin/villas-fpga
install -m 0755 signal -T $(PREFIX)/bin/villas-signal
install -m 0755 pipe -T $(PREFIX)/bin/villas-pipe
install -m 0755 test -T $(PREFIX)/bin/villas-test
install -m 0755 -d $(PREFIX)/include/villas/
install -m 0644 include/villas/*.h $(PREFIX)/include/villas/
install -m 0755 tools/villas.sh $(PREFIX)/bin/villas
ldconfig
clean:
$(RM) $(LIBS) $(PLUGINS) $(TARGETS)
$(RM) -rf obj/ doc/{html,latex}
clean: $(addprefix clean-,$(MODULES))
docker:
docker build -t villas .
docker run -it $(DOCKEROPTS) -v $(PWD):/villas villas
docker run -it -p 80:80 -p 443:443 -p 1234:1234 --privileged --cap-add sys_nic --ulimit memlock=1073741824 --security-opt seccomp:unconfined -v $(PWD):/villas villas
doc:
doxygen
doc: | $(BUILDDIR)/doc/
( cat Doxyfile ; echo "OUTPUT_DIRECTORY=$(BUILDDIR)/doc/" ) | doxygen -
# Include auto-generated dependencies
-include $(wildcard obj/**/*.d)
# Create non-existent directories
%/:
mkdir -p $@
.PHONY: all everything clean install docker doc
.PRECIOUS: %/
.SECONDEXPANSION:
-include $(wildcard $(BUILDDIR)/**/*.d)
-include $(addsuffix /Makefile.inc,$(MODULES))

View file

@ -107,8 +107,6 @@ struct hook {
struct sample *last;
struct sample *prev;
qptr_t head;
hook_cb_t cb; /**< The hook callback function as a function pointer. */
};

View file

@ -10,16 +10,58 @@
#ifndef _PCI_H_
#define _PCI_H_
#include <pci/pci.h>
#include "list.h"
struct pci_access * pci_get_handle();
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
#define PCI_FUNC(devfn) ((devfn) & 0x07)
void pci_release_handle();
struct pci_dev {
struct {
int vendor;
int device;
int class;
} id;
struct {
int domain;
int bus;
int device;
int function;
} slot; /**< Bus, Device, Function (BDF) */
};
struct pci_dev * pci_find_device(struct pci_access *pacc, struct pci_filter *f);
struct pci {
struct list devices; /**> List of available PCI devices in the system (struct pci_dev) */
};
/** 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. */
void pci_destroy(struct pci *p);
int pci_dev_init(struct pci_dev *d);
void pci_dev_destroy(struct pci_dev *d);
int pci_dev_parse_slot(struct pci_dev *f, const char *str, const char **error);
int pci_dev_parse_id(struct pci_dev *f, const char *str, const char **error);
int pci_dev_compare(const struct pci_dev *d, const struct pci_dev *f);
struct pci_dev * pci_lookup_device(struct pci *p, struct pci_dev *filter);
/** Bind a new LKM to the PCI device */
int pci_attach_driver(struct pci_dev *d, const char *driver);
int pci_get_iommu_group(struct pci_dev *pdev);
/** Return the IOMMU group of this PCI device or -1 if the device is not in a group. */
int pci_get_iommu_group(struct pci_dev *d);
#endif /* _PCI_H_ */

View file

@ -11,7 +11,6 @@
#define _VFIO_H_
#include <stdbool.h>
#include <pci/pci.h>
#include <sys/mman.h>
#include <linux/vfio.h>
@ -23,6 +22,7 @@
/* Forward declaration */
struct dma_mem;
struct pci_dev;
struct vfio_group {
int fd; /**< VFIO group file descriptor */

View file

@ -1,83 +0,0 @@
/** Lock-less LIFO stack
*
* Based on a single-linked list and double word compare exchange (DWCAS)
* to solve the ABA problem.
*
* Based on https://github.com/skeeto/lstack
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#ifndef _LSTACK_H_
#define _LSTACK_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdatomic.h>
struct lstack_node {
void *value;
struct lstack_node *next;
};
/** DWCAS cmpexch16 */
struct lstack_head {
uintptr_t aba;
struct lstack_node *node;
};
struct lstack {
struct lstack_node *node_buffer;
_Atomic struct lstack_head head; /**> List of stack elements */
_Atomic struct lstack_head free; /**> List of unused elements */
_Atomic size_t size, avail;
};
/** Initialize a lock-less stack which can hold up to maxsz values.
*
* Note: this function is not thread-safe.
*/
int lstack_init(struct lstack *lstack, size_t maxsz);
/** Pop cnt values from the stack an place them in the array values */
ssize_t lstack_pop_many(struct lstack *lstack, void *values[], size_t cnt);
/** Push cnt values which are giving by the array values to the stack. */
ssize_t lstack_push_many(struct lstack *lstack, void *values[], size_t cnt);
/** Push a single value to the stack. */
static inline __attribute__((unused)) int lstack_push(struct lstack *lstack, void *value)
{
return lstack_push_many(lstack, &value, 1) == 1 ? 0 : -1;
}
/** Pop a single element from the stack and return it. */
static inline __attribute__((unused)) void * lstack_pop(struct lstack *lstack)
{
void *value;
lstack_pop_many(lstack, &value, 1);
return value;
}
/** Return the approximate size of the stack. */
static inline __attribute__((unused)) size_t lstack_size(struct lstack *lstack)
{
return atomic_load(&lstack->size);
}
/** Release memory used by the stack.
*
* Note: this function is not thread-safe.
*/
static inline __attribute__((unused)) void lstack_destroy(struct lstack *lstack)
{
free(lstack->node_buffer);
}
#endif /* _LSTACK_H_ */

53
include/villas/memory.h Normal file
View file

@ -0,0 +1,53 @@
/** Memory allocators.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*/
#include <stddef.h>
#ifndef _MEMORY_H_
#define _MEMORY_H_
typedef void *(*memzone_allocator_t)(size_t len);
typedef int (*memzone_deallocator_t)(void *ptr, size_t len);
enum memtype_flags {
MEMORY_MMAP = (1 << 0),
MEMORY_DMA = (1 << 1),
MEMORY_HUGEPAGE = (1 << 2),
MEMORY_HEAP = (1 << 3)
};
struct memtype {
const char *name;
int flags;
size_t alignment;
memzone_allocator_t alloc;
memzone_deallocator_t free;
};
/** @todo Unused for now */
struct memzone {
struct memtype * const type;
void *addr;
uintptr_t physaddr;
size_t len;
};
void * memory_alloc(const struct memtype *m, size_t len);
void * memory_alloc_aligned(const struct memtype *m, size_t len, size_t alignment);
int memory_free(const struct memtype *m, void *ptr, size_t len);
extern const struct memtype memtype_heap;
extern const struct memtype memtype_hugepage;
#endif /* _MEMORY_H_ */

View file

@ -48,9 +48,6 @@ struct node
int vectorize; /**< Number of messages to send / recv at once (scatter / gather) */
int affinity; /**< CPU Affinity of this node */
qptr_t sent; /**< Number of samples sent / written to this node. */
qptr_t received; /**< Number of samples received / read from this node. */
enum node_state {
NODE_INVALID, /**< This node object is not in a valid state. */
NODE_CREATED, /**< This node has been parsed from the configuration. */

View file

@ -18,8 +18,7 @@
#define _FPGA_H_
#include "kernel/vfio.h"
#include <pci/pci.h>
#include "kernel/pci.h"
#include "fpga/dma.h"
#include "fpga/ip.h"
@ -29,7 +28,7 @@
#include "list.h"
struct fpga {
struct pci_filter filter; /**< Filter for libpci with device id & slot */
struct pci_dev filter; /**< Filter for PCI device. */
struct vfio_dev vd; /**< VFIO device handle. */
int do_reset; /**< Reset VILLASfpga during startup? */

View file

@ -42,7 +42,7 @@ struct path
struct node *in; /**< Pointer to the incoming node */
struct queue queue; /**< A ring buffer for all received messages (unmodified) */
struct mpmc_queue queue; /**< A ring buffer for all received messages (unmodified) */
struct pool pool; /**< Memory pool for messages / samples. */
struct list destinations; /**< List of all outgoing nodes */

View file

@ -12,51 +12,56 @@
#ifndef _POOL_H_
#define _POOL_H_
#include "lstack.h"
#include <sys/types.h>
struct pool_block;
#include "queue.h"
struct memtype;
/** A thread-safe memory pool */
struct pool {
size_t blocksz;
size_t alignment;
void *buffer; /**< Address of the underlying memory area */
const struct memtype *mem;
struct lstack stack;
size_t len; /**< Length of the underlying memory area */
size_t blocksz; /**< Length of a block in bytes */
size_t alignment; /**< Alignment of a block in bytes */
struct mpmc_queue queue; /**< The queue which is used to keep track of free blocks */
};
/** Initiazlize a pool */
int pool_init(struct pool *p, size_t blocksz, size_t alignment, void *buf, size_t len);
#define INLINE static inline __attribute__((unused))
/** Allocate hugepages for the pool and initialize it */
int pool_init_mmap(struct pool *p, size_t blocksz, size_t cnt);
/** Initiazlize a pool */
int pool_init(struct pool *p, size_t blocksz, size_t alignment, const struct memtype *mem);
/** Destroy and release memory used by pool. */
static inline __attribute__((unused)) void pool_destroy(struct pool *p)
{
lstack_destroy(&p->stack);
}
int pool_destroy(struct pool *p);
/** Pop cnt values from the stack an place them in the array blocks */
static inline __attribute__((unused)) ssize_t pool_get_many(struct pool *p, void *blocks[], size_t cnt)
INLINE ssize_t pool_get_many(struct pool *p, void *blocks[], size_t cnt)
{
return lstack_pop_many(&p->stack, blocks, cnt);
return mpmc_queue_pull_many(&p->queue, blocks, cnt);
}
/** Push cnt values which are giving by the array values to the stack. */
static inline __attribute__((unused)) ssize_t pool_put_many(struct pool *p, void *blocks[], size_t cnt)
INLINE ssize_t pool_put_many(struct pool *p, void *blocks[], size_t cnt)
{
return lstack_push_many(&p->stack, blocks, cnt);
return mpmc_queue_push_many(&p->queue, blocks, cnt);
}
/** Get a free memory block from pool. */
static inline __attribute__((unused)) void * pool_get(struct pool *p)
INLINE void * pool_get(struct pool *p)
{
return lstack_pop(&p->stack);
void *ptr;
return mpmc_queue_pull(&p->queue, &ptr) == 1 ? ptr : NULL;
}
/** Release a memory block back to the pool. */
static inline __attribute__((unused)) int pool_put(struct pool *p, void *buf)
INLINE int pool_put(struct pool *p, void *buf)
{
return lstack_push(&p->stack, buf);
return mpmc_queue_push(&p->queue, buf);
}
#endif /* _POOL_H_ */

View file

@ -1,93 +1,87 @@
/** Lock-free Single-Producer Single-consumer (SPSC) queue.
/** Lock-free Multiple-Producer Multiple-consumer (MPMC) queue.
*
* This datastructure queues void pointers in a FIFO style.
* Every queue element has an associated reference count which is
* used to tell wheater the queue is full or empty.
* Based on Dmitry Vyukov#s Bounded MPMC queue:
* http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
*
* Queue head and tail pointers are not part of this datastructure.
* In combination with the reference counting, this enables multiple
* readers each with its own head pointer.
* Each reader will read the same data.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
* @author Steffen Vogel <post@steffenvogel.de>
* @copyright 2016 Steffen Vogel
* @license BSD 2-Clause License
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modiffication, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _QUEUE_H_
#define _QUEUE_H_
#ifndef _MPMC_QUEUE_H_
#define _MPMC_QUEUE_H_
#include <stddef.h>
#include <stdint.h>
#include <stdatomic.h>
typedef uintmax_t qptr_t;
#include "memory.h"
struct queue {
size_t size; /**< Number of pointers in queue::array */
_Atomic int *refcnts; /**< Reference counts to blocks in queue::array */
_Atomic int readers; /**< Initial reference count for pushed blocks */
#define CACHELINE_SIZE 64
typedef char cacheline_pad_t[CACHELINE_SIZE];
void **pointers; /**< Pointers to queue data */
struct mpmc_queue {
cacheline_pad_t _pad0; /**< Shared area: all threads read */
struct memtype const * mem;
size_t buffer_mask;
struct mpmc_queue_cell {
atomic_size_t sequence;
void *data;
} *buffer;
cacheline_pad_t _pad1; /**> Producer area: only producers read & write */
atomic_size_t tail; /**> Queue tail pointer */
cacheline_pad_t _pad2; /**> Consumer area: only consumers read & write */
atomic_size_t head; /**> Queue head pointer */
cacheline_pad_t _pad3; /**> @todo Why needed? */
};
/** Initiliaze a new queue and allocate memory. */
int queue_init(struct queue *q, size_t size);
/** Initialize MPMC queue */
int mpmc_queue_init(struct mpmc_queue *q, size_t size, const struct memtype *mem);
/** Release memory of queue. */
void queue_destroy(struct queue *q);
/** Desroy MPMC queue and release memory */
int mpmc_queue_destroy(struct mpmc_queue *q);
/** Increment the number of readers for this queue.
/** Return estimation of current queue usage.
*
* Important: To garuantee thread-safety this function must by called by
* the (only) writer which holds \p tail.
*/
void queue_reader_add(struct queue *q, qptr_t head, qptr_t tail);
* Note: This is only an estimation and not accurate as long other
* threads are performing operations.
*/
size_t mpmc_queue_available(struct mpmc_queue *q);
/** Decrement the number of readers for this queue.
*
* Important: To garuantee thread-safety this function must by called by
* the (only) writer which holds \p tail.
*/
void queue_reader_remove(struct queue *q, qptr_t head, qptr_t tail);
int mpmc_queue_push(struct mpmc_queue *q, void *ptr);
/** Enqueue up to \p cnt elements from \p ptrs[] at the queue tail pointed by \p tail.
*
* It may happen that the queue is (nearly) full and there is no more
* space to enqueue more elments.
* In this case a call to this function will return a value which is smaller than \p cnt
* or even zero if the queue was already full.
*
* @param q A pointer to the queue datastructure.
* @param[in] ptrs An array of void-pointers which should be enqueued.
* @param cnt The length of the pointer array \p ptrs.
* @param[in,out] tail A pointer to the current tail of the queue. The tail will be updated to new tail after eqeuing.
* @return The function returns the number of successfully enqueued elements from \p ptrs.
*/
int queue_push_many(struct queue *q, void *ptrs[], size_t cnt, qptr_t *tail);
int mpmc_queue_pull(struct mpmc_queue *q, void **ptr);
/** Dequeue up to \p cnt elements from the queue and place them into the array \p ptrs[].
*
* @param q A pointer to the queue datastructure.
* @param[out] ptrs An array with space at least \cnt elements which will receive pointers to the released elements.
* @param cnt The maximum number of elements which should be dequeued. It defines the size of \p ptrs.
* @param[in,out] head A pointer to a queue head. The value will be updated to reflect the new head.
* @return The number of elements which have been dequeued <b>and whose reference counts have reached zero</b>.
*/
int queue_pull_many(struct queue *q, void *ptrs[], size_t cnt, qptr_t *head);
int mpmc_queue_push_many(struct mpmc_queue *q, void *ptr[], size_t cnt);
/** Fill \p ptrs with \p cnt elements of the queue starting at entry \p pos. */
int queue_get_many(struct queue *q, void *ptrs[], size_t cnt, qptr_t pos);
int mpmc_queue_pull_many(struct mpmc_queue *q, void *ptr[], size_t cnt);
int queue_get(struct queue *q, void **ptr, qptr_t pos);
/** Enqueue a new block at the tail of the queue which is given by the qptr_t. */
int queue_push(struct queue *q, void *ptr, qptr_t *tail);
/** Dequeue the first block at the head of the queue. */
int queue_pull(struct queue *q, void **ptr, qptr_t *head);
#endif /* _QUEUE_H_ */
#endif /* _MPMC_QUEUE_H_ */

View file

@ -13,6 +13,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdatomic.h>
#include <time.h>
#include <sys/types.h>
@ -41,6 +42,9 @@ struct sample {
int sequence; /**< The sequence number of this sample. */
int length; /**< The number of values in sample::values which are valid. */
int capacity; /**< The number of values in sample::values for which memory is reserved. */
atomic_int refcnt; /**< Reference counter. */
struct pool *pool; /**< This sample is belong to this memory pool. */
/** All timestamps are seconds / nano seconds after 1.1.1970 UTC */
struct {
@ -59,6 +63,12 @@ struct sample {
/** Request \p cnt samples from memory pool \p p and initialize them. */
int sample_get_many(struct pool *p, struct sample *smps[], int cnt);
/** Increase reference count of sample */
int sample_get(struct sample *s);
/** Decrease reference count and release memory if last reference was held. */
int sample_put(struct sample *s);
/** Print a sample in human readable form to a file stream.
*
* @param buf A character buffer of len bytes.

98
lib/Makefile.inc Normal file
View file

@ -0,0 +1,98 @@
# Libraries
LIBS = $(BUILDDIR)/libvillas.so
# Object files for libvillas
LIB_SRCS = $(addprefix lib/nodes/, file.c cbuilder.c) \
$(addprefix lib/kernel/, kernel.c rt.c) \
$(addprefix lib/, sample.c path.c node.c hooks.c \
log.c utils.c cfg.c hist.c timing.c pool.c list.c \
queue.c memory.c \
) \
$(wildcard lib/hooks/*.c) \
LIB_CFLAGS = $(CFLAGS) -fPIC
LIB_LDFLAGS = -shared
LIB_LDLIBS = $(LDLIBS) -ldl -lrt
######## Node types ########
# Enable Socket node type when libnl3 is available
ifndef WITHOUT_SOCKET
ifeq ($(shell pkg-config libnl-route-3.0; echo $$?),0)
LIB_SRCS += $(addprefix lib/nodes/, socket.c)
LIB_SRCS += $(addprefix lib/kernel/, nl.c tc.c if.c)
LIB_SRCS += $(addprefix lib/, msg.c)
LIB_PKGS += libnl-route-3.0
endif
endif
# Enable VILLASfpga support when libxil is available
ifndef WITHOUT_FPGA
ifeq ($(shell pkg-config libxil; echo $$?),0)
LIB_SRCS += $(addprefix lib/nodes/, fpga.c)
LIB_SRCS += $(addprefix lib/kernel/, pci.c vfio.c)
LIB_SRCS += $(wildcard lib/fpga/*.c)
PKGS += libxil
endif
endif
# Enable NGSI support
ifndef WITHOUT_NGSI
ifeq ($(shell pkg-config libcurl jansson; echo $$?),0)
LIB_SRCS += lib/nodes/ngsi.c
LIB_PKGS += libcurl jansson
endif
endif
# Enable WebSocket support
ifndef WITHOUT_WEBSOCKETS
ifeq ($(shell pkg-config libwebsockets jansson; echo $$?),0)
LIB_SRCS += lib/nodes/websocket.c
LIB_PKGS += libwebsockets jansson
endif
endif
# Enable OPAL-RT Asynchronous Process support (will result in 32bit binary!!!)
ifdef WITH_OPAL
ifneq (,$(wildcard thirdparty/opal/include/AsyncApi.h))
LIB_OBJS += opal.o
LIB_CFLAGS += -I thirdparty/opal/include
LIB_LDFLAGS += -L/lib/i386-linux-gnu/ -L/usr/lib/i386-linux-gnu/ -Lthirdparty/opal/lib/redhawk/
LIB_LDLIBS += -lOpalAsyncApiCore -lOpalCore -lOpalUtils -lirc
# libOpalAsyncApi is a 32bit library. So we need to build everything in 32bit
CFLAGS += -m32
LDFLAGS += -m32
BUILDDIR := $(BUILDDIR)32
endif
endif
# Add flags by pkg-config
LIB_CFLAGS += $(addprefix -DWITH_, $(shell echo ${LIB_PKGS} | tr a-z- A-Z_ | tr -dc ' A-Z0-9_' ))
LIB_CFLAGS += $(shell pkg-config --cflags ${LIB_PKGS})
LIB_LDLIBS += $(shell pkg-config --libs ${LIB_PKGS})
LIB_OBJS = $(patsubst %.c, $(BUILDDIR)/%.o, $(LIB_SRCS))
lib: $(LIBS)
# Compile
$(BUILDDIR)/lib/%.o: lib/%.c | $$(dir $$@)
$(CC) $(LIB_CFLAGS) -c $< -o $@
# Link
$(LIBS): $(LIB_OBJS)
$(CC) $(LIB_LDFLAGS) -o $@ $^ $(LIB_LDLIBS)
# Install
install-lib: lib
install -m 0644 $(LIBS) $(PREFIX)/lib
install -m 0755 -d $(PREFIX)/include/villas/
install -m 0644 include/villas/*.h $(PREFIX)/include/villas/
ldconfig
clean-lib:
rm -rf $(BUILDDIR)/lib $(LIBS)
.PHONY: lib lib-tests lib-tests

View file

@ -18,10 +18,9 @@ void hook_stats_header()
{
#define UNIT(u) "(" YEL(u) ")"
stats("%-40s|%19s|%19s|%19s|%19s|%19s|%19s|%10s|", "Source " MAG("=>") " Destination",
stats("%-40s|%19s|%19s|%19s|%19s|%19s|%10s|", "Source " MAG("=>") " Destination",
"OWD" UNIT("S") " ",
"Rate" UNIT("p/S") " ",
"Recv" UNIT("p") " ",
"Drop" UNIT("p") " ",
"Skip" UNIT("p") " ",
"Inval" UNIT("p") " ",
@ -84,8 +83,8 @@ int hook_stats(struct path *p, struct hook *h, int when, struct sample *smps[],
break;
case HOOK_PERIODIC:
stats("%-40.40s|%10s|%10s|%10ju|%10ju|%10ju|%10ju|%10ju|", path_name(p), "", "",
p->in->received, p->dropped, p->skipped, p->invalid, p->overrun);
stats("%-40.40s|%10s|%10s|%10ju|%10ju|%10ju|%10ju|", path_name(p), "", "",
p->dropped, p->skipped, p->invalid, p->overrun);
break;
}
@ -117,26 +116,19 @@ int hook_stats_send(struct path *p, struct hook *h, int when, struct sample *smp
break;
case HOOK_PERIODIC: {
int ret, length;
char buf[SAMPLE_LEN(9)];
struct sample *last, *smp = (struct sample *) buf;
int i;
char buf[SAMPLE_LEN(16)];
struct sample *smp = (struct sample *) buf;
ret = queue_get(&p->queue, (void **) &last, p->in->received - 1);
if (ret == 1)
length = last->length;
else
length = -1;
smp->data[0].f = p->in->received;
smp->data[1].f = length;
smp->data[2].f = p->invalid;
smp->data[3].f = p->skipped;
smp->data[4].f = p->dropped;
smp->data[5].f = p->overrun;
smp->data[6].f = p->hist.owd.last,
smp->data[7].f = 1.0 / p->hist.gap_msg.last;
smp->data[8].f = 1.0 / p->hist.gap_recv.last;
smp->length = 9;
i = 0;
smp->data[i++].f = p->invalid;
smp->data[i++].f = p->skipped;
smp->data[i++].f = p->dropped;
smp->data[i++].f = p->overrun;
smp->data[i++].f = p->hist.owd.last,
smp->data[i++].f = 1.0 / p->hist.gap_msg.last;
smp->data[i++].f = 1.0 / p->hist.gap_recv.last;
smp->length = i;
node_write(private->dest, &smp, 1); /* Send single message with statistics to destination node */
break;

View file

@ -6,71 +6,236 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include "log.h"
#include "kernel/pci.h"
#include "config.h"
static struct pci_access *pacc;
static void pci_log(char *msg, ...)
int pci_init(struct pci *p)
{
char *tmp = strdup(msg);
struct dirent *entry;
DIR *dp;
FILE *f;
char path[256];
int ret;
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
va_list ap;
va_start(ap, msg);
log_vprint("PCI ", strtok(tmp, "\n"), ap);
va_end(ap);
free(tmp);
}
struct pci_access * pci_get_handle()
{
if (pacc)
return pacc; /* Singleton */
pacc = pci_alloc(); /* Get the pci_access structure */
if (!pacc)
error("Failed to allocate PCI access structure");
pci_init(pacc); /* Initialize the PCI library */
pci_scan_bus(pacc); /* We want to get the list of devices */
pacc->error = pci_log; /* Replace logging and debug functions */
pacc->warning = pci_log;
pacc->debug = pci_log;
pacc->debugging = 1;
pci_scan_bus(pacc); /* We want to get the list of devices */
return pacc;
}
void pci_release_handle()
{
if (pacc) {
//pci_cleanup(pacc);
//pacc = NULL;
}
}
struct pci_dev * pci_find_device(struct pci_access *pacc, struct pci_filter *f)
{
struct pci_dev *d;
/* Iterate over all devices */
for (d = pacc->devices; d; d = d->next) {
if (pci_filter_match(f, d))
return d;
dp = opendir(path);
if (dp == NULL) {
serror("Failed to detect PCI devices");
return -1;
}
return NULL;
while ((entry = readdir(dp))) {
struct pci_dev d;
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, entry->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(entry->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", entry->d_name);
}
closedir(dp);
return 0;
}
void pci_destroy(struct pci *p)
{
list_destroy(&p->devices, NULL, true);
}
int pci_dev_init(struct pci_dev *d)
{
return 0;
}
void pci_dev_destroy(struct pci_dev *d)
{
}
int pci_dev_parse_slot(struct pci_dev *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_dev_parse_id(struct pci_dev *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 = x;
}
return 0;
fail:
return -1;
}
int pci_dev_compare(const struct pci_dev *d, const struct pci_dev *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 0;
if (f->id.device >= 0 || f->id.vendor >= 0) {
if ((f->id.device >= 0 && f->id.device != d->id.device) || (f->id.vendor >= 0 && f->id.vendor != d->id.vendor))
return 0;
}
if (f->id.class >= 0) {
if (f->id.class != d->id.class)
return 0;
}
return 1;
}
struct pci_dev * pci_lookup_device(struct pci *p, struct pci_dev *f)
{
return list_search(&p->devices, (cmp_cb_t) pci_dev_compare, (void *) f);
}
int pci_attach_driver(struct pci_dev *d, const char *driver)
@ -84,8 +249,8 @@ int pci_attach_driver(struct pci_dev *d, const char *driver)
if (!f)
serror("Failed to add PCI id to %s driver (%s)", driver, fn);
debug(5, "Adding ID to %s module: %04x %04x", driver, d->vendor_id, d->device_id);
fprintf(f, "%04x %04x", d->vendor_id, d->device_id);
debug(5, "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 */
@ -95,19 +260,19 @@ int pci_attach_driver(struct pci_dev *d, const char *driver)
serror("Failed to bind PCI device to %s driver (%s)", driver, fn);
debug(5, "Bind device to %s driver", driver);
fprintf(f, "%04x:%02x:%02x.%x\n", d->domain, d->bus, d->dev, d->func);
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(struct pci_dev *pdev)
int pci_get_iommu_group(struct pci_dev *d)
{
int ret;
char *group, link[1024], sysfs[1024];
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
pdev->domain, pdev->bus, pdev->dev, pdev->func);
d->slot.domain, d->slot.bus, d->slot.device, d->slot.function);
ret = readlink(sysfs, link, sizeof(link));
if (ret < 0)

View file

@ -225,7 +225,7 @@ int vfio_pci_attach(struct vfio_dev *d, struct vfio_container *c, struct pci_dev
error("Failed to get IOMMU group of device");
/* VFIO device name consists of PCI BDF */
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev->domain, pdev->bus, pdev->dev, pdev->func);
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev->slot.domain, pdev->slot.bus, pdev->slot.device, pdev->slot.function);
ret = vfio_dev_attach(d, c, name, index);
if (ret < 0)

View file

@ -57,7 +57,7 @@ void list_destroy(struct list *l, dtor_cb_t destructor, bool release)
l->array = NULL;
l->length =
l->length = -1;
l->capacity = 0;
pthread_mutex_unlock(&l->lock);

View file

@ -1,130 +0,0 @@
/** Lock-less LIFO stack
*
* Based on a single-linked list and double word compare exchange (DWCAS)
* to solve the ABA problem.
*
* Based on https://github.com/skeeto/lstack
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <stdlib.h>
#include <errno.h>
#include "lstack.h"
#include "utils.h"
static struct lstack_node * pop_many(_Atomic struct lstack_head *head, size_t cnt)
{
size_t i;
struct lstack_head next, orig = atomic_load(head);
do {
next.aba = orig.aba + 1;
for (i = 0, next.node = orig.node;
i < cnt && next.node;
i++, next.node = next.node->next) {
// debug(3, "pop_many next.node %p next.node->next %p", next.node, next.node->next);
}
if (i != cnt)
return NULL;
} while (!atomic_compare_exchange_weak(head, &orig, next));
return orig.node;
}
static int push_many(_Atomic struct lstack_head *head, struct lstack_node *node, size_t cnt)
{
size_t i;
struct lstack_head next, orig = atomic_load(head);
struct lstack_node *last = node;
/* Find last node which will be pushed */
for (i = 1; i < cnt; i++) {
// debug(3, "push_many node %p node->next %p", last, last->next);
last = last->next;
}
do {
next.aba = orig.aba + 1;
next.node = node;
last->next = orig.node;
} while (!atomic_compare_exchange_weak(head, &orig, next));
return 0;
}
int lstack_init(struct lstack *lstack, size_t maxsz)
{
/* Pre-allocate all nodes. */
lstack->node_buffer = alloc(maxsz * sizeof(struct lstack_node));
for (size_t i = 1; i < maxsz; i++)
lstack->node_buffer[i-1].next = &lstack->node_buffer[i];
lstack->node_buffer[maxsz - 1].next = NULL;
lstack->free = ATOMIC_VAR_INIT(((struct lstack_head) { 0, lstack->node_buffer }));
lstack->head = ATOMIC_VAR_INIT(((struct lstack_head) { 0, NULL }));
lstack->size = ATOMIC_VAR_INIT(0);
lstack->avail = ATOMIC_VAR_INIT(maxsz);
return 0;
}
ssize_t lstack_push_many(struct lstack *lstack, void *values[], size_t cnt)
{
size_t i;
struct lstack_node *nodes, *node;
if (cnt == 0)
return 0;
nodes = pop_many(&lstack->free, cnt);
if (!nodes)
return 0;
atomic_fetch_sub(&lstack->avail, cnt);
for (i = 0, node = nodes;
i < cnt && node;
i++, node = node->next)
node->value = values[i];
push_many(&lstack->head, nodes, cnt);
atomic_fetch_add(&lstack->size, cnt);
return i;
}
ssize_t lstack_pop_many(struct lstack *lstack, void *values[], size_t cnt)
{
size_t i;
struct lstack_node *nodes, *node;
if (cnt == 0)
return 0;
nodes = pop_many(&lstack->head, cnt);
if (!nodes)
return 0;
atomic_fetch_sub(&lstack->size, cnt);
for (i = 0, node = nodes;
i < cnt && node;
i++, node = node->next)
values[i] = node->value;
push_many(&lstack->free, nodes, cnt);
atomic_fetch_add(&lstack->avail, cnt);
return i;
}

97
lib/memory.c Normal file
View file

@ -0,0 +1,97 @@
/** Memory allocators.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*/
#include <stdlib.h>
#include <sys/mman.h>
/* Required to allocate hugepages on Apple OS X */
#ifdef __MACH__
#include <mach/vm_statistics.h>
#endif
#include "log.h"
#include "memory.h"
void * memory_alloc(const struct memtype *m, size_t len)
{
return m->alloc(len);
}
void * memory_alloc_aligned(const struct memtype *m, size_t len, size_t alignment)
{
warn("memory_alloc_aligned: not implemented yet!");
return memory_alloc(m, len);
}
void * memory_aligned_alloc(const struct memtype *m, size_t len, size_t align)
{
warn("memory_aligned_alloc: not implemented yet. Falling back to unaligned version.");
return memory_alloc(m, len);
}
int memory_free(const struct memtype *m, void *ptr, size_t len)
{
return m->free(ptr, len);
}
static void * memory_heap_alloc(size_t len)
{
return malloc(len);
}
int memory_heap_free(void *ptr, size_t len)
{
free(ptr);
return 0;
}
/** Allocate memory backed by hugepages with malloc() like interface */
static void * memory_hugepage_alloc(size_t len)
{
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
#ifdef __MACH__
flags |= VM_FLAGS_SUPERPAGE_SIZE_2MB;
#elif defined(__linux__)
flags |= MAP_HUGETLB | MAP_LOCKED;
#endif
return mmap(NULL, len, prot, flags, -1, 0);
}
static int memory_hugepage_free(void *ptr, size_t len)
{
return munmap(ptr, len);
}
/* List of available memory types */
const struct memtype memtype_heap = {
.name = "heap",
.flags = MEMORY_HEAP,
.alloc = memory_heap_alloc,
.free = memory_heap_free,
.alignment = 1
};
const struct memtype memtype_hugepage = {
.name = "mmap_hugepages",
.flags = MEMORY_MMAP | MEMORY_HUGEPAGE,
.alloc = memory_hugepage_alloc,
.free = memory_hugepage_free,
.alignment = 1 << 21 /* 2 MiB hugepage */
};
/** @todo */
const struct memtype memtype_dma = {
.name = "dma",
.flags = MEMORY_DMA | MEMORY_MMAP,
.alloc = NULL, .free = NULL,
.alignment = 1 << 12
};

View file

@ -22,6 +22,7 @@
#include "timing.h"
struct fpga fpga;
struct pci pci;
struct vfio_container vc;
int fpga_reset(struct fpga *f)
@ -55,21 +56,12 @@ int fpga_reset(struct fpga *f)
void fpga_dump(struct fpga *f)
{
char namebuf[128];
char *name;
struct pci_access *pacc;
pacc = pci_get_handle();
name = pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, fpga.vd.pdev->vendor_id, fpga.vd.pdev->device_id);
pci_fill_info(fpga.vd.pdev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */
info("VILLASfpga card: %s", name);
info("VILLASfpga card:");
{ INDENT
info("Slot: %04x:%02x:%02x.%d", fpga.vd.pdev->domain, fpga.vd.pdev->bus, fpga.vd.pdev->dev, fpga.vd.pdev->func);
info("Vendor ID: %04x", fpga.vd.pdev->vendor_id);
info("Device ID: %04x", fpga.vd.pdev->device_id);
info("Class ID: %04x", fpga.vd.pdev->device_class);
info("Slot: %04x:%02x:%02x.%d", fpga.vd.pdev->slot.domain, fpga.vd.pdev->slot.bus, fpga.vd.pdev->slot.device, fpga.vd.pdev->slot.function);
info("Vendor ID: %04x", fpga.vd.pdev->id.vendor);
info("Device ID: %04x", fpga.vd.pdev->id.device);
info("Class ID: %04x", fpga.vd.pdev->id.class);
info("BAR0 mapped at %p", fpga.map);
@ -94,8 +86,8 @@ int fpga_parse_card(struct fpga *f, int argc, char * argv[], config_setting_t *c
config_setting_t *cfg_ips, *cfg_slot, *cfg_id, *cfg_fpgas;
/* Default values */
f->filter.vendor = FPGA_PCI_VID_XILINX;
f->filter.device = FPGA_PCI_PID_VFPGA;
f->filter.id.vendor = FPGA_PCI_VID_XILINX;
f->filter.id.device = FPGA_PCI_PID_VFPGA;
cfg_fpgas = config_setting_get_member(cfg, "fpgas");
if (!cfg_fpgas)
@ -118,24 +110,24 @@ int fpga_parse_card(struct fpga *f, int argc, char * argv[], config_setting_t *c
if (cfg_slot) {
slot = config_setting_get_string(cfg_slot);
if (slot) {
err = pci_filter_parse_slot(&f->filter, (char*) slot);
if (err)
cerror(cfg_slot, "%s", err);
ret = pci_dev_parse_slot(&f->filter, slot, &err);
if (ret)
cerror(cfg_slot, "Failed to parse PCI slot: %s", err);
}
else
cerror(cfg_slot, "Invalid slot format");
cerror(cfg_slot, "PCI slot must be a string");
}
cfg_id = config_setting_get_member(f->cfg, "id");
if (cfg_id) {
id = config_setting_get_string(cfg_id);
if (id) {
err = pci_filter_parse_id(&f->filter, (char*) id);
if (err)
cerror(cfg_id, "%s", err);
ret = pci_dev_parse_id(&f->filter, (char*) id, &err);
if (ret)
cerror(cfg_id, "Failed to parse PCI id: %s", err);
}
else
cerror(cfg_slot, "Invalid id format");
cerror(cfg_slot, "PCI ID must be a string");
}
cfg_ips = config_setting_get_member(f->cfg, "ips");
@ -162,16 +154,15 @@ int fpga_parse_card(struct fpga *f, int argc, char * argv[], config_setting_t *c
int fpga_init(int argc, char * argv[], config_setting_t *cfg)
{
int ret;
struct pci_access *pacc;
struct pci_dev *pdev;
struct fpga *f;
struct pci_dev *pdev;
/* For now we only support a single VILALSfpga card */
f = fpga_get();
list_init(&f->ips);
pacc = pci_get_handle();
pci_filter_init(pacc, &f->filter);
pci_init(&pci);
pci_dev_init(&f->filter);
/* Parse FPGA configuration */
ret = fpga_parse_card(f, argc, argv, cfg);
@ -192,7 +183,7 @@ int fpga_init(int argc, char * argv[], config_setting_t *cfg)
warn("FPGA is missing an AXI4-Stream switch");
/* Search for FPGA card */
pdev = pci_find_device(pacc, &f->filter);
pdev = pci_lookup_device(&pci, &f->filter);
if (!pdev)
error("Failed to find PCI device");
@ -243,8 +234,8 @@ int fpga_deinit()
int ret;
list_destroy(&fpga.ips, (dtor_cb_t) ip_destroy, true);
pci_release_handle();
pci_destroy(&pci);
ret = vfio_destroy(&vc);
if (ret)
@ -276,9 +267,10 @@ char * fpga_print(struct node *n)
if (d->ip)
return strf("dm=%s (%s:%s:%s:%s) baseaddr=%#jx port=%u slot=%02"PRIx8":%02"PRIx8".%"PRIx8" id=%04"PRIx16":%04"PRIx16,
d->ip->name, d->ip->vlnv.vendor, d->ip->vlnv.library, d->ip->vlnv.name, d->ip->vlnv.version, d->ip->baseaddr, d->ip->port,
f->filter.bus, f->filter.device, f->filter.func,
f->filter.vendor, f->filter.device);
d->ip->name, d->ip->vlnv.vendor, d->ip->vlnv.library, d->ip->vlnv.name, d->ip->vlnv.version,
d->ip->baseaddr, d->ip->port,
f->filter.slot.bus, f->filter.slot.device, f->filter.slot.function,
f->filter.id.vendor, f->filter.id.device);
else
return strf("dm=%s", d->ip_name);
}
@ -370,7 +362,7 @@ int fpga_read(struct node *n, struct sample *smps[], unsigned cnt)
size_t len = SAMPLE_DATA_LEN(64);
/* We dont get a sequence no from the FPGA. Lets fake it */
smp->sequence = n->received;
smp->sequence = -1;
smp->ts.origin = time_now();
/* Read data from RTDS */
@ -441,7 +433,7 @@ int fpga_write(struct node *n, struct sample *smps[], unsigned cnt)
static struct node_type vt = {
.name = "fpga",
.description = "VILLASfpga PCIe card (libpci)",
.description = "VILLASfpga PCIe card (libxil)",
.size = sizeof(struct fpga_dm),
.vectorize = 1,
.parse = fpga_parse,

View file

@ -10,7 +10,6 @@
#include <stdio.h>
#include <curl/curl.h>
#include <uuid/uuid.h>
#include <jansson.h>
#include <math.h>
#include <pthread.h>
@ -24,45 +23,6 @@
/* Some global settings */
static char *name = NULL;
#if 0 /* unused at the moment */
static json_t * json_uuid()
{
char eid[37];
uuid_t uuid;
uuid_generate_time(uuid);
uuid_unparse_lower(uuid, eid);
return json_string(eid);
}
static json_t * json_date(struct timespec *ts)
{
// Example: 2015-09-21T11:42:25+02:00
char date[64];
strftimespec(date, sizeof(date), "%FT%T.%u%z", ts);
return json_string(date);
}
static json_t * json_lookup(json_t *array, char *key, char *needle)
{
size_t ind;
json_t *obj;
json_array_foreach(array, ind, obj) {
json_t *value = json_object_get(obj, key);
if (value && json_is_string(value)) {
if (!strcmp(json_string_value(value), needle))
return obj;
}
}
return NULL;
}
#endif
enum ngsi_flags {
NGSI_ENTITY_ATTRIBUTES = (1 << 0),
NGSI_ENTITY_VALUES = (1 << 1) | NGSI_ENTITY_ATTRIBUTES,
@ -594,7 +554,7 @@ int ngsi_write(struct node *n, struct sample *smps[], unsigned cnt)
static struct node_type vt = {
.name = "ngsi",
.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson, libuuid)",
.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson)",
.vectorize = 0, /* unlimited */
.size = sizeof(struct ngsi),
.parse = ngsi_parse,

View file

@ -23,17 +23,10 @@ static void path_write(struct path *p, bool resend)
{
list_foreach(struct node *n, &p->destinations) {
int cnt = n->vectorize;
int sent, tosend, base, available, release, released;
int sent, tosend, available, released;
struct sample *smps[n->vectorize];
/* The first message in the chunk which we want to send */
if (resend)
base = p->in->received - cnt; /* we simply resend the last vector of samples */
else {
base = n->sent;
}
available = queue_get_many(&p->queue, (void **) smps, cnt, base);
available = mpmc_queue_pull_many(&p->queue, (void **) smps, cnt);
if (available < cnt)
warn("Queue underrun for path %s: available=%u expected=%u", path_name(p), available, cnt);
@ -52,18 +45,9 @@ static void path_write(struct path *p, bool resend)
debug(DBG_PATH | 15, "Sent %u messages to node %s", sent, node_name(n));
/* Release samples from queue in case they are not sent periodically. */
if (resend)
continue;
/* Decrement reference count and release samples back to pool if we had the last reference */
release = queue_pull_many(&p->queue, (void **) smps, sent, &n->sent);
if (release > 0)
debug(DBG_PATH | 3, "Releasing %u samples to pool for path %s", release, path_name(p));
released = pool_put_many(&p->pool, (void **) smps, release);
if (release != released)
warn("Failed to release %u samples to pool for path %s", release - released, path_name(p));
released = pool_put_many(&p->pool, (void **) smps, sent);
if (sent != released)
warn("Failed to release %u samples to pool for path %s", sent - released, path_name(p));
}
}
@ -83,9 +67,6 @@ static void * path_run_async(void *arg)
warn("Overrun detected for path: overruns=%" PRIu64, expir);
}
if (p->in->received == 0)
continue;
if (hook_run(p, NULL, 0, HOOK_ASYNC))
continue;
@ -106,10 +87,6 @@ static void * path_run(void *arg)
/* Main thread loop */
for (;;) {
struct node *out = (struct node *) list_first(&p->destinations);
debug(DBG_PATH | 5, "Current queue status for path %s: ready=%u write=%ju read[0]=%ju", path_name(p), ready, p->in->received, out->sent);
debug(DBG_PATH | 5, "Current pool status for path %s: used=%zu avail=%zu", path_name(p), p->pool.stack.size, p->pool.stack.avail);
/* Fill smps[] free sample blocks from the pool */
ready += sample_get_many(&p->pool, smps, cnt - ready);
if (ready != cnt)
@ -131,29 +108,12 @@ static void * path_run(void *arg)
p->skipped += recv - enqueue;
}
enqueued = queue_push_many(&p->queue, (void **) smps, enqueue, &p->in->received);
enqueued = mpmc_queue_push_many(&p->queue, (void **) smps, enqueue);
if (enqueue != enqueued)
warn("Failed to enqueue %u samples for path %s", enqueue - enqueued, path_name(p));
ready -= enqueued;
list_foreach(struct hook *h, &p->hooks) {
int pull, release, released;
pull = p->in->received - h->head - h->history;
if (pull > 0) {
struct sample *smps[pull];
release = queue_pull_many(&p->queue, (void **) smps, pull, &h->head);
if (release > 0)
debug(DBG_PATH | 3, "Releasing %u samples from queue of path %s", release, path_name(p));
released = pool_put_many(&p->pool, (void **) smps, release);
if (release != released)
warn("Failed to release %u samples to pool of path %s", release - released, path_name(p));
}
}
debug(DBG_PATH | 3, "Enqueuing %u samples to queue of path %s", enqueue, path_name(p));
/* At fixed rate mode, messages are send by another (asynchronous) thread */
@ -255,22 +215,14 @@ int path_prepare(struct path *p)
error("Failed to parse arguments for hooks of path: %s", path_name(p));
/* Initialize queue */
ret = pool_init_mmap(&p->pool, SAMPLE_LEN(p->samplelen), p->queuelen);
ret = pool_init(&p->pool, SAMPLE_LEN(p->samplelen), p->queuelen, &memtype_hugepage);
if (ret)
error("Failed to allocate memory pool for path");
ret = queue_init(&p->queue, p->queuelen);
ret = mpmc_queue_init(&p->queue, p->queuelen, &memtype_hugepage);
if (ret)
error("Failed to initialize queue for path");
/* Add a head pointer for each hook to the queue */
list_foreach(struct hook *h, &p->hooks)
queue_reader_add(&p->queue, h->head, p->in->received);
/* Add a head pointer for each destination node to the queue. */
list_foreach(struct node *out, &p->destinations)
queue_reader_add(&p->queue, out->sent, p->in->received);
return 0;
}
@ -281,7 +233,7 @@ void path_destroy(struct path *p)
list_destroy(&p->destinations, NULL, false);
list_destroy(&p->hooks, NULL, true);
queue_destroy(&p->queue);
mpmc_queue_destroy(&p->queue);
pool_destroy(&p->pool);
free(p->_name);

View file

@ -6,52 +6,37 @@
* Unauthorized copying of this file, via any medium is strictly prohibited.
*/
#include <sys/mman.h>
#include "utils.h"
#include "pool.h"
#include "memory.h"
#include "kernel/kernel.h"
int pool_init_mmap(struct pool *p, size_t blocksz, size_t cnt)
int pool_init(struct pool *p, size_t blocksz, size_t cnt, const struct memtype *m)
{
void *addr;
int flags;
size_t len, alignedsz, align;
align = kernel_get_cacheline_size();
alignedsz = blocksz * CEIL(blocksz, align);
len = cnt * alignedsz;
debug(DBG_POOL | 4, "Allocating %#zx bytes for memory pool", len);
flags = MAP_LOCKED | MAP_PRIVATE | MAP_ANONYMOUS; // MAP_HUGETLB
/** @todo Use hugepages */
/* Make sure that we use a block size that is aligned to the size of a cache line */
p->alignment = kernel_get_cacheline_size();
p->blocksz = blocksz * CEIL(blocksz, p->alignment);
p->len = cnt * p->blocksz;
p->mem = m;
/* addr is allways aligned to pagesize boundary */
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, flags, -1, 0);
if (addr == MAP_FAILED)
serror("Failed to allocate memory for sample pool");
return pool_init(p, blocksz, align, addr, len);
}
int pool_init(struct pool *p, size_t blocksz, size_t align, void *buf, size_t len)
{
size_t alignedsz, cnt;
p->buffer = memory_alloc_aligned(m, p->len, p->alignment);
if (!p->buffer)
serror("Failed to allocate memory for memory pool");
else
debug(DBG_POOL | 4, "Allocated %#zx bytes for memory pool", p->len);
assert(IS_ALIGNED(buf, align)); /* buf has to be aligned */
p->blocksz = blocksz;
p->alignment = align;
alignedsz = blocksz * CEIL(blocksz, align);
cnt = len / alignedsz;
lstack_init(&p->stack, cnt);
mpmc_queue_init(&p->queue, cnt, m);
for (int i = 0; i < cnt; i++)
lstack_push(&p->stack, buf + i * alignedsz);
mpmc_queue_push(&p->queue, (char *) p->buffer + i * p->blocksz);
return 0;
}
int pool_destroy(struct pool *p)
{
mpmc_queue_destroy(&p->queue);
return memory_free(p->mem, p->buffer, p->len);
}

View file

@ -1,133 +1,157 @@
/** Lock-free Single-Producer Single-consumer (SPSC) queue.
/** Lock-free Multiple-Producer Multiple-consumer (MPMC) queue.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
* Based on Dmitry Vyukov#s Bounded MPMC queue:
* http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
*
* @author Steffen Vogel <post@steffenvogel.de>
* @copyright 2016 Steffen Vogel
* @license BSD 2-Clause License
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modiffication, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "queue.h"
#include "utils.h"
int queue_init(struct queue *q, size_t size)
/** Initialize MPMC queue */
int mpmc_queue_init(struct mpmc_queue *q, size_t size, const struct memtype *mem)
{
q->size = size;
/* Queue size must be 2 exponent */
if ((size < 2) || ((size & (size - 1)) != 0))
return -1;
q->pointers = alloc(size * sizeof(void *));
q->refcnts = alloc(size * sizeof(atomic_uint));
q->readers = 0;
q->mem = mem;
q->buffer_mask = size - 1;
q->buffer = memory_alloc(q->mem, sizeof(q->buffer[0]) * size);
if (!q->buffer)
return -2;
for (size_t i = 0; i != size; i += 1)
atomic_store_explicit(&q->buffer[i].sequence, i, memory_order_relaxed);
atomic_store_explicit(&q->tail, 0, memory_order_relaxed);
atomic_store_explicit(&q->head, 0, memory_order_relaxed);
return 0;
}
void queue_destroy(struct queue *q)
int mpmc_queue_destroy(struct mpmc_queue *q)
{
free(q->pointers);
free(q->refcnts);
return memory_free(q->mem, q->buffer, (q->buffer_mask + 1) * sizeof(sizeof(q->buffer[0])));
}
void queue_reader_add(struct queue *q, qptr_t head, qptr_t tail)
/** Return estimation of current queue usage.
*
* Note: This is only an estimation and not accurate as long other
* threads are performing operations.
*/
size_t mpmc_queue_available(struct mpmc_queue *q)
{
assert (head <= tail);
atomic_fetch_add(&q->readers, 1);
for (qptr_t i = head; i < tail; i++)
atomic_fetch_add(&q->refcnts[i % q->size], 1);
return atomic_load_explicit(&q->tail, memory_order_relaxed) -
atomic_load_explicit(&q->head, memory_order_relaxed);
}
void queue_reader_remove(struct queue *q, qptr_t head, qptr_t tail)
int mpmc_queue_push(struct mpmc_queue *q, void *ptr)
{
assert (head <= tail);
struct mpmc_queue_cell *cell;
size_t pos, seq;
intptr_t diff;
atomic_fetch_sub(&q->readers, 1);
for (qptr_t i = head; i < tail; i++)
atomic_fetch_sub(&q->refcnts[i % q->size], 1);
}
pos = atomic_load_explicit(&q->tail, memory_order_relaxed);
for (;;) {
cell = &q->buffer[pos & q->buffer_mask];
seq = atomic_load_explicit(&cell->sequence, memory_order_acquire);
diff = (intptr_t) seq - (intptr_t) pos;
int queue_get(struct queue *q, void **ptr, qptr_t pos)
{
int refcnt = atomic_load(&q->refcnts[pos % q->size]);
if (refcnt == 0)
return 0;
*ptr = q->pointers[pos % q->size];
if (diff == 0) {
if (atomic_compare_exchange_weak_explicit(&q->tail, &pos, pos + 1, memory_order_relaxed, memory_order_relaxed))
break;
}
else if (diff < 0)
return 0;
else
pos = atomic_load_explicit(&q->tail, memory_order_relaxed);
}
cell->data = ptr;
atomic_store_explicit(&cell->sequence, pos + 1, memory_order_release);
return 1;
}
int queue_push(struct queue *q, void *ptr, qptr_t *tail)
int mpmc_queue_pull(struct mpmc_queue *q, void **ptr)
{
int refcnt;
do {
refcnt = atomic_load(&q->refcnts[*tail % q->size]);
if (refcnt != 0)
return 0; /* Queue is full */
q->pointers[*tail % q->size] = ptr;
} while (!atomic_compare_exchange_weak(&q->refcnts[*tail % q->size], &refcnt, q->readers));
*tail = *tail + 1;
struct mpmc_queue_cell *cell;
size_t pos, seq;
intptr_t diff;
pos = atomic_load_explicit(&q->head, memory_order_relaxed);
for (;;) {
cell = &q->buffer[pos & q->buffer_mask];
seq = atomic_load_explicit(&cell->sequence, memory_order_acquire);
diff = (intptr_t) seq - (intptr_t) (pos + 1);
if (diff == 0) {
if (atomic_compare_exchange_weak_explicit(&q->head, &pos, pos + 1, memory_order_relaxed, memory_order_relaxed))
break;
}
else if (diff < 0)
return 0;
else
pos = atomic_load_explicit(&q->head, memory_order_relaxed);
}
*ptr = cell->data;
atomic_store_explicit(&cell->sequence, pos + q->buffer_mask + 1, memory_order_release);
return 1;
}
int queue_pull(struct queue *q, void **ptr, qptr_t *head)
int mpmc_queue_push_many(struct mpmc_queue *q, void *ptr[], size_t cnt)
{
int refcnt;
do {
refcnt = atomic_load(&q->refcnts[*head % q->size]);
if (refcnt == 0)
return -1; /* Queue is empty */
*ptr = q->pointers[*head % q->size];
} while (!atomic_compare_exchange_weak(&q->refcnts[*head % q->size], &refcnt, refcnt - 1));
*head = *head + 1;
return refcnt == 1 ? 1 : 0;
}
int queue_get_many(struct queue *q, void *ptrs[], size_t cnt, qptr_t pos)
{
int ret, i;
int ret;
size_t i;
for (i = 0; i < cnt; i++) {
ret = queue_get(q, &ptrs[i], pos + i);
if (ret == 0)
ret = mpmc_queue_push(q, ptr[i]);
if (!ret)
break;
}
return i;
}
int queue_push_many(struct queue *q, void **ptrs, size_t cnt, qptr_t *tail)
int mpmc_queue_pull_many(struct mpmc_queue *q, void *ptr[], size_t cnt)
{
int ret, i;
int ret;
size_t i;
for (i = 0; i < cnt; i++) {
ret = queue_push(q, ptrs[i], tail);
if (ret == 0)
ret = mpmc_queue_pull(q, &ptr[i]);
if (!ret)
break;
}
return i;
}
int queue_pull_many(struct queue *q, void **ptrs, size_t cnt, qptr_t *head)
{
int released = 0, ret;
for (int i = 0; i < cnt; i++) {
ret = queue_pull(q, &ptrs[i], head);
if (ret < 0) /* empty */
break;
if (ret == 1)
released++;
}
return released;
}

View file

@ -25,6 +25,22 @@ int sample_get_many(struct pool *p, struct sample *smps[], int cnt) {
return ret;
}
int sample_get(struct sample *s)
{
return atomic_fetch_add(&s->refcnt, 1) + 1;
}
int sample_put(struct sample *s)
{
int prev = atomic_fetch_sub(&s->refcnt, 1);
/* Did we had the last refernce? */
if (prev == 1)
pool_put(s->pool, s);
return prev - 1;
}
int sample_print(char *buf, size_t len, struct sample *s, int flags)
{
size_t off = snprintf(buf, len, "%llu", (unsigned long long) s->ts.origin.tv_sec);

View file

@ -71,7 +71,7 @@ struct timespec time_add(struct timespec *start, struct timespec *end)
.tv_nsec = end->tv_nsec + start->tv_nsec
};
if (sum.tv_nsec > 1000000000) {
if (sum.tv_nsec >= 1000000000) {
sum.tv_sec += 1;
sum.tv_nsec -= 1000000000;
}

27
plugins/Makefile.inc Normal file
View file

@ -0,0 +1,27 @@
# Plugins
PLUGINS = $(BUILDDIR)/simple_circuit.so \
$(BUILDDIR)/example_hook.so
PLUGIN_CFLAGS = -fPIC -DVILLAS -I../include/villas
# Dependencies for plugins
$(BUILDDIR)/example_hook.so: $(BUILDDIR)/plugins/hooks/example_hook.o
$(BUILDDIR)/simple_circuit.so: $(BUILDDIR)/plugins/models/simple_circuit.o
plugins: $(PLUGINS)
# Compile
$(BUILDDIR)/plugins/%.o: plugins/%.c | $$(dir $$@)
$(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
# Link
$(PLUGINS):
$(CC) $(LIB_LDFLAGS) -o $@ $^ $(LIB_LDLIBS)
# Plugins are not installed to the system for now...
install-plugins:
clean-plugins:
rm -rf $(BUILDDIR)/plugins $(PLUGINS)
.PHONY: plugins install-plugins clean-plugins

49
src/Makefile.inc Normal file
View file

@ -0,0 +1,49 @@
# Executables
TARGETS = $(BUILDDIR)/villas-node \
$(BUILDDIR)/villas-pipe \
$(BUILDDIR)/villas-signal \
$(BUILDDIR)/villas-test
SRC_LDLIBS = $(LDLIBS) -pthread -lm -lvillas
SRC_CFLAGS = $(CFLAGS)
SRC_LDFLAGS = $(LDFLAGS) -Wl,-rpath,'$$ORIGIN'
# Enable VILLASfpga support when libxil is available
ifeq ($(shell pkg-config libxil; echo $$?),0)
LIB_SRCS += $(addprefix lib/nodes/, fpga.c)
LIB_SRCS += $(addprefix lib/kernel/, pci.c vfio.c)
LIB_SRCS += $(wildcard lib/fpga/*.c)
TARGETS += $(BUILDDIR)/villas-fpga
PKGS += libxil
# Add support for LAPACK / BLAS benchmarks / solvers
ifeq ($(shell pkg-config blas lapack; echo $$?),0)
PKGS += blas lapack
BENCH_OBJS += fpga-bench-overruns.o
endif
endif
src: $(TARGETS)
$(TARGETS): $(BUILDDIR)/villas-%: $(BUILDDIR)/src/%.o
# Some additional prereqs for individual binaries
$(BUILDDIR)/villas-fpga: $(addprefix $(BUILDDIR)/src/,fpga-tests.o fpga-bench.o $(BENCH_OBJS))
# Compile executable objects
$(BUILDDIR)/src/%.o: src/%.c | $$(dir $$@)
$(CC) $(SRC_CFLAGS) -c $< -o $@
# Link target executables
$(TARGETS): | $(LIBS)
$(CC) $(SRC_LDFLAGS) $^ $(SRC_LDLIBS) -o $@
# Install
install-src: src
install -m 0755 $(TARGETS) $(PREFIX)/bin
clean-src:
rm -rf $(BUILDDIR)/src $(TARGETS)
.PHONY: src src-tests src-tests

View file

@ -96,7 +96,7 @@ static void * send_loop(void *ctx)
sendd.started = true;
/* Initialize memory */
ret = pool_init_mmap(&sendd.pool, SAMPLE_LEN(DEFAULT_VALUES), node->vectorize);
ret = pool_init(&sendd.pool, SAMPLE_LEN(DEFAULT_VALUES), node->vectorize, &memtype_hugepage);
if (ret < 0)
error("Failed to allocate memory for receive pool.");
@ -140,7 +140,7 @@ static void * recv_loop(void *ctx)
recvv.started = true;
/* Initialize memory */
ret = pool_init_mmap(&recvv.pool, SAMPLE_LEN(DEFAULT_VALUES), node->vectorize);
ret = pool_init(&recvv.pool, SAMPLE_LEN(DEFAULT_VALUES), node->vectorize, &memtype_hugepage);
if (ret < 0)
error("Failed to allocate memory for receive pool.");

34
tests/Makefile.gcov.inc Normal file
View file

@ -0,0 +1,34 @@
COVERAGE_TESTS = $(BUILDDIR)/testsuite
COVERAGE_OBJS = $(LIB_OBJS) $(SRC_OBJS)
GCDAS = $(COVERAGE_OBJS:.o=.gcda)
GCNOS = $(COVERAGE_OBJS:.o=.gcno)
GCOVR_OPTS = --exclude ^include --root . --sort-percentage --print-summary
coverage: $(BUILDDIR)/coverage/index.html $(BUILDDIR)/coverage.xml $(BUILDDIR)/coverage.txt
$(BUILDDIR)/coverage.txt: $(addsuffix .gcdas,$(COVERAGE_TESTS)) | $$(dir $$@)
gcovr $(GCOVR_OPTS) -o $@
$(BUILDDIR)/coverage.xml: $(addsuffix .gcdas,$(COVERAGE_TESTS)) | $$(dir $$@)
gcovr $(GCOVR_OPTS) --xml --xml-pretty -o $@
$(BUILDDIR)/coverage/index.html: $(addsuffix .gcdas,$(COVERAGE_TESTS)) | $$(dir $$@)
gcovr $(GCOVR_OPTS) --html --html-details -o $@
# This is an intermediate target. It is used to run the test only once for all gcovr rules.
%.gcdas: %
@echo "Delete previous coverage information"
rm -f $(GCDAS)
@echo "Run $< for collecting coverage information (.gcda)"
$^
clean-coverage:
rm -rf $(BUILDDIR)/coverage $(BUILDDIR)/coverage.txt $(BUILDDIR)/coverage.xml
rm -f $(GCDAS)
install-coverage:
.INTERMEDIATE: $(addsuffix .gcdas,$(COVERAGE_TESTS))
.PHONY: coverage gcda clean-coverage install-coverage

28
tests/Makefile.inc Normal file
View file

@ -0,0 +1,28 @@
TEST_SRCS = $(wildcard tests/*.c)
TEST_OBJS = $(patsubst %.c,$(BUILDDIR)/%.o,$(TEST_SRCS))
TEST_CFLAGS = $(CFLAGS)
TEST_LDFLAGS = $(LDFLAGS) -Wl,-rpath,'$$ORIGIN'
TEST_LDLIBS = $(LDLIBS) -lcriterion -lvillas
tests: $(BUILDDIR)/testsuite
# Compile
$(BUILDDIR)/tests/%.o: tests/%.c | $$(dir $$@)
$(CC) $(TEST_CFLAGS) -c $< -o $@
# Link
$(BUILDDIR)/testsuite: $(TEST_OBJS) | $(LIBS)
$(CC) $(TEST_LDFLAGS) $(TEST_LDLIBS) $^ -o $@
ifdef COVERAGE
-include tests/Makefile.gcov.inc
endif
# Tests are not installed
install-tests:
clean-tests:
rm -rf $(BUILDDIR)/tests $(BUILDDIR)/testsuite
.PHONY: tests install-tests clean-tests

35
tests/hist.c Normal file
View file

@ -0,0 +1,35 @@
/** Unit tests for histogram
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <criterion/criterion.h>
#include "hist.h"
#include "utils.h"
const double test_data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
/* Histogram of test_data with 200 buckets between -100 and 100 */
const int hist_result[] = {};
Test(hist, simple) {
struct hist h;
hist_create(&h, -100, 100, 1);
for (int i = 0; i < ARRAY_LEN(test_data); i++)
hist_put(&h, test_data[i]);
cr_assert_float_eq(hist_mean(&h), 5.5, 1e-6);
cr_assert_float_eq(hist_var(&h), 9.1666, 1e-3,);
cr_assert_float_eq(hist_stddev(&h), 3.027650, 1e-6);
// for (int i = 0; i < ARRAY_LEN(hist_result); i++)
// cr_assert_eq()
hist_destroy(&h);
}

140
tests/list.c Normal file
View file

@ -0,0 +1,140 @@
/** Unit tests for array-based list
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <stdint.h>
#include <string.h>
#include <criterion/criterion.h>
#include "utils.h"
#include "list.h"
static char *nouns[] = { "time", "person", "year", "way", "day", "thing", "man", "world", "life", "hand", "part", "child", "eye", "woman", "place", "work", "week", "case", "point", "government", "company", "number", "group", "problem", "fact" };
struct data {
char *tag;
int data;
};
Test(list, list_lookup)
{
struct list l;
list_init(&l);
for (int i = 0; i < ARRAY_LEN(nouns); i++) {
struct data *d = malloc(sizeof(struct data));
d->tag = nouns[i];
d->data = i;
list_push(&l, d);
}
struct data *found = list_lookup(&l, "woman");
cr_assert_eq(found->data, 13);
list_destroy(&l, NULL, true);
}
Test(list, list_search)
{
struct list l;
list_init(&l);
/* Fill list */
for (int i = 0; i < ARRAY_LEN(nouns); i++)
list_push(&l, nouns[i]);
/* Declare on stack! */
char positive[] = "woman";
char negative[] = "dinosaurrier";
char *found = (char *) list_search(&l, (cmp_cb_t) strcmp, positive);
cr_assert_not_null(found);
cr_assert_eq(found, nouns[13]);
cr_assert_str_eq(found, positive);
char *not_found = (char *) list_search(&l, (cmp_cb_t) strcmp, negative);
cr_assert_null(not_found);
list_destroy(&l, NULL, false);
}
struct content {
int destroyed;
};
static void dtor(void *ptr)
{
struct content *elm = (struct content *) ptr;
elm->destroyed = 1;
}
Test(list, destructor)
{
struct list l;
struct content elm = { .destroyed = 0 };
list_init(&l);
list_push(&l, &elm);
cr_assert_eq(list_length(&l), 1);
list_destroy(&l, dtor, false);
cr_assert_eq(elm.destroyed, 1);
}
static int compare(const void *a, const void *b) {
return b - a;
}
Test(list, basics)
{
intptr_t i;
struct list l;
list_init(&l);
for (i = 0; i < 100; i++) {
cr_assert_eq(list_length(&l), i);
list_push(&l, (void *) i);
}
cr_assert_eq(list_at(&l, 555), NULL);
cr_assert_eq(list_last(&l), (void *) 99);
cr_assert_eq(list_first(&l), NULL);
i = 0;
list_foreach (void *j, &l)
cr_assert_eq(j, (void *) i++);
list_sort(&l, compare); /* Reverse list */
i = 99;
list_foreach (void *j, &l) {
cr_assert_eq(j, (void *) i, "Is %p, expected %p", i, j);
i--;
}
cr_assert(list_contains(&l, (void *) 55));
list_remove(&l, (void *) 55);
cr_assert(!list_contains(&l, (void *) 55));
list_destroy(&l, NULL, false);
cr_assert_eq(list_length(&l), -1, "List not properly destroyed: l.length = %zd", l.length);
}

25
tests/queue.c Normal file
View file

@ -0,0 +1,25 @@
/** Unit tests for MPMC queue
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <criterion/criterion.h>
#include "queue.h"
Test(queue, singleThreaded)
{
/* struct queue q;
queue_init(&q, 100, &memtype_heap);
srand(1337);
for (int i = 0; i < 100; i++)
queue_push(&q, &)
queue_destroy(&q);*/
}

124
tests/timing.c Normal file
View file

@ -0,0 +1,124 @@
/** Unit tests for time related utlities
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
*********************************************************************************/
#include <unistd.h>
#include <criterion/criterion.h>
#include "timing.h"
Test(timing, time_now)
{
struct timespec now1 = time_now();
struct timespec now2 = time_now();
double delta = time_delta(&now1, &now2);
cr_assert_float_eq(delta, 0, 1e-5, "time_now() shows large variance!");
cr_assert_gt(delta, 0, "time_now() was reordered!");
}
Test(timing, time_diff)
{
struct timespec ts1 = { .tv_sec = 0, .tv_nsec = 1}; /* Value doesnt matter */
struct timespec ts2 = { .tv_sec = 1, .tv_nsec = 0}; /* Overflow in nano seconds! */
struct timespec ts3 = time_diff(&ts1, &ts2);
/* ts4 == ts2? */
cr_assert_eq(ts3.tv_sec, 0);
cr_assert_eq(ts3.tv_nsec, 999999999);
}
Test(timing, time_add)
{
struct timespec ts1 = { .tv_sec = 1, .tv_nsec = 999999999}; /* Value doesnt matter */
struct timespec ts2 = { .tv_sec = 1, .tv_nsec = 1}; /* Overflow in nano seconds! */
struct timespec ts3 = time_add(&ts1, &ts2);
/* ts4 == ts2? */
cr_assert_eq(ts3.tv_sec, 3);
cr_assert_eq(ts3.tv_nsec, 0);
}
Test(timing, time_delta)
{
struct timespec ts1 = { .tv_sec = 1, .tv_nsec = 123}; /* Value doesnt matter */
struct timespec ts2 = { .tv_sec = 5, .tv_nsec = 246}; /* Overflow in nano seconds! */
double delta = time_delta(&ts1, &ts2);
cr_assert_float_eq(delta, 4 + 123e-9, 1e-9);
}
Test(timing, time_from_double)
{
double ref = 1234.56789;
struct timespec ts = time_from_double(ref);
cr_assert_eq(ts.tv_sec, 1234);
cr_assert_eq(ts.tv_nsec, 567890000);
}
Test(timing, time_to_from_double)
{
double ref = 1234.56789;
struct timespec ts = time_from_double(ref);
double dbl = time_to_double(&ts);
cr_assert_float_eq(dbl, ref, 1e-9);
}
Test(timing, timerfd_create_rate)
{
struct timespec start, end;
double rate = 5, waited;
int tfd = timerfd_create_rate(rate);
cr_assert(tfd > 0);
for (int i = 0; i < 10; i++) {
start = time_now();
timerfd_wait(tfd);
end = time_now();
waited = time_delta(&start, &end);
cr_assert_float_eq(waited, 1.0 / rate, 10e-3, "We slept for %f instead of %f secs in round %d", waited, 1.0 / rate, i);
}
close(tfd);
}
Test(timing, timerfd_wait_until)
{
int tfd = timerfd_create(CLOCK_REALTIME, 0);
cr_assert(tfd > 0);
double waitfor = 0.423456789;
struct timespec start = time_now();
struct timespec diff = time_from_double(waitfor);
struct timespec future = time_add(&start, &diff);
timerfd_wait_until(tfd, &future);
struct timespec end = time_now();
double waited = time_delta(&start, &end);
cr_assert_float_eq(waited, waitfor, 5e-3, "We slept for %f instead of %f secs", waited, waitfor);
close(tfd);
}

35
thirdparty/Makefile vendored
View file

@ -1,35 +0,0 @@
DEPS = libconfig-1.5 libnl-3.2.25 doxygen-1.8.10 pciutils-3.4.1 libwebsockets-1.7.5
.PHONY: $(DEPS) all
TMPDIR ?= $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PREFIX ?= /usr/local
# Install all dependencies
all: $(DEPS)
# Download latest doxygen
doxygen-1.8.10:
wget -O- http://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.10.linux.bin.tar.gz | tar xzC $(TMPDIR)
# Install & compile libconfig dependency
libconfig-1.5:
wget -O- http://www.hyperrealm.com/libconfig/libconfig-1.5.tar.gz | tar -xzC $(TMPDIR)
cd $@ && ./configure --prefix=$(PREFIX) --disable-examples && make install
# Install & compile libnl3 dependency
libnl-3.2.25:
wget -O- http://www.infradead.org/~tgr/libnl/files/libnl-3.2.25.tar.gz | tar -xzC $(TMPDIR)
cd $@ && ./configure --prefix=$(PREFIX) --disable-cli && make install
# Install & compile libpci dependency
pciutils-3.4.1:
wget -O- ftp://atrey.karlin.mff.cuni.cz/pub/linux/pci/pciutils-3.4.1.tar.gz | tar -xzC $(TMPDIR)
cd $@ && make clean && make SHARED=yes && make install-lib PREFIX=$(PREFIX)
ln -s $(PREFIX)/lib/libpci.so.3.4.1 $(PREFIX)/lib/libpci.so
# Install & compile libwebsockets dependency
libwebsockets-2.0.2:
mkdir -p $@/build
wget -O- https://github.com/warmcat/libwebsockets/archive/v2.0.2.tar.gz | tar -xzC $(TMPDIR)
cd $@/build && cmake -DCMAKE_INSTALL_PREFIX:PATH=$(PREFIX) .. && make install

39
thirdparty/Makefile.inc vendored Normal file
View file

@ -0,0 +1,39 @@
DEPS_CMAKE = libxil libwebsockets criterion jansson
DEPS_AUTOCONF = libnl libconfig libcurl
DEPS = $(DEPS_CMAKE) $(DEPS_AUTOCONF)
thirdparty:
# Install & compile autotools based projects
$(DEPS_AUTOCONF): | $(BUILDDIR)/thirdparty/$$@/
autoreconf -fi $(SRCDIR)/thirdparty/$@
cd $(BUILDDIR)/thirdparty/$@ && $(SRCDIR)/thirdparty/$@/configure --prefix=$(PREFIX) && make
# Install & compile CMake based projects
$(DEPS_CMAKE): | $(BUILDDIR)/thirdparty/$$@/
cmake -DCMAKE_INSTALL_PREFIX:PATH=$(PREFIX) \
-H$(SRCDIR)/thirdparty/$@ \
-B$(BUILDDIR)/thirdparty/$@
make -C$(BUILDDIR)/thirdparty/$@
$(addprefix install-,$(DEPS)): install-%: %
make -C$(BUILDDIR)/thirdparty/$(@:install-%=%) install
ldconfig
$(addprefix clean-,$(DEPS)):
rm -rf $(BUILDDIR)/thirdparty/$(@:clean-%=%)
install-thirdparty:
clean-thirdparty:
rm -rf $(BUILDDIR)/thirdparty
.PHONY: $(DEPS) thirdparty clean-thirdparty install-thirdparty
# libconfig's build system is currently broken.
# This is a workaround for: https://github.com/hyperrealm/libconfig/issues/53
libconfig: | libconfig-fix
libconfig-fix:
rm -f $(SRCDIR)/thirdparty/libconfig/lib/scanner.[hc]

1
thirdparty/criterion vendored Submodule

@ -0,0 +1 @@
Subproject commit 5b0f2b129046955c004ff56ab2caada5b55ba8e3

1
thirdparty/libconfig vendored Submodule

@ -0,0 +1 @@
Subproject commit 5a06a161afacce6e32491db33781dab2f216df71

1
thirdparty/libnl vendored Submodule

@ -0,0 +1 @@
Subproject commit 7bf2e64654de75578db34b0eab53a3e4d106d65f

1
thirdparty/libwebsockets vendored Submodule

@ -0,0 +1 @@
Subproject commit 0c984014f0a82e184af2ff18f97b45e2cbccd0bd

1
thirdparty/libxil/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
libxil.so

36
thirdparty/libxil/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,36 @@
cmake_minimum_required (VERSION 3.2)
project(libxil C)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -fPIC -I$(SRCDIR)/include/xilinx -Iorig/common_v1_00_a/src -Wno-int-conversion -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
file(GLOB SOURCES src/*.c orig/*/src/*.c)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/xilinx)
add_library(xil SHARED ${SOURCES})
include(FindPkgConfig QUIET)
if(PKG_CONFIG_FOUND)
# convert lists of link libraries into -lstdc++ -lm etc..
foreach(LIB ${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES} ${PLATFORM_LIBS})
set(PRIVATE_LIBS "${PRIVATE_LIBS} -l${LIB}")
endforeach()
# Produce a pkg-config file for linking against the shared lib
configure_file("libxil.pc.in" "libxil.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libxil.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
endif()
install(TARGETS xil DESTINATION lib)
file(GLOB HEADERS include/xilinx/*.h)
# Make sure we dont install symlinks
set (HEADERS_RESOLVED "")
foreach (HEADER ${HEADERS})
get_filename_component(HEADER_RESOLVED "${HEADER}" REALPATH)
list (APPEND HEADERS_RESOLVED "${HEADER_RESOLVED}")
endforeach()
install(FILES ${HEADERS_RESOLVED} DESTINATION include/xilinx)

10
thirdparty/libxil/libxil.pc.in vendored Normal file
View file

@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib
Name: xil
Description: Xilinx standalone drivers
Version: 1.0.0
Cflags: -I${includedir}
Libs: -L${libdir} -lxil

View file

Before

Width:  |  Height:  |  Size: 746 B

After

Width:  |  Height:  |  Size: 746 B

Some files were not shown because too many files have changed in this diff Show more