Commit 55cf4382 authored by Jens Korinth's avatar Jens Korinth
Browse files

tlkm: Implement mmap support

* devices can set their mmap field to provide mmap-support
* reduced log chatter
* removed redundant struct tlkm_dev_cmd
* fixed bug in general read/write via ioctls
parent addf4830
KERNEL=="tapasco_platform*" OWNER="tapasco" GROUP="tapasco"
KERNEL=="tapasco_platform*" RUN+="/bin/chown tapasco:tapasco /sys/class/misc/tapasco_platform_zynq_tapasco_status/pending_ev" RUN+="/bin/chown tapasco:tapasco /sys/class/misc/tapasco_platform_zynq_tapasco_status/total_ev" RUN+="/bin/chown tapasco:tapasco /sys/class/misc/tapasco_platform_zynq_tapasco_status/wait"
SUBSYSTEM=="tapasco_memory" OWNER="tapasco" GROUP="tapasco"
PLATFORM_API_TAPASCO_STATUS_BASE?=0x77770000UL
LINUX_HOME ?= /lib/modules/$(shell uname -r)/build
ifeq ($(ARCH), arm)
CROSS_COMPILE ?= arm-unknown-linux-gnueabihf-
endif
obj-m += tapasco-platform-zynq.o
tapasco-platform-zynq-objs := zynq_module.o zynq_device.o zynq_dmamgmt.o zynq_irq.o zynq_ioctl.o zynq_async.o
CPPFLAGS=-DPLATFORM_API_TAPASCO_STATUS_BASE=$(PLATFORM_API_TAPASCO_STATUS_BASE)
CPPFLAGS+=-I$(TAPASCO_HOME)/platform/common/include
.PHONY: all clean
all:
make KCPPFLAGS="$(CPPFLAGS)" -C $(LINUX_HOME) M=$(PWD) modules
release:
make KCPPFLAGS+="$(CPPFLAGS) -DNDEBUG -O3" -C $(LINUX_HOME) M=$(PWD) modules
clean:
make -C $(LINUX_HOME) M=$(PWD) clean
rm -rf test-alloc-dealloc
#!/bin/bash
#
# Copyright (C) 2014 Jens Korinth, TU Darmstadt
#
# This file is part of Tapasco (TPC).
#
# Tapasco is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Tapasco is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
#
show_usage() {
cat << EOF
Usage: ${0##*/} [-v|--verbose] [--d|--drv-reload] BITSTREAM
Program FPGA via /dev/xdevcfg.
-v enable verbose output
-d reload device driver
EOF
}
# init vars
BITSTREAM=""
VERBOSE=0
RELOADD=1
OPTIND=1
while getopts vd opt; do
case $opt in
v)
VERBOSE=1
;;
d)
RELOADD=1
;;
*)
echo "unknown option: $opt"
show_usage
exit 1
;;
esac
done
shift "$((OPTIND-1))"
BITSTREAM="$1"
if [ -n $BITSTREAM ] && [[ $BITSTREAM == *.bit ]] && [[ -e $BITSTREAM ]]
then
pushd $TAPASCO_HOME/platform/zynq/module &> /dev/null
if [[ `lsmod | grep tapasco | wc -l` -eq 1 ]]; then
sudo ./unload.sh
fi
popd &> /dev/null
echo "Loading bitstream $BITSTREAM ..."
sudo sh -c "cat $BITSTREAM > /dev/xdevcfg"
echo "Done!"
pushd $TAPASCO_HOME/platform/zynq/module &> /dev/null
echo "Loading kernel module ..."
sudo ./load.sh
popd &> /dev/null
echo "Done."
else
show_usage
exit 1
fi
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
/*
This file is part of gen_fixed_size_pool.
(C) Copyright 2015 Jens Korinth
gen_fixed_size_pool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
gen_fixed_size_pool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gen_fixed_size_pool. If not, see <http://www.gnu.org/licenses/>.
*/
//! @file gen_stack.h
//! @brief Generic, header-only, lock-free implementation of a fixed size
//! pool of things based on statically allocated array.
//! @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
//!
#ifndef __GEN_FIXED_SIZE_POOL_H__
#define __GEN_FIXED_SIZE_POOL_H__
#ifdef USE_ASSERTIONS
#ifndef __cplusplus
#include <assert.h>
#else
#include <cassert>
#endif
#else /* !USE_ASSERTIONS */
#define assert(...)
#endif
/** Index type: external id of pool element. */
typedef uint32_t fsp_idx_t;
#define INVALID_IDX ((fsp_idx_t)(-1))
// define a pool: PRE = prefix, SZ = size, T = type, IF = initializer function
#define MAKE_FIXED_SIZE_POOL(PRE, SZ, T, IF) \
struct PRE##_fsp_t { \
T elems[SZ]; \
int locked[SZ]; \
int refcnt[SZ]; \
fsp_idx_t curr; \
}; \
\
static inline void PRE##_fsp_init(struct PRE##_fsp_t *fsp) \
{ \
int i; \
memset(fsp, 0, sizeof(*fsp)); \
for (i = 0; i < SZ; ++i) { \
__atomic_clear(&fsp->locked[i], __ATOMIC_SEQ_CST); \
IF(&fsp->elems[i], i); \
} \
} \
\
static inline fsp_idx_t PRE##_fsp_get(struct PRE##_fsp_t *fsp) \
{ \
fsp_idx_t ret; \
do { ret = __atomic_fetch_add(&fsp->curr, 1, __ATOMIC_SEQ_CST); } \
while (ret < SZ && __atomic_test_and_set(&fsp->locked[ret], __ATOMIC_SEQ_CST)); \
if (ret < SZ) { \
assert(__atomic_add_fetch(&fsp->refcnt[ret], 1, __ATOMIC_SEQ_CST) < 2); \
return ret; \
} \
return INVALID_IDX; \
} \
\
static inline void PRE##_fsp_put(struct PRE##_fsp_t *fsp, fsp_idx_t const idx) \
{ \
if (idx < SZ) { \
fsp_idx_t old; \
__atomic_sub_fetch(&fsp->refcnt[idx], 1, __ATOMIC_SEQ_CST); \
__atomic_clear(&fsp->locked[idx], __ATOMIC_SEQ_CST); \
do { old = fsp->curr; } \
while (idx < old && ! __atomic_compare_exchange(&fsp->curr, &old, \
&idx, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); \
} \
} \
#endif /* __GEN_FIXED_SIZE_POOL_H__ */
#!/bin/bash
#
# Copyright (C) 2014 Jens Korinth, TU Darmstadt
#
# This file is part of Tapasco (TPC).
#
# Tapasco is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Tapasco is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
#
# Helper script to load zynq TPC Platform device driver.
# Must be run as superuser.
# insmod tapasco-platform-zynq.ko logging_level=132
# insmod tapasco-platform-zynq.ko logging_level=0x7fffffff
insmod tapasco-platform-zynq.ko
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
//! @file logging.h
//! @brief Kernel logging helper functions:
//! Declares module parameter 'logging_level' which determines the
//! amount of printk output; errors are signaled using level 0 and
//! are always output, warnings are level 1, the other levels are
//! bitfield indicators and can be defined by the user.
//! @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
//!
#ifndef __TAPASCO_PLATFORM_LOGGING_H__
#define __TAPASCO_PLATFORM_LOGGING_H__
#include <linux/printk.h>
#ifdef LOGGING_MODULE_INCLUDE
unsigned int logging_level = 0x7fffffff;
module_param(logging_level, uint, S_IRUGO|S_IWUSR|S_IWGRP);
#else
extern int logging_level;
#endif
#ifndef NDEBUG
#define ERR(msg, ...) tapasco_platform_log(0, msg, ##__VA_ARGS__)
#define WRN(msg, ...) tapasco_platform_log(1, msg, ##__VA_ARGS__)
#define LOG(l, msg, ...) tapasco_platform_log((l), msg, ##__VA_ARGS__)
#define tapasco_platform_log(level, fmt, ...) do { \
switch ((int)level) { \
case 0: \
printk(KERN_ERR "tapasco-platform-zynq: [%s] " \
fmt "\n", __func__, \
##__VA_ARGS__); \
break; \
case 1: \
printk(KERN_WARNING "tapasco-platform-zynq: [%s] " \
fmt "\n", __func__, \
##__VA_ARGS__); \
break; \
default: \
if (logging_level & level) \
printk(KERN_NOTICE "tapasco_platform_zynq: [%s] " \
fmt "\n", __func__, \
##__VA_ARGS__); \
break; \
} \
} while(0)
#else
/* only errors and warnings, no other messages */
#define ERR(fmt, ...) printk(KERN_ERR "tapasco-platform-zynq: [%s] " \
fmt "\n", __func__, \
##__VA_ARGS__)
#define WRN(fmt, ...) printk(KERN_WARNING "tapasco-platform-zynq: [%s] " \
fmt "\n", __func__, \
##__VA_ARGS__)
#define LOG(l, msg, ...)
#define tapasco_platform_log(level, fmt, ...)
#endif
#endif /* __TAPASCO_PLATFORM_LOGGING_H__ */
cmake_minimum_required(VERSION 2.7)
project(platform-zynq-module-tests)
include_directories("..")
find_library(NCURSESLIB ncurses)
add_executable(stress-alloc stress-alloc.c)
target_link_libraries(stress-alloc pthread m)
add_executable(stress-ioctl stress-ioctl.c)
target_link_libraries(stress-ioctl pthread ncurses)
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#define UPPER_BND (25)
static char const *const fn[] = {
"/sys/class/misc/tapasco_platform_zynq_gp0/alloc",
"/sys/class/misc/tapasco_platform_zynq_gp0/dealloc",
"/sys/class/misc/tapasco_platform_zynq_gp0/bufferid",
};
static int fd[sizeof(fn) / sizeof(*fn)] = { -1 };
static int stop = 0;
void *stress(void *p)
{
ssize_t res, dma;
unsigned long runs = (unsigned long)p;
printf("Starting %lu runs ...\n", runs);
while (runs && ! stop) {
size_t const sz = pow(2, (rand() % UPPER_BND));
dma = write(fd[0], &sz, sizeof(sz));
if (dma < 0) {
stop = 1;
fprintf(stderr, "error during allocation of size 0x%zu byte: %s\n",
sz, strerror(errno));
return NULL;
}
usleep(rand() % 1000);
res = write(fd[2], &res, sizeof(res));
if (res < 0) {
stop = 1;
fprintf(stderr, "could not find buffer for address 0x%08lx: %s\n",
sz, (unsigned long)dma, strerror(errno));
return NULL;
}
usleep(rand() % 1000);
res = write(fd[1], &dma, sizeof(dma));
if (res < 0) {
stop = 1;
fprintf(stderr, "could not deallocate 0x%08lx: %s\n",
(unsigned long)dma, strerror(errno));
return NULL;
}
--runs;
}
return NULL;
}
int main(int argc, char **argv)
{
int i, t;
long const thread_count = argc > 1 ? strtol(argv[1], NULL, 0) : sysconf(_SC_NPROCESSORS_CONF);
unsigned long const runs = argc > 2 ? strtoul(argv[2], NULL, 0) : 10000;
pthread_t threads[thread_count];
srand(time(NULL));
for (i = 0; i < sizeof(fn) / sizeof(*fn); ++i) {
fd[i] = open(fn[i], O_WRONLY);
if (fd[i] == -1) {
fprintf(stderr, "could not open %s: %s\n", fn[i], strerror(errno));
while (i >= 0)
close(fd[--i]);
exit(EXIT_FAILURE);
}
}
printf("Starting %ld threads ...\n", thread_count);
for (t = 0; t < thread_count; ++t)
pthread_create(&threads[t], NULL, stress, (void *)runs);
for (t = 0; t < thread_count; ++t)
pthread_join(threads[t], NULL);
while (i >= 0)
close(fd[--i]);
if (! stop)
printf("Test successful.\n");
else
fprintf(stderr, "Test failed!\n");
return stop ? EXIT_FAILURE : EXIT_SUCCESS;
}
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
// This file is part of Tapasco (TPC).
//
// Tapasco is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tapasco is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Tapasco. If not, see <http://www.gnu.org/licenses/>.
//
/**
* @file stress-ioctl.c
* @brief
* @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
**/
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <assert.h>
#include <pthread.h>
#include <ncurses.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <zynq_ioctl_cmds.h>
static int fd_ioctl = -1;
static long thrdcnt = 0;
static volatile int stop = 0;
static volatile int finish = 0;
static volatile long runs = 0;
static volatile unsigned long long alloced_bytes = 0ULL;
static volatile unsigned long long freed_bytes = 0ULL;
static volatile unsigned long long copyto_bytes = 0ULL;
static volatile unsigned long long copyfrom_bytes = 0ULL;
static volatile long errors = 0;
static volatile long terrors = 0;
static inline void random_fill(void *p, size_t const len)
{
FILE *fd = fopen("/dev/urandom", "r");
assert(fd);
fread(p, 1, len, fd);
fclose(fd);
}
static inline void print_cmd(volatile struct zynq_ioctl_cmd_t *c)
{
printf("data = 0x%08x, length = %zu, id = %ld, dma = 0x%08x\n",
(unsigned long) c->data, c->length, c->id, c->dma_addr);
}
static inline void copy_check(size_t const *lp)
{
volatile struct zynq_ioctl_cmd_t cmd;
size_t const sz = lp ? *lp : rand() % (1 << 20) & ~0x3;
unsigned char *data1 = malloc(sz);
unsigned char *data2 = malloc(sz);
assert(data1); assert(data2);
memset((void *)&cmd, 0, sizeof(cmd));
random_fill(data1, sz);
cmd.data = data1;
cmd.length = sz;
cmd.id = -1;
if (! ioctl(fd_ioctl, ZYNQ_IOCTL_COPYTO, &cmd)) {
__atomic_fetch_add(&alloced_bytes, sz, __ATOMIC_SEQ_CST);
__atomic_fetch_add(&copyto_bytes, sz, __ATOMIC_SEQ_CST);
cmd.data = data2;
if (! ioctl(fd_ioctl, ZYNQ_IOCTL_COPYFREE, &cmd)) {
__atomic_fetch_add(&freed_bytes, sz, __ATOMIC_SEQ_CST);
__atomic_fetch_add(&copyfrom_bytes, sz, __ATOMIC_SEQ_CST);
} else
__atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST);
} else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST);
if (memcmp(data1, data2, sz) != 0)
__atomic_fetch_add(&terrors, 1, __ATOMIC_SEQ_CST);
free(data1);
free(data2);
}
static inline void alloc_free(size_t const *lp)
{
volatile struct zynq_ioctl_cmd_t cmd;
size_t sz = lp ? *lp : rand() % (1 << 20) & ~0x3;
memset((void *)&cmd, 0, sizeof(cmd));
cmd.id = -1;
cmd.length = sz;
if (! ioctl(fd_ioctl, ZYNQ_IOCTL_ALLOC, &cmd)) {
__atomic_fetch_add(&alloced_bytes, sz, __ATOMIC_SEQ_CST);
if (ioctl(fd_ioctl, ZYNQ_IOCTL_FREE, &cmd))
__atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST);
else
__atomic_fetch_add(&freed_bytes, sz, __ATOMIC_SEQ_CST);
} else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST);
}
static void *stress(void *p)
{
long const which = (long) p;
while (! finish) {
switch (which) {
case 1: alloc_free(NULL); break;
case 2: copy_check(NULL); break;
default: stop = 1; return NULL;
}
__atomic_fetch_add(&runs, 1, __ATOMIC_SEQ_CST);
sched_yield();
}
return NULL;
}
static void init_ncurses()
{
initscr();
noecho();
cbreak();
curs_set(0);
timeout(0);
}
static void exit_ncurses()
{
endwin();
}
static int runtest(long const which)
{
const char *const stre = "--- press any key to exit ---";
struct timespec tv_begin, tv_now;
pthread_t threads[thrdcnt];
char str[255];
int rows, cols;
getmaxyx(stdscr, rows, cols);
fd_ioctl = open("/dev/" ZYNQ_IOCTL_FN, O_RDONLY);
if (fd_ioctl == -1) {
exit_ncurses();
fprintf(stderr, "could not open /dev/%s: %s\n", ZYNQ_IOCTL_FN,
strerror(errno));
exit(EXIT_FAILURE);
}
clear();
mvprintw(rows / 2, (cols - strlen(stre)) / 2, stre);
clock_gettime(CLOCK_MONOTONIC, &tv_begin);
for (long t = 0; t < thrdcnt; ++t)
pthread_create(&threads[t], NULL, stress, (void *)which);
while (getch() == ERR) {
int r = rows / 3 - 3;
snprintf(str, 255, " Passes: %16lu runs ", runs);
mvprintw(r++, (cols - strlen(str)) / 2, str);
snprintf(str, 255, " Errors: %16lu ", errors);
mvprintw(r++, (cols - strlen(str)) / 2, str);
snprintf(str, 255, " T-Errors: %16lu ", terrors);
mvprintw(r++,