diff --git a/gtfpga/main.c b/gtfpga/main.c deleted file mode 100644 index f94d6dd00..000000000 --- a/gtfpga/main.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define SYSFS_PATH "/sys/bus/pci" -#define GTFPGA_DID 0x10f5 -#define GTFPGA_VID 0x8086 - -int main(int argc, char *argv[]) -{ - struct pci_access *pacc; - struct pci_dev *dev; - struct pci_filter filter; - - unsigned int c; - char namebuf[1024], *name; - - pacc = pci_alloc(); /* Get the pci_access structure */ - - pci_init(pacc); /* Initialize the PCI library */ - pci_scan_bus(pacc); /* We want to get the list of devices */ - - pci_filter_init(pacc, &filter); - filter.vendor = GTFPGA_VID; - filter.device = GTFPGA_DID; - - for (dev = pacc->devices; dev; dev = dev->next) { /* Iterate over all devices */ - if (pci_filter_match(&filter, dev)) - break; - } - - pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */ - - c = pci_read_byte(dev, PCI_INTERRUPT_PIN); /* Read config register directly */ - - printf("%04x:%02x:%02x.%d vendor=%04x device=%04x class=%04x irq=%d (pin %d)", - dev->domain, dev->bus, dev->dev, dev->func, dev->vendor_id, dev->device_id, - dev->device_class, dev->irq, c); - - /* Look up and print the full name of the device */ - printf(" (%s)\n", pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id)); - - for (int i = 0; i< 6; i++) { - printf("base_addr[%u] = %#lx, size = %#lx\n", i, - dev->base_addr[i], - dev->size[i]); - } - - int fd = open("/dev/mem", O_RDWR); - if (!fd) - perror("Failed open(): "); - - void *map = mmap(NULL, dev->size[0], PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev->base_addr[0]); - - if (map == MAP_FAILED) - perror("Failed mmap(): "); - - for (int i = 0; i < 100; i++) { - unsigned char *p = map + i; - printf("%02hx ", *p); - - if (i % 32 == 31) - printf("\n"); - } - - getchar(); - - munmap(map, dev->size[0]); - - pci_cleanup(pacc); /* Close everything */ - - return 0; -} diff --git a/server/include/cfg.h b/server/include/cfg.h index 8f097c749..9c1274923 100644 --- a/server/include/cfg.h +++ b/server/include/cfg.h @@ -72,10 +72,25 @@ int config_parse_global(config_setting_t *cfg, struct settings *set); */ int config_parse_path(config_setting_t *cfg, struct list *paths, struct list *nodes); - + +/** Parse an array or single node and checks if they exist in the "nodes" section. + * + * Examples: + * out = [ "sintef", "scedu" ] + * out = "acs" + * + * @param cfg The libconfig object handle for "out". + * @param nodes The nodes will be added to this list. + * @param all This list contains all valid nodes. + */ int config_parse_nodelist(config_setting_t *cfg, struct list *nodes, struct list *all); - +/** Parse an array or single hook function. + * + * Examples: + * hooks = [ "print", "fir" ] + * hooks = "log" + **/ int config_parse_hooks(config_setting_t *cfg, struct list *hooks); /** Parse a single node and add it to the global configuration. @@ -87,40 +102,4 @@ int config_parse_hooks(config_setting_t *cfg, struct list *hooks); */ int config_parse_node(config_setting_t *cfg, struct list *nodes); -/** Parse node connection details for OPAL type - * - * @param cfg A libconfig object pointing to the node. - * @param nodes Add new nodes to this linked list. - * @retval 0 Success. Everything went well. - * @retval <0 Error. Something went wrong. - */ -int config_parse_opal(config_setting_t *cfg, struct node *n); - -/** Parse node connection details for GTFPGA type - * - * @param cfg A libconfig object pointing to the node. - * @param n A pointer to the node structure which should be parsed. - * @retval 0 Success. Everything went well. - * @retval <0 Error. Something went wrong. - */ -int config_parse_gtfpga(config_setting_t *cfg, struct node *n); - -/** Parse node connection details for SOCKET type - * - * @param cfg A libconfig object pointing to the node. - * @param n A pointer to the node structure which should be parsed. - * @retval 0 Success. Everything went well. - * @retval <0 Error. Something went wrong. - */ -int config_parse_socket(config_setting_t *cfg, struct node *n); - -/** Parse network emulator (netem) settings. - * - * @param cfg A libconfig object containing the settings. - * @param em A pointer to the netem settings structure (part of the path structure). - * @retval 0 Success. Everything went well. - * @retval <0 Error. Something went wrong. - */ -int config_parse_netem(config_setting_t *cfg, struct netem *em); - #endif /* _CFG_H_ */ diff --git a/server/include/gtfpga.h b/server/include/gtfpga.h index b449d5f60..444c46a50 100644 --- a/server/include/gtfpga.h +++ b/server/include/gtfpga.h @@ -9,8 +9,47 @@ #ifndef _GTFPGA_H_ #define _GTFPGA_H_ -struct gtfpga { +#include +#define GTFPGA_BAR 0 /**< The Base Address Register which is mmap()ed to the User Space */ + +#define GTFPGA_MAX_TX 64 /**< The amount of values which is supported by the GTFPGA card */ +#define GTFPGA_MAX_RX 64 /**< The amount of values which is supported by the GTFPGA card */ + +#define GTFPGA_VID 0x10ee /**< The default vendor ID of the GTFPGA card */ +#define GTFPGA_DID 0x0007 /**< The default device ID of the GTFPGA card */ + + +struct gtfpga { + struct pci_filter filter; + + int fd_mmap, fd_uio; + void *map; + + struct pci_dev *dev; }; +int gtfpga_init(int argc, char * argv[]); + +int gtfpga_deinit(); + +/** Parse node connection details for GTFPGA type + * + * @param cfg A libconfig object pointing to the node. + * @param n A pointer to the node structure which should be parsed. + * @retval 0 Success. Everything went well. + * @retval <0 Error. Something went wrong. + */ +int gtfpga_parse(config_setting_t *cfg, struct node *n); + +int gtfpga_print(struct node *n, char *buf, int len); + +int gtfpga_open(struct node *n); + +int gtfpga_close(struct node *n); + +int gtfpga_read(struct node *n, struct msg *m); + +int gtfpga_write(struct node *n, struct msg *m); + #endif /* _GTFPGA_H_ */ diff --git a/server/include/log.h b/server/include/log.h index ecde7b582..6175f48a0 100644 --- a/server/include/log.h +++ b/server/include/log.h @@ -5,34 +5,44 @@ * @file */ - #ifndef _LOG_H_ +#ifndef _LOG_H_ #define _LOG_H_ +#include + #ifdef __GNUC__ #define INDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_indent(1); #else #define INDENT ; #endif -/** Global debug level used by the debug() macro. - * It defaults to V (defined by the Makefile) and can be - * overwritten by the 'debug' setting in the config file. - */ -extern int _debug; - /** The log level which is passed as first argument to print() */ -enum log_level { - DEBUG, - INFO, - WARN, - ERROR -}; +#define DEBUG GRY("Debug") +#define INFO "" +#define WARN YEL("Warn") +#define ERROR RED("Error") + +/** Change log indention for current thread. + * + * The argument level can be negative! + */ int log_indent(int levels); +/** A helper function the restore the previous log indention level. + * + * This function is usually called by a __cleanup__ handler (GCC C Extension). + * See INDENT macro. + */ void log_outdent(int *); -/** Reset the wallclock of debugging outputs */ +/** Set the verbosity level of debug messages. + * + * @param lvl The new debug level. + */ +void log_setlevel(int lvl); + +/** Reset the wallclock of debug messages. */ void log_reset(); /** Logs variadic messages to stdout. @@ -40,38 +50,40 @@ void log_reset(); * @param lvl The log level * @param fmt The format string (printf alike) */ -void log_print(enum log_level lvl, const char *fmt, ...) +void log_print(const char *lvl, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); +/** Logs variadic messages to stdout. + * + * @param lvl The log level + * @param fmt The format string (printf alike) + * @param va The variadic argument list (see stdarg.h) + */ +void log_vprint(const char *lvl, const char *fmt, va_list va); + /** Printf alike debug message with level. */ -#define debug(lvl, msg, ...) do if (lvl <= _debug) log_print(DEBUG, msg, ##__VA_ARGS__); while (0) +void debug(int lvl, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); /** Printf alike info message. */ -#define info(msg, ...) do log_print(INFO, msg, ##__VA_ARGS__); while (0) +void info(const char *fmt, ...) + __attribute__ ((format(printf, 1, 2))); /** Printf alike warning message. */ -#define warn(msg, ...) do log_print(WARN, msg, ##__VA_ARGS__); while (0) - +void warn(const char *fmt, ...) + __attribute__ ((format(printf, 1, 2))); + /** Print error and exit. */ -#define error(msg, ...) do { \ - log_print(ERROR, msg, ##__VA_ARGS__); \ - die(); \ - } while (0) +void error(const char *fmt, ...) + __attribute__ ((format(printf, 1, 2))); /** Print error and strerror(errno). */ -#define serror(msg, ...) do { \ - log_print(ERROR, msg ": %s", ##__VA_ARGS__, strerror(errno)); \ - die(); \ - } while (0) +void serror(const char *fmt, ...) + __attribute__ ((format(printf, 1, 2))); /** Print configuration error and exit. */ -#define cerror(c, msg, ...) do { \ - log_print(ERROR, msg " in %s:%u", ##__VA_ARGS__, \ - (config_setting_source_file(c)) ? \ - config_setting_source_file(c) : "(stdio)", \ - config_setting_source_line(c)); \ - die(); \ - } while (0) +void cerror(config_setting_t *cfg, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); #endif /* _LOG_H_ */ diff --git a/server/include/node.h b/server/include/node.h index e6a3883e6..c1f088094 100644 --- a/server/include/node.h +++ b/server/include/node.h @@ -42,13 +42,15 @@ enum node_type { TCPD, /* BSD socket: AF_INET SOCK_STREAM bind + listen + accept */ TCP, /* BSD socket: AF_INET SOCK_STREAM bind + connect */ OPAL_ASYNC, /* OPAL-RT Asynchronous Process Api */ -// GTFPGA, /* Xilinx ML507 GTFPGA card */ + GTFPGA, /* Xilinx ML507 GTFPGA card */ INVALID }; -/** C++ like vtable construct for node_types */ +/** C++ like vtable construct for node_types + * @todo Add comments + */ struct node_vtable { - enum node_type type; + const enum node_type type; const char *name; int (*parse)(config_setting_t *cfg, struct node *n); @@ -58,6 +60,11 @@ struct node_vtable { int (*close)(struct node *n); int (*read)(struct node *n, struct msg *m); int (*write)(struct node *n, struct msg *m); + + int (*init)(int argc, char *argv[]); + int (*deinit)(); + + int refcnt; }; /** The data structure for a node. @@ -73,7 +80,7 @@ struct node const char *name; /** C++ like virtual function call table */ - struct node_vtable const *vt; + struct node_vtable *vt; /** Virtual data (used by vtable functions) */ union { struct socket *socket; @@ -86,6 +93,20 @@ struct node config_setting_t *cfg; }; +/** Initialize node type subsystems. + * + * These routines are only called once per type (not node). + * See node_vtable::init + */ +int node_init(int argc, char *argv[]); + +/** De-initialize node type subsystems. + * + * These routines are only called once per type (not node). + * See node_vtable::deinit + */ +int node_deinit(); + /** Connect and bind the UDP socket of this node. * * Depending on the type (vtable) of this node, @@ -121,7 +142,7 @@ int node_stop(struct node *n); * @param str A string describing the socket type. This must be one of: tcp, tcpd, udp, ip, ieee802.3 or opal * @return A pointer to the vtable, or NULL if there is no socket type / vtable with this id. */ -struct node_vtable const * node_lookup_vtable(const char *str); +struct node_vtable * node_lookup_vtable(const char *str); /** Search list of nodes for a name. * diff --git a/server/include/opal.h b/server/include/opal.h index 4fbf74b8c..1cfa6376c 100644 --- a/server/include/opal.h +++ b/server/include/opal.h @@ -71,6 +71,15 @@ int opal_init(int argc, char *argv[]); */ int opal_deinit(); +/** Parse node connection details for OPAL type + * + * @param cfg A libconfig object pointing to the node. + * @param nodes Add new nodes to this linked list. + * @retval 0 Success. Everything went well. + * @retval <0 Error. Something went wrong. + */ +int opal_parse(config_setting_t *cfg, struct node *n); + int opal_print(struct node *n, char *buf, int len); int opal_print_global(struct opal_global *g); diff --git a/server/include/socket.h b/server/include/socket.h index 0b8b63bf3..e9a3c4ce6 100644 --- a/server/include/socket.h +++ b/server/include/socket.h @@ -67,6 +67,15 @@ int socket_write(struct node *n, struct msg *m); */ int socket_read(struct node *n, struct msg *m); +/** Parse node connection details for SOCKET type + * + * @param cfg A libconfig object pointing to the node. + * @param n A pointer to the node structure which should be parsed. + * @retval 0 Success. Everything went well. + * @retval <0 Error. Something went wrong. + */ +int socket_parse(config_setting_t *cfg, struct node *n); + /** Print details of socket connection * * @param n A pointer to the node structure diff --git a/server/include/tc.h b/server/include/tc.h index a399e77d5..38df85061 100644 --- a/server/include/tc.h +++ b/server/include/tc.h @@ -14,6 +14,7 @@ #define _TC_H_ #include +#include /** A type alias for TC handles. * @@ -64,6 +65,15 @@ struct netem { int duplicate; }; +/** Parse network emulator (netem) settings. + * + * @param cfg A libconfig object containing the settings. + * @param em A pointer to the netem settings structure (part of the path structure). + * @retval 0 Success. Everything went well. + * @retval <0 Error. Something went wrong. + */ +int tc_parse(config_setting_t *cfg, struct netem *em); + /** Remove all queuing disciplines and filters. * * @param i The interface diff --git a/server/include/utils.h b/server/include/utils.h index fce60c599..ed7b0c3dd 100644 --- a/server/include/utils.h +++ b/server/include/utils.h @@ -24,39 +24,25 @@ #endif /* Some color escape codes for pretty log messages */ -#ifndef ENABLE_OPAL_ASYNC - #define GRY(str) "\e[30m" str "\e[0m" /**< Print str in gray */ - #define RED(str) "\e[31m" str "\e[0m" /**< Print str in red */ - #define GRN(str) "\e[32m" str "\e[0m" /**< Print str in green */ - #define YEL(str) "\e[33m" str "\e[0m" /**< Print str in yellow */ - #define BLU(str) "\e[34m" str "\e[0m" /**< Print str in blue */ - #define MAG(str) "\e[35m" str "\e[0m" /**< Print str in magenta */ - #define CYN(str) "\e[36m" str "\e[0m" /**< Print str in cyan */ - #define WHT(str) "\e[37m" str "\e[0m" /**< Print str in white */ - #define BLD(str) "\e[1m" str "\e[0m" /**< Print str in bold */ +#define GRY(str) "\e[30m" str "\e[0m" /**< Print str in gray */ +#define RED(str) "\e[31m" str "\e[0m" /**< Print str in red */ +#define GRN(str) "\e[32m" str "\e[0m" /**< Print str in green */ +#define YEL(str) "\e[33m" str "\e[0m" /**< Print str in yellow */ +#define BLU(str) "\e[34m" str "\e[0m" /**< Print str in blue */ +#define MAG(str) "\e[35m" str "\e[0m" /**< Print str in magenta */ +#define CYN(str) "\e[36m" str "\e[0m" /**< Print str in cyan */ +#define WHT(str) "\e[37m" str "\e[0m" /**< Print str in white */ +#define BLD(str) "\e[1m" str "\e[0m" /**< Print str in bold */ - #define GFX(chr) "\e(0" chr "\e(B" - #define UP(n) "\e[" ## n ## "A" - #define DOWN(n) "\e[" ## n ## "B" - #define RIGHT(n) "\e[" ## n ## "C" - #define LEFT(n) "\e[" ## n ## "D" -#else - #define GRY(str) str - #define RED(str) str - #define GRN(str) str - #define YEL(str) str - #define BLU(str) str - #define MAG(str) str - #define CYN(str) str - #define WHT(str) str - #define BLD(str) str - - #define GFX(chr) " " - #define UP(n) "" - #define DOWN(n) "" - #define RIGHT(n) "" - #define LEFT(n) "" -#endif +/* Alternate character set */ +#define ACS(chr) "\e(0" chr "\e(B" +#define ACS_VERTICAL ACS("\x78") +#define ACS_VERTRIGHT ACS("\x74s") +s +/* UTF-8 Line drawing characters */ +#define UTF8_BOX "\xE2\x96\x88" +#define UTF8_VERTICAL "\xE2\x94\x82" +#define UTF8_VERTRIGHT "\xE2\x94\x9C" /* CPP stringification */ #define XSTR(x) STR(x) diff --git a/server/src/cfg.c b/server/src/cfg.c index 6a3cf53ba..cd4958728 100644 --- a/server/src/cfg.c +++ b/server/src/cfg.c @@ -19,9 +19,12 @@ #include "hooks.h" #include "socket.h" -#include "gtfpga.h" + +#ifdef ENABLE_GTFPGA + #include "gtfpga.h" +#endif #ifdef ENABLE_OPAL_ASYNC -#include "opal.h" + #include "opal.h" #endif int config_parse(const char *filename, config_t *cfg, struct settings *set, @@ -83,8 +86,6 @@ int config_parse_global(config_setting_t *cfg, struct settings *set) config_setting_lookup_int(cfg, "debug", &set->debug); config_setting_lookup_float(cfg, "stats", &set->stats); - _debug = set->debug; - set->cfg = cfg; return 0; @@ -132,13 +133,16 @@ int config_parse_path(config_setting_t *cfg, if (enabled) { p->in->refcnt++; - FOREACH(&p->destinations, it) + p->in->vt->refcnt++; + + FOREACH(&p->destinations, it) { it->node->refcnt++; + it->node->vt->refcnt++; + } if (reverse) { if (list_length(&p->destinations) > 1) - warn("Using first destination '%s' as source for reverse path. " - "Ignoring remaining nodes", p->out->name); + error("Can't reverse path with multiple destination nodes"); struct path *r = path_create(); @@ -149,6 +153,8 @@ int config_parse_path(config_setting_t *cfg, r->in->refcnt++; r->out->refcnt++; + r->in->vt->refcnt++; + r->out->vt->refcnt++; list_push(paths, r); } @@ -257,106 +263,3 @@ int config_parse_node(config_setting_t *cfg, struct list *nodes) return ret; } -#ifdef ENABLE_OPAL_ASYNC -/** @todo: Remove this global variable. */ -extern struct opal_global *og; - -int config_parse_opal(config_setting_t *cfg, struct node *n) -{ - if (!og) { - warn("Skipping node '%s', because this server is not running as an OPAL Async process!", n->name); - return -1; - } - - struct opal *o = alloc(sizeof(struct opal)); - - config_setting_lookup_int(cfg, "send_id", &o->send_id); - config_setting_lookup_int(cfg, "recv_id", &o->recv_id); - config_setting_lookup_bool(cfg, "reply", &o->reply); - - /* Search for valid send and recv ids */ - int sfound = 0, rfound = 0; - for (int i=0; isend_icons; i++) - sfound += og->send_ids[i] == o->send_id; - for (int i=0; isend_icons; i++) - rfound += og->send_ids[i] == o->send_id; - - if (!sfound) - cerror(config_setting_get_member(cfg, "send_id"), "Invalid send_id '%u' for node '%s'", o->send_id, n->name); - if (!rfound) - cerror(config_setting_get_member(cfg, "recv_id"), "Invalid recv_id '%u' for node '%s'", o->recv_id, n->name); - - n->opal = o; - n->opal->global = og; - n->cfg = cfg; - - return 0; -} -#endif /* ENABLE_OPAL_ASYNC */ - - -#ifdef ENABLE_GTFPGA -/** @todo Implement */ -int config_parse_gtfpga(config_setting_t *cfg, struct node *n) -{ - return 0; -} -#endif /* ENABLE_GTFPGA */ - -int config_parse_socket(config_setting_t *cfg, struct node *n) -{ - const char *local, *remote; - int ret; - - struct socket *s = alloc(sizeof(struct socket)); - - if (!config_setting_lookup_string(cfg, "remote", &remote)) - cerror(cfg, "Missing remote address for node '%s'", n->name); - - if (!config_setting_lookup_string(cfg, "local", &local)) - cerror(cfg, "Missing local address for node '%s'", n->name); - - ret = socket_parse_addr(local, (struct sockaddr *) &s->local, node_type(n), AI_PASSIVE); - if (ret) - cerror(cfg, "Failed to resolve local address '%s' of node '%s': %s", - local, n->name, gai_strerror(ret)); - - ret = socket_parse_addr(remote, (struct sockaddr *) &s->remote, node_type(n), 0); - if (ret) - cerror(cfg, "Failed to resolve remote address '%s' of node '%s': %s", - remote, n->name, gai_strerror(ret)); - - /** @todo Netem settings are not usable AF_UNIX */ - config_setting_t *cfg_netem = config_setting_get_member(cfg, "netem"); - if (cfg_netem) { - s->netem = alloc(sizeof(struct netem)); - - config_parse_netem(cfg_netem, s->netem); - } - - n->socket = s; - - return 0; -} - -int config_parse_netem(config_setting_t *cfg, struct netem *em) -{ - em->valid = 0; - - if (config_setting_lookup_string(cfg, "distribution", &em->distribution)) - em->valid |= TC_NETEM_DISTR; - if (config_setting_lookup_int(cfg, "delay", &em->delay)) - em->valid |= TC_NETEM_DELAY; - if (config_setting_lookup_int(cfg, "jitter", &em->jitter)) - em->valid |= TC_NETEM_JITTER; - if (config_setting_lookup_int(cfg, "loss", &em->loss)) - em->valid |= TC_NETEM_LOSS; - if (config_setting_lookup_int(cfg, "duplicate", &em->duplicate)) - em->valid |= TC_NETEM_DUPL; - if (config_setting_lookup_int(cfg, "corrupt", &em->corrupt)) - em->valid |= TC_NETEM_CORRUPT; - - /** @todo Validate netem config values */ - - return 0; -} diff --git a/server/src/gtfpga.c b/server/src/gtfpga.c index 1c911e730..107f05d44 100644 --- a/server/src/gtfpga.c +++ b/server/src/gtfpga.c @@ -7,3 +7,185 @@ */ #include "gtfpga.h" + +#define SYSFS_PATH "/sys/bus/pci" + +static pci_access *pacc; + +int gtfpga_init(int argc, char *argv[]) +{ + pacc = pci_alloc(); /* Get the pci_access structure */ + pci_init(pacc); /* Initialize the PCI library */ + + pacc->error = error; /* Replace logging and debug functions */ + pacc->warning = warn; + pacc->debug = debug; + + pci_scan_bus(pacc); /* We want to get the list of devices */ +} + +int gtfpga_deinit() +{ + pci_cleanup(pacc); +} + +int gtfpga_parse(config_setting_t *cfg, struct node *n) +{ + char *slot, *id; + config_setting_t *cfg_slot, *cfg_id; + struct gtfpga *g = alloc(sizeof(struct gtfpga)); + + pci_filter_init(NULL, &g->filter); + + if (cfg_slot = config_setting_get_member(cfg, "slot")) { + if (slot = config_setting_get_string(cfg_slot)) { + if ((err = pci_filter_parse_slot(&g->filter, slot)) + cerror(cfg_slot, "%s", err); + } + else + cerror(cfg_slot, "Invalid slot format"); + } + + if (cfg_id = config_setting_get_member(cfg, "id")) { + if (id = config_setting_get_string(cfg_id)) { + if ((err = pci_filter_parse_id(&g->filter, id)) + cerror(cfg_id, "%s", err); + } + else + cerror(cfg_slot, "Invalid id format"); + } + + + return 0; +} + +int gtfpga_print(struct node *n, char *buf, int len) +{ + +} + +static int gtfpga_load_driver(struct pci_dev *d) +{ + FILE *f; + char slot[16]; + int ret; + + /* Prepare slot identifier */ + snprintf(slot, sizeof(slot), "%04x:%02x:%02x.%x", + d->domain, d->bus, d->slot, d->func); + + /* Load uio_pci_generic module */ + ret = system2("modprobe uio_pci_generic"); + if (ret) + serror("Failed to load module"); + + /* Add new ID to uio_pci_generic */ + f = fopen(SYSFS_PATH "/drivers/uio_pci_generic/new_id", "w"); + if (!f) + serror("Failed to add PCI id to uio_pci_generic driver"); + + fprintf(f, "%04x %04x", d->vendor_id, d->device_id); + fclose(f); + + /* Bind to uio_pci_generic */ + f = fopen(SYSFS_PATH "/drivers/uio_pci_generic/bind", "w"); + if (!f) + serror("Failed to add PCI id to uio_pci_generic driver"); + + fprintf(f, "%s\n", slot); + fclose(f); +} + +static struct pci_dev * gtfpga_find_device(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; + } + + return NULL; +} + +static int gtfpga_mmap(struct node *n) +{ + struct gtfpga *g = n->gtfpga; + + int fd = open("/dev/mem", O_RDWR | O_SYNC); + if (!fd) + serror("Failed open()"); + + long int addr = g->dev->base_addr[GTFPGA_BAR] & ~0xfff; + int size = g->dev->size[GTFPGA_BAR]; + + /* mmap() first BAR */ + printf("mmap(NULL, %#x, PROT_READ | PROT_WRITE, MAP_SHARED, %u, %#lx)", size, fd, addr); + void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr); + if (map == MAP_FAILED) + serror("Failed mmap()"); +} + +int gtfpga_open(struct node *n) +{ + struct gtfpga *g = n->gtfpga; + struct pci_dev *dev; + + int ret; + + dev = gtfpga_find_device(g->filter); + if (!dev) + error("No GTFPGA card detected"); + + g->dev = dev; + + ret = gtfpga_load_driver(dev); + if (ret) + error("Failed to load and bind driver (uio_pci_generic)"); + + ret = gtfpga_mmap(g); + if (ret) + error("Failed to setup memory mapping for GTFGPA card"); + + /* Show some debug infos */ + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */ + + debug(3, "Found GTFPGA card: %04x:%02x:%02x.%d vendor=%04x device=%04x class=%04x irq=%d", + dev->domain, dev->bus, dev->dev, dev->func, dev->vendor_id, dev->device_id, + dev->device_class, dev->irq); + + debug(3, " (%s)\n", pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id)); + + return 0; +} + +int gtfpga_close(struct node *n) +{ + struct gtfpga *g = n->gtfpga; + + if (g->map) + munmap(g->map, g->dev->size[GTFPGA_BAR]); + + close(g->fd_mmap); + close(g->fd_uio); + + return 0; +} + +/** @todo implement */ +int gtfpga_read(struct node *n, struct msg *m) +{ + struct gtfpga *g = n->gtfpga; + + return 0; +} + +/** @todo implement */ +int gtfpga_write(struct node *n, struct msg *m) +{ + struct gtfpga *g = n->gtfpga; + + return 0; +} + diff --git a/server/src/hist.c b/server/src/hist.c index 361375c41..b747ca58e 100644 --- a/server/src/hist.c +++ b/server/src/hist.c @@ -95,25 +95,24 @@ double hist_stddev(struct hist *h) void hist_print(struct hist *h) { INDENT + char buf[h->length * 8]; + hist_dump(h, buf, sizeof(buf)); + info("Total: %u values between %f and %f", h->total, h->low, h->high); info("Missed: %u (above), %u (below) ", h->higher, h->lower); info("Highest value: %f, lowest %f", h->highest, h->lowest); info("Mean: %f", hist_mean(h)); info("Variance: %f", hist_var(h)); info("Standard derivation: %f", hist_stddev(h)); - + hist_plot(h); - - char buf[h->length * 8]; - hist_dump(h, buf, sizeof(buf)); - - info("hist = %s", buf); + info(buf); } void hist_plot(struct hist *h) { - unsigned min = UINT_MAX; - unsigned max = 0; + char buf[HIST_HEIGHT] = { '#' }; + unsigned int min = UINT_MAX, max = 0; /* Get max, first & last */ for (int i = 0; i < h->length; i++) { @@ -123,9 +122,6 @@ void hist_plot(struct hist *h) min = h->data[i]; } - char buf[HIST_HEIGHT]; - memset(buf, '#', HIST_HEIGHT); - /* Print plot */ info("%9s | %5s | %s", "Value", "Occur", "Histogram Plot:"); for (int i = 0; i < h->length; i++) { @@ -139,17 +135,14 @@ void hist_plot(struct hist *h) void hist_dump(struct hist *h, char *buf, int len) { - char tok[8]; - memset(buf, 0, len); + *buf = 0; - strncat(buf, "[ ", len); + strap(buf, len, "[ "); - for (int i = 0; i < h->length; i++) { - snprintf(tok, sizeof(tok), "%u ", h->data[i]); - strncat(buf, tok, len - strlen(buf)); - } - - strncat(buf, "]", len - strlen(buf)); + for (int i = 0; i < h->length; i++) + strap(buf, len, "%u ", h->data[i]); + + strap(buf, len, "]"); } void hist_matlab(struct hist *h, FILE *f) diff --git a/server/src/hooks.c b/server/src/hooks.c index 637328ce9..8502a960f 100644 --- a/server/src/hooks.c +++ b/server/src/hooks.c @@ -65,8 +65,6 @@ int hook_log(struct msg *m, struct path *p) localtime_r(&ts, &tm); strftime(fstr, sizeof(fstr), HOOK_LOG_TEMPLATE, &tm); - - file = fopen(fstr, HOOK_LOG_MODE); if (file) debug(5, "Opened log file for path %s: %s", pstr, fstr); diff --git a/server/src/log.c b/server/src/log.c index 61a30abee..9a27f7a09 100644 --- a/server/src/log.c +++ b/server/src/log.c @@ -4,20 +4,25 @@ * @copyright 2015, Institute for Automation of Complex Power Systems, EONERC */ - #include +#include #include #include "log.h" #include "utils.h" -int _debug = V; +/** Debug level used by the debug() macro. + * It defaults to V (defined by the Makefile) and can be + * overwritten by the 'debug' setting in the configuration file. + */ +static int level = V; +/** A global clock used to prefix the log messages. */ static struct timespec epoch; #ifdef __GNUC__ +/** The current log indention level (per thread!). */ static __thread int indent = 0; -/** Get thread-specific pointer to indent level */ int log_indent(int levels) { int old = indent; @@ -31,41 +36,47 @@ void log_outdent(int *old) } #endif +void log_setlevel(int lvl) +{ + level = lvl; + debug(10, "Switched to debug level %u", level); +} + void log_reset() { clock_gettime(CLOCK_REALTIME, &epoch); + debug(10, "Debug clock resetted"); } -void log_print(enum log_level lvl, const char *fmt, ...) +void log_print(const char *lvl, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + log_vprint(lvl, fmt, ap); + va_end(ap); +} + +void log_vprint(const char *lvl, const char *fmt, va_list ap) { struct timespec ts; char buf[512] = ""; - va_list ap; - /* Timestamp */ clock_gettime(CLOCK_REALTIME, &ts); - strap(buf, sizeof(buf), "%8.3f ", timespec_delta(&epoch, &ts)); + strap(buf, sizeof(buf), "%10.3f ", timespec_delta(&epoch, &ts)); /* Severity */ - switch (lvl) { - case DEBUG: strap(buf, sizeof(buf), BLD("%-5s "), GRY("Debug")); break; - case INFO: strap(buf, sizeof(buf), BLD("%-5s "), " " ); break; - case WARN: strap(buf, sizeof(buf), BLD("%-5s "), YEL(" Warn")); break; - case ERROR: strap(buf, sizeof(buf), BLD("%-5s "), RED("Error")); break; - } - + strap(buf, sizeof(buf), BLD("%-5s "), lvl); + /* Indention */ #ifdef __GNUC__ for (int i = 0; i < indent; i++) - strap(buf, sizeof(buf), GFX("\x78") " "); - strap(buf, sizeof(buf), GFX("\x74") " "); + strap(buf, sizeof(buf), ACS_VERTICAL " "); + strap(buf, sizeof(buf), ACS_VERTRIGHT " "); #endif /* Format String */ - va_start(ap, fmt); vstrap(buf, sizeof(buf), fmt, ap); - va_end(ap); /* Output */ #ifdef ENABLE_OPAL_ASYNC @@ -73,3 +84,79 @@ void log_print(enum log_level lvl, const char *fmt, ...) #endif fprintf(stderr, "\r%s\n", buf); } + +/** Printf alike debug message with level. */ +void debug(int lvl, const char *fmt, ...) +{ + va_list ap; + + if (lvl <= level) { + va_start(ap, fmt); + log_vprint(DEBUG, fmt, ap); + va_end(ap); + } +} + +/** Printf alike info message. */ +void info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_vprint(INFO, fmt, ap); + va_end(ap); +} + +/** Printf alike warning message. */ +void warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_vprint(WARN, fmt, ap); + va_end(ap); +} + +/** Print error and exit. */ +void error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_vprint(ERROR, fmt, ap); + va_end(ap); + + die(); +} + +/** Print error and strerror(errno). */ +void serror(const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + log_print(ERROR, "%s: %s", buf, strerror(errno)); + die(); +} + +/** Print configuration error and exit. */ +void cerror(config_setting_t *cfg, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + log_print(ERROR, "%s in %s:%u", buf, + config_setting_source_file(cfg) + ? config_setting_source_file(cfg) + : "(stdio)", + config_setting_source_line(cfg)); + die(); +} \ No newline at end of file diff --git a/server/src/node.c b/server/src/node.c index 566d7e8e0..831408e3d 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -18,17 +18,24 @@ #include "opal.h" #endif -#define VTABLE(type, name, fnc) { type, name, config_parse_ ## fnc, \ - fnc ## _print, \ - fnc ## _open, \ - fnc ## _close, \ - fnc ## _read, \ - fnc ## _write } +#define VTABLE(type, name, fnc) { type, name, \ + fnc ## _parse, fnc ## _print, \ + fnc ## _open, fnc ## _close, \ + fnc ## _read, fnc ## _write } + +#define VTABLE2(type, name, fnc) { type, name, \ + fnc ## _parse, fnc ## _print, \ + fnc ## _open, fnc ## _close, \ + fnc ## _read, fnc ## _write, \ + fnc ## _init, fnc ## _deinit } /** Vtable for virtual node sub types */ -static const struct node_vtable vtables[] = { +struct node_vtable vtables[] = { #ifdef ENABLE_OPAL_ASYNC - VTABLE(OPAL_ASYNC, "opal", opal), + VTABLE2(OPAL_ASYNC, "opal", opal), +#endif +#ifdef ENABLE_GTFPGA + VTABLE2(GTFPGA, "gtfpga", gtfpga), #endif VTABLE(LOG_FILE, "file", file), VTABLE(IEEE_802_3, "ieee802.3", socket), @@ -41,6 +48,37 @@ static const struct node_vtable vtables[] = { /** Linked list of nodes. */ struct list nodes; +int node_init(int argc, char *argv[]) +{ INDENT + for (int i=0; irefcnt && vt->init) { + if (vt->init(argc, argv)) + error("Failed to initialize '%s' node type", vt->name); + else + info("Initializing '%s' node type", vt->name); + } + } + + return 0; +} + +int node_deinit() +{ INDENT + /* De-initialize node types */ + for (int i=0; irefcnt && vt->deinit) { + if (vt->deinit()) + error("Failed to de-initialize '%s' node type", vt->name); + else + info("De-initializing '%s' node type", vt->name); + + } + } + return 0; +} + struct node * node_lookup_name(const char *str, struct list *nodes) { FOREACH(nodes, it) { @@ -51,7 +89,7 @@ struct node * node_lookup_name(const char *str, struct list *nodes) return NULL; } -struct node_vtable const * node_lookup_vtable(const char *str) +struct node_vtable * node_lookup_vtable(const char *str) { for (int i = 0; i < ARRAY_LEN(vtables); i++) { if (!strcmp(vtables[i].name, str)) diff --git a/server/src/opal.c b/server/src/opal.c index b7783aa05..382ad1643 100644 --- a/server/src/opal.c +++ b/server/src/opal.c @@ -12,8 +12,7 @@ #include "opal.h" #include "utils.h" -/** @todo: delcare statice */ -struct opal_global *og = NULL; +static struct opal_global *og = NULL; int opal_init(int argc, char *argv[]) { @@ -112,6 +111,40 @@ int opal_print_global(struct opal_global *g) return 0; } +int opal_parse(config_setting_t *cfg, struct node *n) +{ + if (!og) { + warn("Skipping node '%s', because this server is not running as an OPAL Async process!", n->name); + return -1; + } + + struct opal *o = alloc(sizeof(struct opal)); + + config_setting_lookup_int(cfg, "send_id", &o->send_id); + config_setting_lookup_int(cfg, "recv_id", &o->recv_id); + config_setting_lookup_bool(cfg, "reply", &o->reply); + + /* Search for valid send and recv ids */ + int sfound = 0, rfound = 0; + for (int i=0; isend_icons; i++) + sfound += og->send_ids[i] == o->send_id; + for (int i=0; isend_icons; i++) + rfound += og->send_ids[i] == o->send_id; + + if (!sfound) + cerror(config_setting_get_member(cfg, "send_id"), + "Invalid send_id '%u' for node '%s'", o->send_id, n->name); + if (!rfound) + cerror(config_setting_get_member(cfg, "recv_id"), + "Invalid recv_id '%u' for node '%s'", o->recv_id, n->name); + + n->opal = o; + n->opal->global = og; + n->cfg = cfg; + + return 0; +} + int opal_print(struct node *n, char *buf, int len) { struct opal *o = n->opal; diff --git a/server/src/server.c b/server/src/server.c index ccd1e1018..a55a73eb7 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -51,15 +51,13 @@ static void quit() FOREACH(&interfaces, it) if_stop(it->interface); -#ifdef ENABLE_OPAL_ASYNC - opal_deinit(); -#endif - /* Freeing dynamically allocated memory */ list_destroy(&paths); list_destroy(&nodes); list_destroy(&interfaces); config_destroy(&config); + + node_deinit(); info("Goodbye!"); @@ -89,7 +87,7 @@ void realtime_init() /* Setup exit handler */ void signals_init() -{ INDENT +{ struct sigaction sa_quit = { .sa_flags = SA_SIGINFO, .sa_sigaction = quit @@ -129,9 +127,12 @@ int main(int argc, char *argv[]) if (argc != 2) #endif usage(argv[0]); + + char *configfile = (argc == 2) ? argv[1] : "opal-shmem.conf"; - info("This is Simulator2Simulator Server (S2SS) %s (built on %s, %s, debug=%d)", - BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__)), _debug); + log_reset(); + info("This is Simulator2Simulator Server (S2SS) %s (built on %s, %s)", + BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__))); /* Check priviledges */ if (getuid() != 0) @@ -141,28 +142,18 @@ int main(int argc, char *argv[]) list_init(&nodes, (dtor_cb_t) node_destroy); list_init(&paths, (dtor_cb_t) path_destroy); list_init(&interfaces, (dtor_cb_t) if_destroy); - - - info("Initialize realtime system:"); + + info("Initialize real-time system:"); realtime_init(); - info("Setup signals:"); + info("Initialize signals:"); signals_init(); + info("Initialize node types:"); + node_init(argc, argv); + info("Parsing configuration:"); config_init(&config); - -#ifdef ENABLE_OPAL_ASYNC - /* Check if called we are called as an asynchronous process from RT-LAB. */ - opal_init(argc, argv); - - /* @todo: look in predefined locations for a file */ - char *configfile = "opal-shmem.conf"; -#else - char *configfile = argv[1]; -#endif - - /* Parse configuration and create nodes/paths */ config_parse(configfile, &config, &settings, &nodes, &paths); /* Connect all nodes and start one thread per path */ diff --git a/server/src/socket.c b/server/src/socket.c index 3e4fc85d6..f54c70329 100644 --- a/server/src/socket.c +++ b/server/src/socket.c @@ -172,6 +172,42 @@ int socket_write(struct node *n, struct msg *m) return 0; } +int socket_parse(config_setting_t *cfg, struct node *n) +{ + const char *local, *remote; + int ret; + + struct socket *s = alloc(sizeof(struct socket)); + + if (!config_setting_lookup_string(cfg, "remote", &remote)) + cerror(cfg, "Missing remote address for node '%s'", n->name); + + if (!config_setting_lookup_string(cfg, "local", &local)) + cerror(cfg, "Missing local address for node '%s'", n->name); + + ret = socket_parse_addr(local, (struct sockaddr *) &s->local, node_type(n), AI_PASSIVE); + if (ret) + cerror(cfg, "Failed to resolve local address '%s' of node '%s': %s", + local, n->name, gai_strerror(ret)); + + ret = socket_parse_addr(remote, (struct sockaddr *) &s->remote, node_type(n), 0); + if (ret) + cerror(cfg, "Failed to resolve remote address '%s' of node '%s': %s", + remote, n->name, gai_strerror(ret)); + + /** @todo Netem settings are not usable AF_UNIX */ + config_setting_t *cfg_netem = config_setting_get_member(cfg, "netem"); + if (cfg_netem) { + s->netem = alloc(sizeof(struct netem)); + + tc_parse(cfg_netem, s->netem); + } + + n->socket = s; + + return 0; +} + int socket_print_addr(char *buf, int len, struct sockaddr *sa) { switch (sa->sa_family) { diff --git a/server/src/tc.c b/server/src/tc.c index bfda86339..e087c91b2 100644 --- a/server/src/tc.c +++ b/server/src/tc.c @@ -14,6 +14,28 @@ #include "if.h" #include "tc.h" +int tc_parse(config_setting_t *cfg, struct netem *em) +{ + em->valid = 0; + + if (config_setting_lookup_string(cfg, "distribution", &em->distribution)) + em->valid |= TC_NETEM_DISTR; + if (config_setting_lookup_int(cfg, "delay", &em->delay)) + em->valid |= TC_NETEM_DELAY; + if (config_setting_lookup_int(cfg, "jitter", &em->jitter)) + em->valid |= TC_NETEM_JITTER; + if (config_setting_lookup_int(cfg, "loss", &em->loss)) + em->valid |= TC_NETEM_LOSS; + if (config_setting_lookup_int(cfg, "duplicate", &em->duplicate)) + em->valid |= TC_NETEM_DUPL; + if (config_setting_lookup_int(cfg, "corrupt", &em->corrupt)) + em->valid |= TC_NETEM_CORRUPT; + + /** @todo Validate netem config values */ + + return 0; +} + int tc_reset(struct interface *i) { char cmd[128]; diff --git a/server/src/utils.c b/server/src/utils.c index d30dd2503..f77d3862f 100644 --- a/server/src/utils.c +++ b/server/src/utils.c @@ -101,16 +101,15 @@ struct timespec timespec_rate(double rate) /** @todo: Proper way: create additional pipe for stderr in child process */ int system2(const char *cmd, ...) { + va_list ap; char buf[1024]; - va_list ap; va_start(ap, cmd); - - vsnprintf(buf, sizeof(buf), cmd, ap); - strncat(buf, " 2>&1", sizeof(buf)); - + vsnprintf(buf, sizeof(buf), cmd, ap); va_end(ap); + strap(buf, sizeof(buf), " 2>&1", sizeof(buf)); /* redirect stderr to stdout */ + debug(1, "System: %s", buf); FILE *f = popen(buf, "r");