blob: 7ce1a44a0d3ae09714564776f6baf03ee0b7c1d7 [file] [log] [blame]
/*
* Copyright 2010
* Princeton 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:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY PRINCETON UNIVERSITY ``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 PRINCETON UNIVERSITY OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of Princeton University.
*
*/
/*
* @(#)uptcp_api.c (Princeton) 11/10/2010
*
* A library for UpTCP's Socket API Layer (SAL). SAL uses a local Unix domain to
* communicate with UpTCP's deamon process. This library has a global variable
* "SAL_local_socket" which is a file descriptor of local socket.
*
*/
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/un.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/time.h>
#include "uptcp_daemon.h"
#include "host_serv.h"
int mp_ncpus;
int mp_maxid;
extern void vnet_hif_init(const void *unused );
extern void* clock_intr_handler(void* arg);
extern int bsd_uptcp_init();
extern int bsd_syscall_socket(int sockfd,int domain, int type, int protocol);
extern int bsd_syscall_bind(int sockfd, char* name, int namelen);
extern int bsd_syscall_listen(int sockfd, int backlog);
extern int bsd_syscall_accept(int sockfd, int newfd, struct sockaddr *name, socklen_t *anamelen);
extern int bsd_syscall_connect(int sockfd, char* name, int namelen);
extern int bsd_syscall_socketpair(int sockfd, int domain, int type, int protocol, int *rsv);
extern int bsd_syscall_sendto(int sockfd, char* buf, size_t len, int flags, char* to, int tolen);
extern int bsd_syscall_send(int sockfd, char* buf, int len, int flags);
extern int bsd_syscall_sendmsg(int sockfd, char* msg, int flags);
extern int bsd_syscall_recvfrom(int sockfd, char* buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlenaddr);
extern int bsd_syscall_recv(int sockfd, char* buf, int len, int flags);
extern int bsd_syscall_recvmsg(int sockfd, struct msghdr *msg, int flags);
extern int bsd_syscall_shutdown(int sockfd, int how);
extern int bsd_syscall_setsockopt(int sockfd, int level, int name, char* val, int valsize);
extern int bsd_syscall_getsockopt(int sockfd, int level, int name, void * val, socklen_t *avalsize);
extern int bsd_syscall_getsockname(int sockfd, struct sockaddr *asa, socklen_t *alen) ;
extern int bsd_syscall_getpeername(int sockfd, struct sockaddr *asa, socklen_t *alen);
void uptcp_print_statis();
//#define UPTCP_API_DEBUG
#ifndef UPTCP_API_DEBUG
#define UPTCP_API_DEBUG_PRINT(fmt, ...)
#else
#define UPTCP_API_DEBUG_PRINT(fmt, args...) \
do{ \
char str[256]; \
sprintf(str, "[API] %s", (fmt)); \
printf(str, ## args); \
}while(0);
#endif
#define SOCKADDR_LINUX2BSD(bsd, len) \
do{\
unsigned char *ptr = (unsigned char*)(bsd);\
unsigned char ch = (unsigned char)((bsd)->sa_family); \
*ptr = (unsigned char)(len); \
ptr++;\
*ptr = (unsigned char)ch;\
}while(0)
#define SOCKADDR_BSD2LINUX(bsd) \
do{\
unsigned char *ptr = (unsigned char*)(bsd);\
(bsd)->sa_family = (sa_family_t)(*(ptr+1));\
}while(0)
struct timeval global_roi_start_tv, global_roi_end_tv;
static int uptcp_initialized;
static pthread_mutex_t uptcp_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static int uptcp_accept_fd = 1024;
static pthread_mutex_t uptcp_accept_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Implementation
*/
void uptcp_init()
{
pthread_t pthread;
/* 1. initialize all TCP/IP stack */
if(!uptcp_initialized){
/* 1.1 initialize global thread list */
memset(thread_list, 0, THREAD_LIST_SIZE*sizeof(thread_wrapper));
memset(thread_index, 0xff, THREAD_INDEX_SIZE);
pthread_mutex_lock(&uptcpip_thread_count_mutex);
thread_wrapper * tw_ptr;
tw_ptr = &thread_list[uptcpip_thread_count];
tw_ptr->ptr = pthread_self(); /* main thread */
tw_ptr->id = uptcpip_thread_count; //start from "0"
tw_ptr->type = THREAD_TYPE_APP ;
int hash = (unsigned long)tw_ptr->ptr % THREAD_INDEX_SIZE;
thread_index[hash] = tw_ptr->id;
uptcpip_thread_count ++;
pthread_mutex_unlock(&uptcpip_thread_count_mutex);
/* 1.2 initialize mp_maxcpus */
char * rx_threads;
rx_threads = getenv ("UPTCPIP_RX_THREADS");
if (rx_threads != NULL)
mp_ncpus = atoi(rx_threads);
else mp_ncpus = 1;
mp_maxid = mp_ncpus;
/* 1.3 initialize TCP/IP stack */
bsd_uptcp_init();
vnet_hif_init(NULL);
/* setup clock */
if(pthread_create_wrapper(&pthread, NULL, clock_intr_handler, NULL, THREAD_TYPE_TIMER) < 0){
printf("uptcp_init(): pthread_create() failed\n");
}
else uptcp_initialized = 1;
}
}
void parsec_enter_tcpip_roi()
{
int i;
pthread_mutex_lock(&uptcp_init_mutex);
if(!uptcp_initialized){
/* initialize TCP/IP stack */
uptcp_init();
uptcp_initialized = 1;
}
pthread_mutex_unlock(&uptcp_init_mutex);
printf("\n");
for(i = 0; i < 60; i ++) printf("="); printf("\n");
printf(" Enter PARSEC TCPIP ROI \n");
for(i = 0; i < 60; i ++) printf("="); printf("\n");
fflush(stdout);
gettimeofday(&global_roi_start_tv, NULL);
enter_tcpip_roi();
}
void parsec_exit_tcpip_roi()
{
int i;
gettimeofday(&global_roi_end_tv, NULL);
exit_tcpip_roi();
uptcp_print_statis();
for(i = 0; i < 60; i ++) printf("="); printf("\n");
printf(" Exit PARSEC TCPIP ROI \n");
for(i = 0; i < 60; i ++) printf("="); printf("\n\n");
fflush(stdout);
}
char * thread_type_str [] = {
"App",
"Socket",
"Rx_Tcpip",
"Tx_Link",
"Rx_Link",
"Timer",
NULL
};
void uptcp_print_statis()
{
int i;
printf("\n");
for(i = 0; i < 60; i ++) printf("-"); printf("\n");
printf(" Execution Statistics of u-TCP/IP Stack \n");
for(i = 0; i < 60; i ++) printf("-"); printf("\n");
int not_involved = 0;
for(i = 0; i < uptcpip_thread_count; i++)
if(thread_list[i].type == THREAD_TYPE_APP)
not_involved ++;
printf("There are %d threads, %d involved to u-TCP/IP stack\n", uptcpip_thread_count, (uptcpip_thread_count - not_involved));
printf("(u-TCP/IP_Threads) / (Total_Threads) = %4.2f\%\n", 100.0*((uptcpip_thread_count - not_involved))/uptcpip_thread_count);
printf("\n");
unsigned long long interval = (global_roi_end_tv.tv_sec * 1000000 + global_roi_end_tv.tv_usec) - (global_roi_start_tv.tv_sec * 1000000 + global_roi_start_tv.tv_usec);
printf("ROI_RUN_TIME = %6.2fs\n", interval/1000000.0);
printf("\n");
#ifdef PERFORMANCE_TUNING
printf("%16s%20s%20s%24s\n", "THREAD_TYPE", "ACTIVE_TIME", "ACTIVE(\%)", "PERF_ROI");
#else
printf("%16s%20s%20s\n", "THREAD_TYPE", "ACTIVE_TIME", "ACTIVE(\%)");
#endif
for(i = 0; i < uptcpip_thread_count; i ++){
if(thread_list[i].type == THREAD_TYPE_APP)
continue;
printf("%16s", thread_type_str[thread_list[i].type]);
printf("%13s%6.3fs", " ", thread_list[i].active_time/1000000.0);
printf("%14s%6.2f", " ", 100.0*thread_list[i].active_time/interval);
#ifdef PERFORMANCE_TUNING
if(thread_list[i].perf_enter_roi_cnt != 0){
printf("%10s(Enter:%llu, Exit:%llu)\t%4.3fs / %llu = %4.1fus\n", " ",
thread_list[i].perf_roi_time/1000000.0,
thread_list[i].perf_enter_roi_cnt,
thread_list[i].perf_exit_roi_cnt,
1.0*thread_list[i].perf_roi_time/thread_list[i].perf_exit_roi_cnt);
printf("\t\t\t%16s %16s %16s\n", "Overhead(us)", "Count", "%");
int j;
for(j = 0; j < 100; j++)
if(thread_list[i].perf_time_dist[j] != 0)
printf("\t\t\t%16d %16d %16.2f\n",
j,
thread_list[i].perf_time_dist[j],
100.0*thread_list[i].perf_time_dist[j]/thread_list[i].perf_exit_roi_cnt);
}
#endif
printf("\n");
}
for(i = 0; i < 60; i ++) printf("-"); printf("\n");
printf("\n");
#ifdef LOCK_PROFILING
printf("\n+++++++++++ Lock Profiling Begin ++++++++++\n");
for(i = 0; i < MAX_LOCK_TRACE; i ++){
if(lock_profile[i].lock_addr != 0){
if(lock_profile[i].lock_cond == 0){
printf("[%d]\t%24s%12u%12llu%12llu\n", i,
host_get_lock_name((void*)lock_profile[i].lock_addr),
lock_profile[i].lock_cnt,
lock_profile[i].wait_time,
lock_profile[i].hold_time);
unsigned long long tmask = lock_profile[i].threads_mask;
int j = 0;
while(tmask != 0){
if(tmask & 0x1)
printf("\t%36s\n", thread_type_str[thread_list[j].type]);
j++;
tmask = (tmask >> 1);
}
#ifdef ENABLE_DUMP_STACK
stack_trace *p_stack_log = lock_profile[i].stack_log;
if(p_stack_log != 0){
int k;
printf("\t\t\t--------------\n");
for(k = 0; k < MAX_STACK_TRACK; k++){
if(p_stack_log[k].hash != 0){
printf("\t\t\t%10s:%6d %s\n",
thread_type_str[thread_list[p_stack_log[k].tid].type],
p_stack_log[k].cnt,
p_stack_log[k].str);
}
}
}
#endif
}else{
printf("[%d]\t%18s(cond)%12u%12llu%12llu\n", i,
host_get_lock_name((void*)lock_profile[i].lock_cond),
lock_profile[i].lock_cnt,
lock_profile[i].wait_time,
lock_profile[i].hold_time);
}
}
}
printf("\n+++++++++++ Lock Profiling End ++++++++++\n");
#endif
#ifdef ENABLE_TIME_PROBE
printf("\n+++++++++++ Time Probe Begin ++++++++++\n");
printf("\n");
printf("%5s", " ");
for(i = 0; i < MAX_TIME_PROBES; i++)
if(global_time_probes[i].timestamp.tv_sec != 0)
printf("%2d->%-2d ", i, global_time_probes[i].pre_index);
printf("\n");
printf("%5s", " ");
for(i = 0; i < MAX_TIME_PROBES; i++)
if(global_time_probes[i].timestamp.tv_sec != 0)
printf("%7d", global_time_probes[i].pass_cnt);
printf("\n");
printf("%5s", " ");
for(i = 0; i < MAX_TIME_PROBES; i++)
if(global_time_probes[i].timestamp.tv_sec != 0)
printf("%7.1f", 1.0*global_time_probes[i].pass_time/global_time_probes[i].pass_cnt);
printf("\n");
int j;
for(j = 0; j < 50; j++){
printf("%-5d", j);
for(i = 0; i < MAX_TIME_PROBES; i++)
if(global_time_probes[i].timestamp.tv_sec != 0)
printf("%7.1f",
global_time_probes[i].time_dist[j],
100.0*global_time_probes[i].time_dist[j]/global_time_probes[i].pass_cnt);
printf("\n");
}
printf("\n+++++++++++ Time Probe End ++++++++++\n");
#endif
}
/*
* @uptcp_accept()
*
* A new socket is created when application calls accept().
*
*/
int uptcp_accept(int s, struct sockaddr * name, socklen_t * anamelen)
{
int newfd = -1;
UPTCP_API_DEBUG_PRINT(">> uptcp_accept(): sockfd = %d\n", s)
pthread_mutex_lock(&uptcp_accept_fd_mutex);
newfd = uptcp_accept_fd;
uptcp_accept_fd ++;
pthread_mutex_unlock(&uptcp_accept_fd_mutex);
/*convert freebsd.sockaddr to linux.sockaddr */
if(bsd_syscall_accept(s, newfd, name, anamelen) < 0){
printf("uptcp_accept(): bsd_syscall_accept() failed\n");
return -1;
}
SOCKADDR_BSD2LINUX(name);
UPTCP_API_DEBUG_PRINT(">> uptcp_accept(): newfd = %d\n", newfd)
return newfd;
}
int uptcp_bind(int s, const struct sockaddr * name, socklen_t namelen)
{
UPTCP_API_DEBUG_PRINT(">> uptcp_bind(): sockfd = %d\n", s)
/*convert local.sockaddr to freebsd.sockaddr */
SOCKADDR_LINUX2BSD(name, namelen);
return bsd_syscall_bind(s, (void*)name, namelen);
}
int uptcp_connect(int s, const struct sockaddr * name, socklen_t namelen)
{
UPTCP_API_DEBUG_PRINT(">> uptcp_connect(): sockfd = %d\n", s)
/*convert local.sockaddr to freebsd.sockaddr */
SOCKADDR_LINUX2BSD(name, namelen);
return bsd_syscall_connect(s, (void*)name, namelen);
}
int uptcp_getpeername(int s, struct sockaddr * asa, socklen_t * alen)
{
return bsd_syscall_getpeername(s, asa, alen);
}
int uptcp_getsockname(int s, struct sockaddr * asa, socklen_t * alen)
{
return bsd_syscall_getsockname(s, asa, alen);
}
int uptcp_getsockopt(int s, int level, int name, void * val, socklen_t * avalsize)
{
return bsd_syscall_getsockopt(s, level, name, val, avalsize);
}
int uptcp_listen(int s, int backlog)
{
UPTCP_API_DEBUG_PRINT(">> uptcp_listen(): sockfd = %d\n", s)
return bsd_syscall_listen(s, backlog);
}
ssize_t uptcp_recv(int s, void * buf, size_t len, int flags)
{
UPTCP_API_DEBUG_PRINT(">> uptcp_recv(): sockfd = %d", s)
int thread_id = host_thread_enter_tcpip_region(THREAD_TYPE_SOCKET);
ssize_t res = bsd_syscall_recv(s, buf, len, flags);
host_thread_exit_tcpip_region(thread_id);
return res;
}
ssize_t uptcp_recvfrom(int s, void * buf, size_t len, int flags, struct sockaddr * from, socklen_t * fromlenaddr)
{
return bsd_syscall_recvfrom(s, buf, len, flags, from, fromlenaddr);
}
ssize_t uptcp_recvmsg(int s, struct msghdr * msg, int flags)
{
return bsd_syscall_recvmsg(s, msg, flags);
}
ssize_t uptcp_send(int s, void * msg, size_t len, int flags)
{
UPTCP_API_DEBUG_PRINT(">> uptcp_send(): sockfd = %d, len = %d\n", s, len)
int thread_id = host_thread_enter_tcpip_region(THREAD_TYPE_SOCKET);
ssize_t res = bsd_syscall_send(s, msg, len, flags);
host_thread_exit_tcpip_region(thread_id);
return res;
}
ssize_t uptcp_sendto(int s, const void * buf, size_t len, int flags, const struct sockaddr * to, socklen_t tolen)
{
return bsd_syscall_sendto(s, (char*)buf, len, flags, (char*)to, tolen);
}
ssize_t uptcp_sendmsg(int s, const struct msghdr * msg, int flags)
{
return bsd_syscall_sendmsg(s, (char*)msg, flags);
}
int uptcp_setsockopt(int s, int level, int name, const void * val, socklen_t valsize)
{
return bsd_syscall_setsockopt(s, level, name, (char*)val, valsize);
}
int uptcp_shutdown(int s, int how)
{
int res;
UPTCP_API_DEBUG_PRINT(">> uptcp_shutdown(): sockfd = %d\n", s)
res = bsd_syscall_shutdown(s, how);
if(how == SHUT_RDWR)
close(s);
return res;
}
int uptcp_close(int s)
{
return uptcp_shutdown(s, SHUT_RDWR);
}
int uptcp_sockatmark(int arg)
{
printf("FIXME: sockatmark()\n");
return -1;
}
/*
* @uptcp_socket()
*
* Upon application's socket(), we create a local AF_UNIX socket
* to communicate with UpTCP daemon's local socket.
*/
int uptcp_socket(int domain, int type, int protocol)
{
int sockfd;
UPTCP_API_DEBUG_PRINT(">> uptcp_socket(): (do, type, pro) = (%d, %d, %d)\n", domain, type, protocol)
/* initialize all TCP/IP stack */
pthread_mutex_lock(&uptcp_init_mutex);
if(!uptcp_initialized){
/* initialize TCP/IP stack */
uptcp_init();
uptcp_initialized = 1;
}
pthread_mutex_unlock(&uptcp_init_mutex);
if((sockfd = open("/dev/null", O_RDWR)) < 0){
printf("uptcp_socket(): open() failed\n");
return -1;
}
return bsd_syscall_socket(sockfd, domain, type, protocol);
}
int uptcp_socketpair(int s, int domain, int type, int protocol, int * rsv)
{
return bsd_syscall_socketpair(s, domain, type, protocol, rsv);
}
int uptcp_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
{
pthread_mutex_lock(&uptcp_init_mutex);
if(!uptcp_initialized){
/* initialize TCP/IP stack */
uptcp_init();
uptcp_initialized = 1;
}
pthread_mutex_unlock(&uptcp_init_mutex);
return pthread_create_wrapper(thread, attr, start_routine, arg, THREAD_TYPE_APP);
}