Commit 82e09058 authored by Jaco Hofmann's avatar Jaco Hofmann
Browse files

Adds support for MPSoC and specifially the ZCU102 dev board

parent 571a4137
......@@ -30,6 +30,7 @@ namespace eval tapasco {
namespace export createRegisterSlice
namespace export createIntCtrl
namespace export createInterconnect
namespace export createSmartConnect
namespace export createMIG
namespace export createOLEDController
namespace export createPCIeBridge
......@@ -69,10 +70,10 @@ namespace eval tapasco {
return "2017.1"
}
# Instantiates an AXI4 Interconnect IP.
# Instantiates an AXI Interconnect IP.
# @param name Name of the instance.
# @param no_slaves Number of AXI4 Slave interfaces.
# @param no_masters Number of AXI4 Master interfaces.
# @param no_slaves Number of AXI Slave interfaces.
# @param no_masters Number of AXI Master interfaces.
# @return bd_cell of the instance.
proc createInterconnect {name no_slaves no_masters} {
variable stdcomps
......@@ -93,6 +94,23 @@ namespace eval tapasco {
return $ic
}
# Instantiates an AXI4 SmartConnect IP.
# @param name Name of the instance.
# @param no_slaves Number of AXI4 Slave interfaces.
# @param no_masters Number of AXI4 Master interfaces.
# @param reset If 1 a dedicated reset port is added to the block.
# @return bd_cell of the instance.
proc createSmartConnect {name no_slaves no_masters {reset 0}} {
variable stdcomps
puts "Creating AXI SmartConnect $name with $no_slaves slaves and $no_masters masters..."
puts " VLNV: [dict get $stdcomps axi_ic vlnv]"
set ic [create_bd_cell -type ip -vlnv [dict get $stdcomps axi_sc vlnv] $name]
set props [list CONFIG.NUM_SI $no_slaves CONFIG.NUM_MI $no_masters CONFIG.HAS_ARESETN $reset]
set_property -dict $props $ic
return $ic
}
# Instantiates a Zynq-7000 Processing System IP core.
# @param name Name of the instance (default: ps7).
# @param preset Name of board preset to apply (default: tapasco::get_board_preset).
......@@ -115,6 +133,24 @@ namespace eval tapasco {
return $ps
}
# Instantiates a Zynq Ultra Scale MPSoC Processing System IP core.
# @param name Name of the instance (default: ps7).
# @param preset Name of board preset to apply (default: tapasco::get_board_preset).
# @param freq_mhz FCLK_0 frequency in MHz (default: tapasco::get_design_frequency).
# @return bd_cell of the instance.
proc createMPSoCPS {{name mpsoc} {preset [tapasco::get_board_preset]} {freq_mhz [tapasco::get_design_frequency]}} {
variable stdcomps
puts "Creating Zynq-Ultra Scale series IP core ..."
puts " VLNV: [dict get $stdcomps psmpsoc vlnv]"
puts " Preset: $preset"
puts " PL0 : $freq_mhz"
set ps [create_bd_cell -type ip -vlnv [dict get $stdcomps psmpsoc vlnv] $name]
apply_bd_automation -rule xilinx.com:bd_rule:zynq_ultra_ps_e -config {apply_board_preset "1" Master "Disable" Slave "Disable" } $ps
set_property -dict [list CONFIG.PSU__CRL_APB__PL0_REF_CTRL__FREQMHZ $freq_mhz] $ps
return $ps
}
# Instantiates a Zynq-7000 Processing System IP BFM simulation core.
# @param name Name of the instance (default: ps7).
# @param preset Name of board preset to apply (default: tapasco::get_board_preset).
......@@ -787,7 +823,7 @@ namespace eval tapasco {
# @param freqs list of name frequency (MHz) pairs, e.g., [list design 100 memory 250]
# @param name Name of the subsystem group
# @return Subsystem group
proc create_subsystem_clocks_and_resets {{freqs {}} {name ClockResets}} {
proc create_subsystem_clocks_and_resets {{freqs {}} {name ClockResets} {sys_clock_name sys_diff_clock}} {
if {$freqs == {}} { set freqs [get_frequencies] }
puts "Creating clock and reset subsystem ..."
puts " frequencies: $freqs"
......@@ -798,10 +834,10 @@ namespace eval tapasco {
set reset_in [create_bd_pin -dir I -type rst "reset_in"]
set clk [createClockingWizard "clk_wiz"]
set_property -dict [list CONFIG.USE_LOCKED {false} CONFIG.USE_RESET {false} CONFIG.NUM_OUT_CLKS [expr "[llength $freqs] / 2"]] $clk
set clk_mode "sys_diff_clock"
set clk_mode "$sys_clock_name"
if {[catch {set_property CONFIG.CLK_IN1_BOARD_INTERFACE {sys_diff_clock} $clk}]} {
puts " sys_diff_clock is not supported, trying sys_clock instead"
if {[catch {set_property CONFIG.CLK_IN1_BOARD_INTERFACE $sys_clock_name $clk}]} {
puts " $sys_clock_name is not supported, trying sys_clock instead"
set clk_mode "sys_clock"
}
# check if external port already exists, re-use
......@@ -812,7 +848,7 @@ namespace eval tapasco {
set_property -dict [list CONFIG.PRIMITIVE {PLL} CONFIG.USE_MIN_POWER {true}] $clk
} {
# apply board automation to create top-level port
if {$clk_mode == "sys_diff_clock"} {
if {$clk_mode == "$sys_clock_name"} {
set cport [get_bd_intf_pins -of_objects $clk]
} {
set cport [get_bd_pins -filter {DIR == I} -of_objects $clk]
......
# create a dictionary of compatible VLNVs
dict set stdcomps axi_ic vlnv "xilinx.com:ip:axi_interconnect:2.1"
dict set stdcomps axi_sc vlnv "xilinx.com:ip:smartconnect:1.0"
dict set stdcomps ps vlnv "xilinx.com:ip:processing_system7:5.5"
dict set stdcomps psmpsoc vlnv "xilinx.com:ip:zynq_ultra_ps_e:3.0"
dict set stdcomps ps_bfm vlnv "xilinx.com:ip:processing_system7_bfm:2.0"
dict set stdcomps axi_irqc vlnv "xilinx.com:ip:axi_intc:4.1"
dict set stdcomps axi_cache vlnv "xilinx.com:ip:system_cache:3.0"
......
......@@ -23,5 +23,5 @@
# device families for the IP cores.
# @authors J. Korinth (jk@esa.cs.tu-darmstadt.de
#
sed -i 's/set_property supported_families .*$/set_property supported_families [list zynq Pre-Production virtex7 Pre-Production kintex7 Pre-Production artix7 Pre-Production] $core/g' run_ippack.tcl
sed -i 's/set_property supported_families .*$/set_property supported_families [list zynq Pre-Production virtex7 Pre-Production kintex7 Pre-Production artix7 Pre-Production zynquplus Pre-Production] $core/g' run_ippack.tcl
sed -i 's/\(set DisplayName.*\)$/\1\nset IPName $DisplayName/g' run_ippack.tcl
cmake_minimum_required(VERSION 2.6)
project(platform-zynq)
set(CMAKE_INSTALL_PREFIX "..")
set(CMAKE_SKIP_RPATH true)
if (NOT EXISTS "$ENV{TAPASCO_HOME}")
message(FATAL_ERROR "Please set TAPASCO_HOME environment variable to root directory of Tapasco")
endif (NOT EXISTS "$ENV{TAPASCO_HOME}")
include ("$ENV{TAPASCO_HOME}/cmake/Tapasco.cmake")
set(SRCDIR "src")
set(CMNDIR "../common")
set(GCMNDIR "${TAPASCO_HOME}/common")
set(COMPFLAGS "-Wall -Werror -g -O3 -std=gnu11")
set(CMNSRCS "${CMNDIR}/src/platform_logging.c"
"${CMNDIR}/src/platform_errors.c"
"${SRCDIR}/platform_address_map.c"
"${CMNDIR}/src/platform_version.c"
"${GCMNDIR}/src/gen_queue.c")
set(SRCS "${SRCDIR}/platform_zynq.c")
set(PLATFORM_LIBS_DIR "${TAPASCO_HOME}/platform/lib")
include_directories("." "include" "${CMNDIR}/include" "${GCMNDIR}/include")
set_source_files_properties(${CMNSRCS} ${SRCS} PROPERTIES COMPILE_FLAGS ${COMPFLAGS})
add_library(platform SHARED ${CMNSRCS} ${SRCS})
add_library(platform-static STATIC ${CMNSRCS} ${SRCS})
set_target_properties(platform-static PROPERTIES OUTPUT_NAME platform)
set_target_properties(platform PROPERTIES COMPILE_FLAGS ${COMPFLAGS})
set_target_properties(platform-static PROPERTIES COMPILE_FLAGS ${COMPFLAGS})
if ("armv71" STREQUAL ${TAPASCO_TARGET})
target_link_libraries(platform atomic)
target_link_libraries(platform-static atomic)
endif ()
install(TARGETS platform platform-static
LIBRARY DESTINATION "lib/${TAPASCO_TARGET}"
ARCHIVE DESTINATION "lib/${TAPASCO_TARGET}/static")
get_filename_component(LIBSDIR "lib" REALPATH)
add_custom_command(OUTPUT ${PLATFORM_LIBS_DIR}
COMMAND ln;-fs;${LIBSDIR};${PLATFORM_LIBS_DIR})
add_custom_target(install_libs ALL DEPENDS platform platform-static ${PLATFORM_LIBS_DIR})
#!/usr/bin/python
import sys
import subprocess
clean = len(sys.argv) > 1 and sys.argv[1] == "clean"
debug = len(sys.argv) > 1 and (sys.argv[1] == "debug" or sys.argv[1] == "driver_debug")
driver_debug = len(sys.argv) > 1 and sys.argv[1] == "driver_debug"
moddir = "$TAPASCO_HOME/platform/zynq/module"
pdir = "$TAPASCO_HOME/platform/zynq/build"
adir = "$TAPASCO_HOME/arch/axi4mm/build"
if clean:
subprocess.call(["rm -rf " + pdir], shell=True)
subprocess.call(["rm -rf " + adir], shell=True)
subprocess.call(["cd " + moddir + " && make clean"], shell=True)
else:
if debug:
print("Building debug mode libraries...")
else:
print("Building release mode libraries, pass 'debug' as first argument to build debug libs...")
subprocess.call(["cd " + moddir + " && make " + ("" if driver_debug else "release ")], shell=True)
subprocess.call(["mkdir -p " + pdir + " && cd " + pdir + " && cmake " + ("" if debug else "-DCMAKE_BUILD_TYPE=Release") + " .. && make && make install"], shell=True)
subprocess.call(["mkdir -p " + adir + " && cd " + adir + " && cmake " + ("" if debug else "-DCMAKE_BUILD_TYPE=Release") + " .. && make && make install"], shell=True)
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"
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
.PHONY: all clean
all:
make -C $(LINUX_HOME) M=$(PWD) modules
release:
KCPPFLAGS="-DNDEBUG -O3" make -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");