From f9058f51e5180ed2d5319e1959680e7773c45984 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sun, 10 Jan 2016 03:13:31 +0100 Subject: [PATCH] increase the interoperability between LwIP and newlib - create own socket functions for newlib - exchange error numbers between newlib and LwIP - add LWIP_FD_BIT to mark a file descriptor as LwIP file descriptor - add netio example --- hermit/Makefile | 2 +- hermit/arch/x86/kernel/tasks.c | 17 +- hermit/include/hermit/tasks.h | 11 +- hermit/kernel/main.c | 69 ++--- hermit/kernel/syscall.c | 89 +++++-- hermit/kernel/tasks.c | 34 +++ hermit/lwip | 2 +- hermit/tools/netinet/in.h | 39 +++ hermit/tools/proxy.c | 2 +- hermit/usr/Makefile | 13 +- hermit/usr/benchmarks/Makefile | 11 +- hermit/usr/benchmarks/netio.c | 465 +++++++++++++++++++++++++++++++++ 12 files changed, 666 insertions(+), 88 deletions(-) create mode 100644 hermit/tools/netinet/in.h create mode 100644 hermit/usr/benchmarks/netio.c diff --git a/hermit/Makefile b/hermit/Makefile index 560bee573..a2eb565e5 100644 --- a/hermit/Makefile +++ b/hermit/Makefile @@ -41,7 +41,7 @@ RM = rm -rf CFLAGS_FOR_NEWLIB = -m64 -mtls-direct-seg-refs -mno-red-zone -O3 -mavx2 -mfma -mtune=intel $(STACKPROT) FFLAGS_FOR_NEWLIB = -m64 -mtls-direct-seg-refs -mno-red-zone -O3 -mavx2 -mfma -mtune=intel -CXXFLAGS_FOR_NEWLIB = $(CFLAGS_FOR_NEWLIB) +CXXFLAGS_FOR_NEWLIB = LDFLAGS_FOR_NEWLIB = NASMFLAGS_FOR_NEWLIB = -felf64 CFLAGS_FOR_TOOLS = -O2 -Wall diff --git a/hermit/arch/x86/kernel/tasks.c b/hermit/arch/x86/kernel/tasks.c index 0d00adb5d..a21081f40 100644 --- a/hermit/arch/x86/kernel/tasks.c +++ b/hermit/arch/x86/kernel/tasks.c @@ -59,7 +59,6 @@ static inline void enter_user_task(size_t ep, size_t stack) static int thread_entry(void* arg, size_t ep) { - task_t* curr_task = per_core(current_task); #if 0 size_t addr, stack = 0; size_t flags; @@ -95,21 +94,11 @@ static int thread_entry(void* arg, size_t ep) vma_add(stack, stack+npages*PAGE_SIZE-1, flags); #endif + if (init_tls()) + return -ENOMEM; + //vma_dump(); - // do we have to create a TLS segement? - if (curr_task->tls_addr && curr_task->tls_size) { - char* tls_addr = NULL; - - tls_addr = kmalloc(curr_task->tls_size); - // copy default TLS segment to stack - memcpy((void*) (tls_addr), (void*) curr_task->tls_addr, curr_task->tls_size); - - // set fs register to the TLS segment - writefs((size_t) tls_addr); - kprintf("Task %d set fs to %p\n", curr_task->id, tls_addr); - } else writefs(0); // no TLS => clear fs register - // set first argument //asm volatile ("mov %0, %%rdi" :: "r"(arg)); //enter_user_task(ep, stack+offset); diff --git a/hermit/include/hermit/tasks.h b/hermit/include/hermit/tasks.h index 088a254ef..01ee9a4ea 100644 --- a/hermit/include/hermit/tasks.h +++ b/hermit/include/hermit/tasks.h @@ -137,10 +137,19 @@ int create_kernel_task_on_core(tid_t* id, entry_point_t ep, void* args, uint8_t * * @return * - 0 on success - * - -EINVAL (-22) or -ENOMEM (-12)on failure + * - -EINVAL (-22) or -ENOMEM (-12) on failure */ int create_user_task_on_core(tid_t* id, const char* fame, char** argv, uint8_t prio, uint32_t core_id); + +/** @brief Create a thread local storage for the current task + * + * @return + * - 0 on success + * - -EONMEM on failure + */ +int init_tls(void); + /** @brief Cleanup function for the task termination * * On termination, the task call this function to cleanup its address space. diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index f3c53646c..31cc62d7a 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -54,12 +54,11 @@ #include #include -#define HERMIT_PORT 0x494F +#define HERMIT_PORT 0x494E #define HEMRIT_MAGIC 0x7E317 static struct netif mmnif_netif; static const int sobufsize = 131072; -volatile int8_t shutdown = 0; /* * Note that linker symbols are not variables, they have no memory allocated for @@ -193,7 +192,7 @@ int network_shutdown(void) kputs("Shutdown LwIP\n"); if (libc_sd > 0) - closesocket(libc_sd); + lwip_close(libc_sd); mmnif_shutdown(); netifapi_netif_set_down(&mmnif_netif); @@ -281,33 +280,13 @@ static int initd(void* arg) //create_kernel_task(NULL, foo, "foo1", NORMAL_PRIO); //create_kernel_task(NULL, foo, "foo2", NORMAL_PRIO); + // initialize network init_netifs(); - // do we have a thread local storage? - if (((size_t) &tls_end - (size_t) &tls_start) > 0) { - char* tls_addr = NULL; - - curr_task->tls_addr = (size_t) &tls_start; - curr_task->tls_size = (size_t) &tls_end - (size_t) &tls_start; - - // TODO: free TLS after termination - tls_addr = kmalloc(curr_task->tls_size); - if (BUILTIN_EXPECT(!tls_addr, 0)) { - kprintf("load_task: heap is missing!\n"); - kfree(curr_task->heap); - return -ENOMEM; - } - - memcpy((void*) tls_addr, (void*) curr_task->tls_addr, curr_task->tls_size); - - // set fs register to the TLS segment - set_tls((size_t) tls_addr); - kprintf("Task %d set fs to 0x%zx\n", curr_task->id, tls_addr); - } else set_tls(0); // no TLS => clear fs register - + // initialize iRCCE //init_rcce(); - s = socket(PF_INET , SOCK_STREAM , 0); + s = lwip_socket(PF_INET , SOCK_STREAM , 0); if (s < 0) { kprintf("socket failed: %d\n", server); return -1; @@ -319,17 +298,17 @@ static int initd(void* arg) server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(HERMIT_PORT); - if ((err = bind(s, (struct sockaddr *) &server, sizeof(server))) < 0) + if ((err = lwip_bind(s, (struct sockaddr *) &server, sizeof(server))) < 0) { kprintf("bind failed: %d\n", errno); - closesocket(s); + lwip_close(s); return -1; } - if ((err = listen(s, 2)) < 0) + if ((err = lwip_listen(s, 2)) < 0) { kprintf("listen failed: %d\n", errno); - closesocket(s); + lwip_close(s); return -1; } @@ -337,28 +316,28 @@ static int initd(void* arg) kputs("TCP server listening.\n"); - if ((c = accept(s, (struct sockaddr *)&client, (socklen_t*)&len)) < 0) + if ((c = lwip_accept(s, (struct sockaddr *)&client, (socklen_t*)&len)) < 0) { kprintf("accept faild: %d\n", errno); - closesocket(s); + lwip_close(s); return -1; } kputs("Establish IP connection\n"); - setsockopt(c, SOL_SOCKET, SO_RCVBUF, (char *) &sobufsize, sizeof(sobufsize)); - setsockopt(c, SOL_SOCKET, SO_SNDBUF, (char *) &sobufsize, sizeof(sobufsize)); - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag)); + lwip_setsockopt(c, SOL_SOCKET, SO_RCVBUF, (char *) &sobufsize, sizeof(sobufsize)); + lwip_setsockopt(c, SOL_SOCKET, SO_SNDBUF, (char *) &sobufsize, sizeof(sobufsize)); + lwip_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag)); - read(c, &magic, sizeof(magic)); + lwip_read(c, &magic, sizeof(magic)); if (magic != HEMRIT_MAGIC) { kprintf("Invalid magic number %d\n", magic); - closesocket(c); + lwip_close(c); return -1; } - err = read(c, &argc, sizeof(argc)); + err = lwip_read(c, &argc, sizeof(argc)); if (err != sizeof(argc)) goto out; @@ -369,7 +348,7 @@ static int initd(void* arg) for(i=0; i 0) - closesocket(c); + lwip_close(c); libc_sd = -1; if (s > 0) - closesocket(s); + lwip_close(s); //network_shutdown(); diff --git a/hermit/kernel/syscall.c b/hermit/kernel/syscall.c index 8589c83c4..716c4a9c8 100644 --- a/hermit/kernel/syscall.c +++ b/hermit/kernel/syscall.c @@ -47,6 +47,15 @@ extern int32_t isle; extern int32_t possible_isles; extern int libc_sd; + +int* __getreent(void); + +static inline int* libc_errno(void) +{ + + return __getreent(); +} + tid_t sys_getpid(void) { task_t* task = per_core(current_task); @@ -83,10 +92,10 @@ void NORETURN sys_exit(int arg) if (libc_sd >= 0) { spinlock_lock(&lwip_lock); - write(libc_sd, &sysargs, sizeof(sysargs)); + lwip_write(libc_sd, &sysargs, sizeof(sysargs)); spinlock_unlock(&lwip_lock); - closesocket(libc_sd); + lwip_close(libc_sd); libc_sd = -1; } @@ -104,20 +113,32 @@ ssize_t sys_read(int fd, char* buf, size_t len) sys_read_t sysargs = {__NR_read, fd, len}; ssize_t j, ret; + // do we have an LwIP file descriptor? + if (fd & LWIP_FD_BIT) { + ret = lwip_read(fd & ~LWIP_FD_BIT, buf, len); + if (ret) + { + *libc_errno() = errno; + return -1; + } + + return 0; + } + if (libc_sd < 0) return -ENOSYS; spinlock_lock(&lwip_lock); - write(libc_sd, &sysargs, sizeof(sysargs)); + lwip_write(libc_sd, &sysargs, sizeof(sysargs)); - read(libc_sd, &j, sizeof(j)); + lwip_read(libc_sd, &j, sizeof(j)); if (j > 0) { ssize_t i = 0; while(i < j) { - ret = read(libc_sd, buf+i, j-i); + ret = lwip_read(libc_sd, buf+i, j-i); if (ret < 0) { spinlock_unlock(&lwip_lock); return ret; @@ -147,6 +168,18 @@ ssize_t sys_write(int fd, const char* buf, size_t len) if (BUILTIN_EXPECT(!buf, 0)) return -1; + // do we have an LwIP file descriptor? + if (fd & LWIP_FD_BIT) { + ret = lwip_write(fd & ~LWIP_FD_BIT, buf, len); + if (ret) + { + *libc_errno() = errno; + return -1; + } + + return 0; + } + if (libc_sd < 0) { for(i=0; i 2) { - ret = read(libc_sd, &i, sizeof(i)); + ret = lwip_read(libc_sd, &i, sizeof(i)); if (ret < 0) i = ret; } else i = len; @@ -227,37 +260,37 @@ int sys_open(const char* name, int flags, int mode) spinlock_lock(&lwip_lock); i = 0; - setsockopt(libc_sd, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i)); + lwip_setsockopt(libc_sd, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i)); - ret = write(libc_sd, &sysnr, sizeof(sysnr)); + ret = lwip_write(libc_sd, &sysnr, sizeof(sysnr)); if (ret < 0) goto out; - ret = write(libc_sd, &len, sizeof(len)); + ret = lwip_write(libc_sd, &len, sizeof(len)); if (ret < 0) goto out; i=0; while(i #include +/* + * Note that linker symbols are not variables, they have no memory allocated for + * maintaining a value, rather their address is their value. + */ +extern const void tls_start; +extern const void tls_end; + /* * HermitCore is a single address space OS * => we need only a lock to protect the page tables & VMA @@ -174,6 +181,33 @@ int set_idle_task(void) return ret; } +int init_tls(void) +{ + task_t* curr_task = per_core(current_task); + + // do we have a thread local storage? + if (((size_t) &tls_end - (size_t) &tls_start) > 0) { + char* tls_addr = NULL; + + curr_task->tls_addr = (size_t) &tls_start; + curr_task->tls_size = (size_t) &tls_end - (size_t) &tls_start; + + tls_addr = kmalloc(curr_task->tls_size); + if (BUILTIN_EXPECT(!tls_addr, 0)) { + kprintf("load_task: heap is missing!\n"); + return -ENOMEM; + } + + memcpy((void*) tls_addr, (void*) curr_task->tls_addr, curr_task->tls_size); + + // set fs register to the TLS segment + set_tls((size_t) tls_addr); + kprintf("Task %d set fs to 0x%zx\n", curr_task->id, tls_addr); + } else set_tls(0); // no TLS => clear fs register + + return 0; +} + void finish_task_switch(void) { task_t* old; diff --git a/hermit/lwip b/hermit/lwip index cf00aee08..9e76e0b89 160000 --- a/hermit/lwip +++ b/hermit/lwip @@ -1 +1 @@ -Subproject commit cf00aee089047c3356b558423b368a85fc76caa7 +Subproject commit 9e76e0b89f21173c1a311be6789360c1054eb269 diff --git a/hermit/tools/netinet/in.h b/hermit/tools/netinet/in.h new file mode 100644 index 000000000..aa88cfed8 --- /dev/null +++ b/hermit/tools/netinet/in.h @@ -0,0 +1,39 @@ +#ifndef __NETINET_IN_H__ +#define __NETINET_IN_H__ + +#include +#include + +#ifdef __cplusplus +{ +#endif + +#if 0 +struct in_addr { + uint32_t s_addr; +}; +#endif + +/** 255.255.255.255 */ +#define IPADDR_NONE ((uint32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((uint32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((uint32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((uint32_t)0xffffffffUL) + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +#ifdef __cplusplus +} +#endif + +#endif /* __NETINET_IN_H__ */ diff --git a/hermit/tools/proxy.c b/hermit/tools/proxy.c index 891229a8e..4497db5ba 100644 --- a/hermit/tools/proxy.c +++ b/hermit/tools/proxy.c @@ -37,7 +37,7 @@ #include #include -#define HERMIT_PORT 0x494F +#define HERMIT_PORT 0x494E #define HERMIT_MAGIC 0x7E317 #define MAX_PATH 255 diff --git a/hermit/usr/Makefile b/hermit/usr/Makefile index c76804f17..6aa74c3c7 100644 --- a/hermit/usr/Makefile +++ b/hermit/usr/Makefile @@ -3,9 +3,11 @@ ARCH = x86 TARGET=x86_64-hermit NJOBS=-j20 +CROSSCOMPREFIX = x86_64-hermit NEWLIB = $(TOPDIR)/$(ARCH)/$(TARGET) RM = rm -rf CD = cd +CP = cp MKDIR = mkdir TMP = $(TOPDIR)/tmp OPT = --disable-shared --disable-multilib --enable-newlib-io-c99-formats --enable-newlib-multithread #--enable-newlib-reent-small @@ -43,7 +45,7 @@ $(TMP)/bootstrap: $Q$(MKDIR) $(TMP)/bootstrap $Q$(CD) $(TMP)/bootstrap; $(TOPDIR)/gcc/configure --target=$(TARGET) --prefix=$(TOPDIR)/$(ARCH) --without-headers --enable-languages=c --disable-nls --disable-shared --disable-libssp --disable-libgomp --enable-threads=posix --enable-tls && $(MAKE) $(NJOBS) all-gcc && $(MAKE) install-gcc -toolchain: $(TMP)/newlib libs $(TMP)/gcc demo +toolchain: $(TMP)/newlib libs $(TMP)/gcc headers demo $(TMP)/newlib: @echo Build newlib, libpthread and libgomp @@ -58,6 +60,13 @@ libs: $Q$(MAKE) TARGET=$(TARGET) CC_FOR_TARGET=$(CC_FOR_TARGET) AR_FOR_TARGET=$(AR_FOR_TARGET) CFLAGS_FOR_TARGET+="-I. -Wall" -C ircce depend $Q$(MAKE) TARGET=$(TARGET) CC_FOR_TARGET=$(CC_FOR_TARGET) AR_FOR_TARGET=$(AR_FOR_TARGET) CFLAGS_FOR_TARGET+="-I. -Wall" -C ircce +headers: + @echo [CP] Copy headers + $Q$(CP) -R ../lwip/src/include/posix/* $(TOPDIR)/$(ARCH)/$(CROSSCOMPREFIX)/include/ + $Q$(CP) -R ../lwip/src/include/arch $(TOPDIR)/$(ARCH)/$(CROSSCOMPREFIX)/include/ + $Q$(CP) -R ../lwip/src/include/lwip* $(TOPDIR)/$(ARCH)/$(CROSSCOMPREFIX)/include/ + $Q$(CP) -R ../lwip/src/include/ipv4/lwip/* $(TOPDIR)/$(ARCH)/$(CROSSCOMPREFIX)/include/lwip/ + $Q$(CP) -R ../tools/neti* $(TOPDIR)/$(ARCH)/$(CROSSCOMPREFIX)/include/ $(TMP)/gcc: @echo Build final gcc @@ -81,3 +90,5 @@ veryclean: $Q$(MAKE) -C benchmarks veryclean $Q$(RM) $(TOPDIR)/$(ARCH) $Q$(RM) $(TMP) + +.PHONY: default all clean veryclean headers libs diff --git a/hermit/usr/benchmarks/Makefile b/hermit/usr/benchmarks/Makefile index cff96d93e..628deb1f3 100644 --- a/hermit/usr/benchmarks/Makefile +++ b/hermit/usr/benchmarks/Makefile @@ -48,7 +48,7 @@ endif default: all -all: stream.bin hg.bin #RCCE_pingping RCCE_pingpong +all: stream.bin hg.bin netio.bin #RCCE_pingping RCCE_pingpong stream.o: stream.c @echo [CC] $@ @@ -75,6 +75,13 @@ RCCE_pingpong: RCCE_pingpong.o $Q$(OBJCOPY_FOR_TARGET) $(STRIP_DEBUG) $@ $Qchmod a-x $@.sym +netio: netio.o + @echo [LD] $@ + $Q$(CC_FOR_TARGET) $(LDFLAGS_FOR_TARGET) $(CFLAGS_FOR_TARGET) -o $@ $< + $Q$(OBJCOPY_FOR_TARGET) $(KEEP_DEBUG) $@ $@.sym + $Q$(OBJCOPY_FOR_TARGET) $(STRIP_DEBUG) $@ + $Qchmod a-x $@.sym + hg: hg.o hist.o rdtsc.o run.o init.o opt.o report.o setup.o @echo [LD] $@ $Q$(CC_FOR_TARGET) $(LDFLAGS_FOR_TARGET) $(CFLAGS_FOR_TARGET) -o $@ $< hist.o rdtsc.o run.o init.o opt.o report.o setup.o @@ -84,7 +91,7 @@ hg: hg.o hist.o rdtsc.o run.o init.o opt.o report.o setup.o clean: @echo Cleaning benchmarks - $Q$(RM) stream hg RCCE_pingping RCCE_pingpong *.sym *.o *~ *.bin + $Q$(RM) stream hg netio RCCE_pingping RCCE_pingpong *.sym *.o *~ *.bin veryclean: @echo Propper cleaning benchmarks diff --git a/hermit/usr/benchmarks/netio.c b/hermit/usr/benchmarks/netio.c new file mode 100644 index 000000000..5cff5b519 --- /dev/null +++ b/hermit/usr/benchmarks/netio.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, 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. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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 REGENTS OR CONTRIBUTORS BE LIABLE FOR AN + * 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 +#include +#include +#include +#include +#include +#include + +/* + * This implements a netio server and client (only TCP version). + * The client sends a command word (4 bytes) then a data length word (4 bytes). + * If the command is "receive", the server is to consume "data length" bytes into + * a circular buffer until the first byte is non-zero, then it is to consume + * another command/data pair. + * If the command is "send", the server is to send "data length" bytes from a circular + * buffer with the first byte being zero, until "some time" (6 seconds in the + * current netio131.zip download) has passed and then send one final buffer with + * the first byte being non-zero. Then it is to consume another command/data pair. + */ + +/* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */ + +#include +#include +#include +#include + +typedef struct +{ + uint32_t cmd; + uint32_t data; +} CONTROL; + +#define CMD_QUIT 0 +#define CMD_C2S 1 +#define CMD_S2C 2 +#define CMD_RES 3 + +#define CTLSIZE sizeof(CONTROL) +#define DEFAULTPORT 0x494F +#define TMAXSIZE 65536 + +static int tSizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32767}; +static size_t ntSizes = sizeof(tSizes) / sizeof(int); +static int nPort = DEFAULTPORT; +static const int sobufsize = 131072; +static struct in_addr addr_local; +static struct in_addr addr_server; + +extern unsigned int get_cpufreq(void); + +inline static unsigned long long rdtsc(void) +{ + unsigned long lo, hi; + asm volatile ("rdtsc" : "=a"(lo), "=d"(hi) :: "memory"); + return ((unsigned long long) hi << 32ULL | (unsigned long long) lo); +} + +static int send_data(int socket, void *buffer, size_t size, int flags) +{ + ssize_t rc = send(socket, buffer, size, flags); + + if (rc < 0) + { + printf("send failed: %d\n", errno); + return -1; + } + + if (rc != size) + return 1; + + return 0; +} + +static int recv_data(int socket, void *buffer, size_t size, int flags) +{ + ssize_t rc = recv(socket, buffer, size, flags); + + if (rc < 0) { + printf("recv failed: %d\n", errno); + return -1; + } + + if (rc != size) + return 1; + + return 0; +} + +static char *InitBuffer(size_t nSize) +{ + char *cBuffer = malloc(nSize); + + memset(cBuffer, 0xFF, nSize); + cBuffer[0] = 0; + + return cBuffer; +} + +static char *PacketSize(int nSize) +{ + static char szBuffer[64]; + + if ((nSize % 1024) == 0 || (nSize % 1024) == 1023) + sprintf(szBuffer, "%2dk", (nSize + 512) / 1024); + else + sprintf(szBuffer, "%d", nSize); + + return szBuffer; +} + +static int TCPServer(void) +{ + char *cBuffer; + CONTROL ctl; + uint64_t nData; + struct sockaddr_in sa_server, sa_client; + int server, client; + socklen_t length; + struct timeval tv; + fd_set fds; + int rc; + int nByte; + int err; + uint64_t start, end; + uint32_t freq = get_cpufreq(); /* in MHz */ + + if ((cBuffer = InitBuffer(TMAXSIZE)) == NULL) { + printf("Netio: Not enough memory\n"); + return -1; + } + + if ((server = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + printf("socket failed: %d\n", server); + free(cBuffer); + return -1; + } + + setsockopt(server, SOL_SOCKET, SO_RCVBUF, (char *) &sobufsize, sizeof(sobufsize)); + setsockopt(server, SOL_SOCKET, SO_SNDBUF, (char *) &sobufsize, sizeof(sobufsize)); + + memset((char *) &sa_server, 0x00, sizeof(sa_server)); + sa_server.sin_family = AF_INET; + sa_server.sin_port = htons(nPort); + sa_server.sin_addr = addr_local; + + if ((err = bind(server, (struct sockaddr *) &sa_server, sizeof(sa_server))) < 0) + { + printf("bind failed: %d\n", errno); + close(server); + free(cBuffer); + return -1; + } + + if ((err = listen(server, 2)) != 0) + { + printf("listen failed: %d\n", errno); + close(server); + free(cBuffer); + return -1; + } + + for (;;) + { + printf("TCP server listening.\n"); + + FD_ZERO(&fds); + FD_SET(server, &fds); + tv.tv_sec = 3600; + tv.tv_usec = 0; + + if ((rc = select(FD_SETSIZE, &fds, 0, 0, &tv)) < 0) + { + printf("select failed: %d\n", errno); + break; + } + + if (rc == 0 || FD_ISSET(server, &fds) == 0) + continue; + length = sizeof(sa_client); + + if ((client = accept(server, (struct sockaddr *) &sa_client, &length)) < 0) { + printf("accept faild: %d\n", errno); + continue; + } + + setsockopt(client, SOL_SOCKET, SO_RCVBUF, (char *) &sobufsize, sizeof(sobufsize)); + setsockopt(client, SOL_SOCKET, SO_SNDBUF, (char *) &sobufsize, sizeof(sobufsize)); + + printf("TCP connection established ... "); + + for (;;) + { + if (recv_data(client, (void *) &ctl, CTLSIZE, 0)) + break; + + ctl.cmd = ntohl(ctl.cmd); + ctl.data = ntohl(ctl.data); + + if (ctl.cmd == CMD_C2S) + { + start = rdtsc(); + + printf("\nReceiving from client, packet size %s ... \n", PacketSize(ctl.data)); + cBuffer[0] = 0; + nData = 0; + + do { + for (nByte = 0; nByte < ctl.data; ) + { + rc = recv(client, cBuffer + nByte, ctl.data - nByte, 0); + + if (rc < 0) + { + printf("recv failed: %d\n", errno); + break; + } + + if (rc > 0) + nByte += rc; + } + + nData += ctl.data; + } while (cBuffer[0] == 0 && rc > 0); + + end = rdtsc(); + printf("Time to receive %llu bytes: %llu nsec (ticks %llu)\n", nData, ((end-start)*1000ULL)/freq, end-start); + } else if (ctl.cmd == CMD_S2C) { + start = rdtsc(); + + printf("\nSending to client, packet size %s ... \n", PacketSize(ctl.data)); + cBuffer[0] = 0; + nData = 0; + + do + { + //GenerateRandomData(cBuffer, ctl.data); + + for (nByte = 0; nByte < ctl.data; ) + { + rc = send(client, cBuffer + nByte, ctl.data - nByte, 0); + + if (rc < 0) + { + printf("send failed: %d\n", errno); + break; + } + + if (rc > 0) + nByte += rc; + } + + nData += ctl.data; + end = rdtsc(); + } while((end-start)/freq < 6000000ULL /* = 6s */); + + cBuffer[0] = 1; + + if (send_data(client, cBuffer, ctl.data, 0)) + break; + + end = rdtsc(); + printf("Time to send %llu bytes: %llu nsec (ticks %llu)\n", nData, ((end-start)*1000ULL)/freq, end-start); + } else /* quit */ + break; + } + + printf("\nDone.\n"); + + close(client); + + if (rc < 0) + break; + } + + close(server); + free(cBuffer); + + return 0; +} + +int TCP_Bench(void) +{ + char *cBuffer; + CONTROL ctl; + uint64_t nData; + int i; + struct sockaddr_in sa_server; + int server; + int rc, err; + int nByte; + uint64_t start, end; + uint32_t freq = get_cpufreq(); /* in MHz */ + + if ((cBuffer = InitBuffer(TMAXSIZE)) == NULL) + { + printf("Netio: Not enough memory\n"); + return -1; + } + + if ((server = socket(PF_INET, SOCK_STREAM, 0)) < 0) + { + printf("socket failed: %d\n", errno); + free(cBuffer); + return -2; + } + + setsockopt(server, SOL_SOCKET, SO_RCVBUF, (char *) &sobufsize, sizeof(sobufsize)); + setsockopt(server, SOL_SOCKET, SO_SNDBUF, (char *) &sobufsize, sizeof(sobufsize)); + + sa_server.sin_family = AF_INET; + sa_server.sin_port = htons(nPort); + sa_server.sin_addr = addr_server; + + if ((err = connect(server, (struct sockaddr *) &sa_server, sizeof(sa_server))) < 0) + { + printf("connect failed: %d\n", errno); + close(server); + free(cBuffer); + return -2; + } + + printf("\nTCP connection established.\n"); + + for (i = 0; i < ntSizes; i++) + { + printf("Packet size %s bytes: ", PacketSize(tSizes[i])); + + /* tell the server we will send it data now */ + + ctl.cmd = htonl(CMD_C2S); + ctl.data = htonl(tSizes[i]); + + if (send_data(server, (void *) &ctl, CTLSIZE, 0)) + break; + + /* 1 - Tx test */ + + start = rdtsc(); + nData = 0; + cBuffer[0] = 0; + + do + { + //GenerateRandomData(cBuffer, tSizes[i]); + + for (nByte = 0; nByte < tSizes[i]; ) + { + rc = send(server, cBuffer + nByte, tSizes[i] - nByte, 0); + + if (rc < 0) + { + printf("send failed: %d\n", errno); + return -1; + } + + if (rc > 0) + nByte += rc; + } + + nData += tSizes[i]; + end = rdtsc(); + } while((end-start)/freq < 6000000ULL /* = 6s */); + + printf("%llu/100 MBytes/s", ((100ULL*nData)/(1024ULL*1024ULL))/((end-start)/(1000000ULL*freq))); + + printf(" Tx, "); + + cBuffer[0] = 1; + + if (send_data(server, cBuffer, tSizes[i], 0)) + break; + + /* tell the server we expect him to send us data now */ + + ctl.cmd = htonl(CMD_S2C); + ctl.data = htonl(tSizes[i]); + + if (send_data(server, (void *) &ctl, CTLSIZE, 0)) + break; + + /* 2 - Rx test */ + + start = rdtsc(); + nData = 0; + cBuffer[0] = 0; + rc = 0; + + do + { + for (nByte = 0; nByte < tSizes[i]; ) + { + rc = recv(server, cBuffer + nByte, tSizes[i] - nByte, 0); + + if (rc < 0) + { + printf("recv failed: %d\n", errno); + return -1; + } + + if (rc > 0) + nByte += rc; + } + + nData += tSizes[i]; + } while (cBuffer[0] == 0 && rc > 0); + + end = rdtsc(); + printf("%llu/100 MBytes/s", ((100ULL*nData)/(1024ULL*1024ULL))/((end-start)/(1000000ULL*freq))); + + printf(" Rx.\n"); + } + + ctl.cmd = htonl(CMD_QUIT); + ctl.data = 0; + + send_data(server, (void *) &ctl, CTLSIZE, 0); + + printf("Done.\n"); + + close(server); + free(cBuffer); + + return 0; +} + +int main(int argc, char** argv) +{ + int err = 0; + + addr_local.s_addr = INADDR_ANY; + //addr_server.s_addr = inet_addr("192.168.28.254"); + addr_server.s_addr = inet_addr("192.168.28.1"); + + err = TCPServer(); + + return err; +}