Browse code

- for tcp read processes, reuse the read fd for sending - keep a tcp send fd cache (experimental) (performance improvement)

Andrei Pelinescu-Onciul authored on 27/11/2007 21:05:32
Showing 3 changed files
... ...
@@ -128,6 +128,7 @@ struct tcp_connection{
128 128
 	gen_lock_t write_lock;
129 129
 	int id; /* id (unique!) used to retrieve a specific connection when
130 130
 	           reply-ing*/
131
+	int reader_pid; /* pid of the active reader process */
131 132
 	struct receive_info rcv; /* src & dst ip, ports, proto a.s.o*/
132 133
 	struct tcp_req req; /* request data */
133 134
 	atomic_t refcnt;
... ...
@@ -84,6 +84,7 @@
84 84
  *  2007-11-22  always add the connection & clear the coresponding flags before
85 85
  *               io_watch_add-ing its fd - it's safer this way (andrei)
86 86
  *  2007-11-26  improved tcp timers: switched to local_timer (andrei)
87
+ *  2007-11-27  added send fd cache and reader fd reuse (andrei)
87 88
  */
88 89
 
89 90
 
... ...
@@ -176,6 +177,23 @@
176 176
 enum fd_types { F_NONE, F_SOCKINFO /* a tcp_listen fd */,
177 177
 				F_TCPCONN, F_TCPCHILD, F_PROC };
178 178
 
179
+
180
+#define TCP_FD_CACHE
181
+
182
+#ifdef TCP_FD_CACHE
183
+
184
+#define TCP_FD_CACHE_SIZE 8
185
+
186
+struct fd_cache_entry{
187
+	struct tcp_connection* con;
188
+	int id;
189
+	int fd;
190
+};
191
+
192
+
193
+static struct fd_cache_entry fd_cache[TCP_FD_CACHE_SIZE];
194
+#endif /* TCP_FD_CACHE */
195
+
179 196
 static int is_tcp_main=0;
180 197
 
181 198
 int tcp_accept_aliases=0; /* by default don't accept aliases */
... ...
@@ -971,6 +989,48 @@ error:
971 971
 
972 972
 
973 973
 
974
+#ifdef TCP_FD_CACHE
975
+
976
+static void tcp_fd_cache_init()
977
+{
978
+	int r;
979
+	for (r=0; r<TCP_FD_CACHE_SIZE; r++)
980
+		fd_cache[r].fd=-1;
981
+}
982
+
983
+
984
+inline static struct fd_cache_entry* tcp_fd_cache_get(struct tcp_connection *c)
985
+{
986
+	int h;
987
+	
988
+	h=c->id%TCP_FD_CACHE_SIZE;
989
+	if ((fd_cache[h].fd>0) && (fd_cache[h].id==c->id) && (fd_cache[h].con==c))
990
+		return &fd_cache[h];
991
+	return 0;
992
+}
993
+
994
+
995
+inline static void tcp_fd_cache_rm(struct fd_cache_entry* e)
996
+{
997
+	e->fd=-1;
998
+}
999
+
1000
+
1001
+inline static void tcp_fd_cache_add(struct tcp_connection *c, int fd)
1002
+{
1003
+	int h;
1004
+	
1005
+	h=c->id%TCP_FD_CACHE_SIZE;
1006
+	if (fd_cache[h].fd>0)
1007
+		close(fd_cache[h].fd);
1008
+	fd_cache[h].fd=fd;
1009
+	fd_cache[h].id=c->id;
1010
+	fd_cache[h].con=c;
1011
+}
1012
+
1013
+#endif /* TCP_FD_CACHE */
1014
+
1015
+
974 1016
 /* finds a tcpconn & sends on it
975 1017
  * uses the dst members to, proto (TCP|TLS) and id and tries to send
976 1018
  *  from the "from" address (if non null and id==0)
... ...
@@ -986,21 +1046,27 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
986 986
 	int fd;
987 987
 	long response[2];
988 988
 	int n;
989
+	int do_close_fd;
990
+#ifdef TCP_FD_CACHE
991
+	struct fd_cache_entry* fd_cache_e;
989 992
 	
993
+	fd_cache_e=0;
994
+#endif /* TCP_FD_CACHE */
995
+	do_close_fd=1; /* close the fd on exit */
990 996
 	port=su_getport(&dst->to);
991
-	if (port){
997
+	if (likely(port)){
992 998
 		su2ip_addr(&ip, &dst->to);
993 999
 		c=tcpconn_get(dst->id, &ip, port, from, tcp_con_lifetime); 
994
-	}else if (dst->id){
1000
+	}else if (likely(dst->id)){
995 1001
 		c=tcpconn_get(dst->id, 0, 0, 0, tcp_con_lifetime);
996 1002
 	}else{
997 1003
 		LOG(L_CRIT, "BUG: tcp_send called with null id & to\n");
998 1004
 		return -1;
999 1005
 	}
1000 1006
 	
1001
-	if (dst->id){
1002
-		if (c==0) {
1003
-			if (port){
1007
+	if (likely(dst->id)){
1008
+		if (unlikely(c==0)) {
1009
+			if (likely(port)){
1004 1010
 				/* try again w/o id */
1005 1011
 				c=tcpconn_get(0, &ip, port, from, tcp_con_lifetime);
1006 1012
 				goto no_id;
... ...
@@ -1012,10 +1078,10 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1012 1012
 		}else goto get_fd;
1013 1013
 	}
1014 1014
 no_id:
1015
-		if (c==0){
1015
+		if (unlikely(c==0)){
1016 1016
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
1017 1017
 			/* create tcp connection */
1018
-			if (from==0){
1018
+			if (likely(from==0)){
1019 1019
 				/* check to see if we have to use a specific source addr. */
1020 1020
 				switch (dst->to.s.sa_family) {
1021 1021
 					case AF_INET:
... ...
@@ -1031,7 +1097,7 @@ no_id:
1031 1031
 						break;
1032 1032
 				}
1033 1033
 			}
1034
-			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
1034
+			if (unlikely((c=tcpconn_connect(&dst->to, from, dst->proto))==0)){
1035 1035
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
1036 1036
 				return -1;
1037 1037
 			}
... ...
@@ -1042,7 +1108,7 @@ no_id:
1042 1042
 			response[0]=(long)c;
1043 1043
 			response[1]=CONN_NEW;
1044 1044
 			n=send_fd(unix_tcp_sock, response, sizeof(response), c->s);
1045
-			if (n<=0){
1045
+			if (unlikely(n<=0)){
1046 1046
 				LOG(L_ERR, "BUG: tcp_send: failed send_fd: %s (%d)\n",
1047 1047
 						strerror(errno), errno);
1048 1048
 				n=-1;
... ...
@@ -1051,14 +1117,27 @@ no_id:
1051 1051
 			goto send_it;
1052 1052
 		}
1053 1053
 get_fd:
1054
-			/* todo: see if this is not the same process holding
1055
-			 *  c  and if so send directly on c->fd */
1054
+		/* check if this is not the same reader process holding
1055
+		 *  c  and if so send directly on c->fd */
1056
+		if (c->reader_pid==my_pid()){
1057
+			WARN("tcp_send: FIXME: send from reader (%d (%d)), reusing fd\n",
1058
+					my_pid(), process_no);
1059
+			fd=c->fd;
1060
+			do_close_fd=0; /* don't close the fd on exit, it's in use */
1061
+#ifdef TCP_FD_CACHE
1062
+		}else if (likely((fd_cache_e=tcp_fd_cache_get(c))!=0)){
1063
+			fd=fd_cache_e->fd;
1064
+			do_close_fd=0;
1065
+			WARN("tcp_send: FIXME: found fd in cache ( %d, %p, %d)\n",
1066
+					fd, c, fd_cache_e->id);
1067
+#endif /* TCP_FD_CACHE */
1068
+		}else{
1056 1069
 			DBG("tcp_send: tcp connection found (%p), acquiring fd\n", c);
1057 1070
 			/* get the fd */
1058 1071
 			response[0]=(long)c;
1059 1072
 			response[1]=CONN_GET_FD;
1060 1073
 			n=send_all(unix_tcp_sock, response, sizeof(response));
1061
-			if (n<=0){
1074
+			if (unlikely(n<=0)){
1062 1075
 				LOG(L_ERR, "BUG: tcp_send: failed to get fd(write):%s (%d)\n",
1063 1076
 						strerror(errno), errno);
1064 1077
 				n=-1;
... ...
@@ -1066,13 +1145,13 @@ get_fd:
1066 1066
 			}
1067 1067
 			DBG("tcp_send, c= %p, n=%d\n", c, n);
1068 1068
 			n=receive_fd(unix_tcp_sock, &tmp, sizeof(tmp), &fd, MSG_WAITALL);
1069
-			if (n<=0){
1069
+			if (unlikely(n<=0)){
1070 1070
 				LOG(L_ERR, "BUG: tcp_send: failed to get fd(receive_fd):"
1071 1071
 							" %s (%d)\n", strerror(errno), errno);
1072 1072
 				n=-1;
1073 1073
 				goto release_c;
1074 1074
 			}
1075
-			if (c!=tmp){
1075
+			if (unlikely(c!=tmp)){
1076 1076
 				LOG(L_CRIT, "BUG: tcp_send: get_fd: got different connection:"
1077 1077
 						"  %p (id= %d, refcnt=%d state=%d) != "
1078 1078
 						"  %p (n=%d)\n",
... ...
@@ -1083,7 +1162,7 @@ get_fd:
1083 1083
 				goto end;
1084 1084
 			}
1085 1085
 			DBG("tcp_send: after receive_fd: c= %p n=%d fd=%d\n",c, n, fd);
1086
-		
1086
+		}
1087 1087
 	
1088 1088
 	
1089 1089
 send_it:
... ...
@@ -1099,7 +1178,7 @@ send_it:
1099 1099
 	lock_release(&c->write_lock);
1100 1100
 	DBG("tcp_send: after write: c= %p n=%d fd=%d\n",c, n, fd);
1101 1101
 	DBG("tcp_send: buf=\n%.*s\n", (int)len, buf);
1102
-	if (n<0){
1102
+	if (unlikely(n<0)){
1103 1103
 		LOG(L_ERR, "ERROR: tcp_send: failed to send\n");
1104 1104
 		/* error on the connection , mark it as bad and set 0 timeout */
1105 1105
 		c->state=S_CONN_BAD;
... ...
@@ -1115,11 +1194,25 @@ send_it:
1115 1115
 		}
1116 1116
 		/* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put 
1117 1117
 		 * if it succeeds */
1118
-		close(fd);
1118
+#ifdef TCP_FD_CACHE
1119
+		if (unlikely(fd_cache_e)){
1120
+			LOG(L_ERR, "ERROR: tcp_send: error on cached fd, removing from the"
1121
+					"cache (%d, %p, %d)\n", 
1122
+					fd, fd_cache_e->con, fd_cache_e->id);
1123
+			tcp_fd_cache_rm(fd_cache_e);
1124
+			close(fd);
1125
+		}else
1126
+#endif /* TCP_FD_CACHE */
1127
+		if (do_close_fd) close(fd);
1119 1128
 		return n; /* error return, no tcpconn_put */
1120 1129
 	}
1121 1130
 end:
1122
-	close(fd);
1131
+#ifdef TCP_FD_CACHE
1132
+	if (unlikely(fd_cache_e==0)){
1133
+		tcp_fd_cache_add(c, fd);
1134
+	}else
1135
+#endif /* TCP_FD_CACHE */
1136
+	if (do_close_fd) close(fd);
1123 1137
 release_c:
1124 1138
 	tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */
1125 1139
 	return n;
... ...
@@ -1248,6 +1341,9 @@ static void tcpconn_destroy(struct tcp_connection* tcpconn)
1248 1248
 			tls_close(tcpconn, fd);
1249 1249
 #endif
1250 1250
 		_tcpconn_free(tcpconn);
1251
+#ifdef TCP_FD_CACHE
1252
+		shutdown(fd, SHUT_RDWR);
1253
+#endif /* TCP_FD_CACHE */
1251 1254
 		close(fd);
1252 1255
 		(*tcp_connections_no)--;
1253 1256
 	}else{
... ...
@@ -1958,6 +2054,9 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln* tl, void* data)
1958 1958
 						tls_close(c, fd);
1959 1959
 #endif /* USE_TLS */
1960 1960
 					_tcpconn_free(c);
1961
+#ifdef TCP_FD_CACHE
1962
+					shutdown(fd, SHUT_RDWR);
1963
+#endif /* TCP_FD_CACHE */
1961 1964
 					close(fd);
1962 1965
 				}
1963 1966
 				(*tcp_connections_no)--; /* modified only in tcp_main
... ...
@@ -2024,6 +2123,9 @@ static inline void tcpconn_destroy_all()
2024 2024
 #endif
2025 2025
 				_tcpconn_rm(c);
2026 2026
 				if (fd>0) {
2027
+#ifdef TCP_FD_CACHE
2028
+					shutdown(fd, SHUT_RDWR);
2029
+#endif /* TCP_FD_CACHE */
2027 2030
 					close(fd);
2028 2031
 				}
2029 2032
 				(*tcp_connections_no)--;
... ...
@@ -2059,6 +2161,15 @@ void tcp_main_loop()
2059 2059
 		goto error;
2060 2060
 	/* init: start watching all the fds*/
2061 2061
 	
2062
+	/* init local timer */
2063
+	if (init_local_timer(&tcp_main_ltimer, get_ticks_raw())!=0){
2064
+		LOG(L_ERR, "ERROR: init_tcp: failed to init local timer\n");
2065
+		goto error;
2066
+	}
2067
+#ifdef TCP_FD_CACHE
2068
+	tcp_fd_cache_init();
2069
+#endif /* TCP_FD_CACHE */
2070
+	
2062 2071
 	/* add all the sockets we listen on for connections */
2063 2072
 	for (si=tcp_listen; si; si=si->next){
2064 2073
 		if ((si->proto==PROTO_TCP) &&(si->socket!=-1)){
... ...
@@ -2312,11 +2423,6 @@ int init_tcp()
2312 2312
 					poll_method_name(tcp_poll_method));
2313 2313
 	}
2314 2314
 	
2315
-	if (init_local_timer(&tcp_main_ltimer, get_ticks_raw())!=0){
2316
-		LOG(L_ERR, "ERROR: init_tcp: failed to init local timer\n");
2317
-		goto error;
2318
-	}
2319
-	
2320 2315
 	return 0;
2321 2316
 error:
2322 2317
 	/* clean-up */
... ...
@@ -64,6 +64,7 @@
64 64
 #include "timer.h"
65 65
 #include "local_timer.h"
66 66
 #include "ut.h"
67
+#include "pt.h"
67 68
 #ifdef CORE_TLS
68 69
 #include "tls/tls_server.h"
69 70
 #else
... ...
@@ -671,6 +672,7 @@ void release_tcpconn(struct tcp_connection* c, long state, int unix_sock)
671 671
 				c, state, c->fd, c->id);
672 672
 		DBG(" extra_data %p\n", c->extra_data);
673 673
 		/* release req & signal the parent */
674
+		c->reader_pid=0; /* reset it */
674 675
 		if (c->fd!=-1) close(c->fd);
675 676
 		/* errno==EINTR, EWOULDBLOCK a.s.o todo */
676 677
 		response[0]=(long)c;
... ...
@@ -755,6 +757,7 @@ again:
755 755
 									"no fd read\n");
756 756
 				goto con_error;
757 757
 			}
758
+			con->reader_pid=my_pid();
758 759
 			if (unlikely(con==tcp_conn_lst)){
759 760
 				LOG(L_CRIT, "BUG: tcp_receive: handle_io: duplicate"
760 761
 							" connection received: %p, id %d, fd %d, refcnt %d"