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:
commit
7e5f656567
402 changed files with 1852 additions and 1126 deletions
|
@ -1 +1,4 @@
|
|||
*
|
||||
!thirdparty/libxil/
|
||||
!thirdparty/criterion/
|
||||
!thirdparty/libwebsockets/
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,10 +1,2 @@
|
|||
*.o
|
||||
*.d
|
||||
*.so
|
||||
/build/
|
||||
*~
|
||||
|
||||
/node
|
||||
/pipe
|
||||
/test
|
||||
/signal
|
||||
/fpga
|
||||
|
|
121
.gitlab-ci.yml
121
.gitlab-ci.yml
|
@ -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
21
.gitmodules
vendored
|
@ -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
|
42
Dockerfile
42
Dockerfile
|
@ -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
|
||||
|
|
2
Doxyfile
2
Doxyfile
|
@ -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
243
Makefile
|
@ -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))
|
||||
|
|
|
@ -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. */
|
||||
};
|
||||
|
|
|
@ -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_ */
|
|
@ -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 */
|
||||
|
|
|
@ -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
53
include/villas/memory.h
Normal 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_ */
|
|
@ -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. */
|
||||
|
|
|
@ -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? */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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
98
lib/Makefile.inc
Normal 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
|
|
@ -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;
|
||||
|
|
283
lib/kernel/pci.c
283
lib/kernel/pci.c
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
130
lib/lstack.c
130
lib/lstack.c
|
@ -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
97
lib/memory.c
Normal 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
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
66
lib/path.c
66
lib/path.c
|
@ -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);
|
||||
|
|
57
lib/pool.c
57
lib/pool.c
|
@ -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);
|
||||
}
|
202
lib/queue.c
202
lib/queue.c
|
@ -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;
|
||||
}
|
16
lib/sample.c
16
lib/sample.c
|
@ -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);
|
||||
|
|
|
@ -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
27
plugins/Makefile.inc
Normal 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
49
src/Makefile.inc
Normal 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
|
|
@ -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
34
tests/Makefile.gcov.inc
Normal 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
28
tests/Makefile.inc
Normal 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
35
tests/hist.c
Normal 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
140
tests/list.c
Normal 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
25
tests/queue.c
Normal 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
124
tests/timing.c
Normal 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
35
thirdparty/Makefile
vendored
|
@ -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
39
thirdparty/Makefile.inc
vendored
Normal 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
1
thirdparty/criterion
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5b0f2b129046955c004ff56ab2caada5b55ba8e3
|
1
thirdparty/libconfig
vendored
Submodule
1
thirdparty/libconfig
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5a06a161afacce6e32491db33781dab2f216df71
|
1
thirdparty/libnl
vendored
Submodule
1
thirdparty/libnl
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 7bf2e64654de75578db34b0eab53a3e4d106d65f
|
1
thirdparty/libwebsockets
vendored
Submodule
1
thirdparty/libwebsockets
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 0c984014f0a82e184af2ff18f97b45e2cbccd0bd
|
1
thirdparty/libxil/.gitignore
vendored
Normal file
1
thirdparty/libxil/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
libxil.so
|
36
thirdparty/libxil/CMakeLists.txt
vendored
Normal file
36
thirdparty/libxil/CMakeLists.txt
vendored
Normal 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
10
thirdparty/libxil/libxil.pc.in
vendored
Normal 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
|
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
Loading…
Add table
Reference in a new issue