Browse code

- tcp children number is not anymore limited (was limited to 100) - tcp connection are passed to the tcp reader processes after some data is received on them and not immediately after accept(). This speeds up accepting lots of new connections. Old behaviour can be select by undef-ing TCP_PASS_NEW_CONNECTION_ON_DATA in tcp_main.c - lots of cleanups and small fixes - tsend_stream now support waiting forever (timeout==-1)

Orientative test results:
- 50k connections to ser opened, 1 packet sent of them and then closed in 9.5s - 50-65Mb data rate on 50k connections (with 20 ser tcp processes)
- 200-240Mb data rate on 1 connection

Andrei Pelinescu-Onciul authored on 03/02/2006 21:56:23
Showing 3 changed files
... ...
@@ -64,8 +64,8 @@
64 64
  *              process all children requests, before attempting to send
65 65
  *              them new stuff (fixes some deadlocks) (andrei)
66 66
  *  2006-02-03  timers are run only once per s (andrei)
67
- *              tcp children fds can be non-blocking; send fds are queues on
68
- *              EAGAIN (andrei)
67
+ *              tcp children fds can be non-blocking; send fds are queued on
68
+ *              EAGAIN; lots of bug fixes (andrei)
69 69
  */
70 70
 
71 71
 
... ...
@@ -112,6 +112,7 @@
112 112
 #include "tcp_server.h"
113 113
 #include "tcp_init.h"
114 114
 #include "tsend.h"
115
+#include "timer_ticks.h"
115 116
 #ifdef USE_TLS
116 117
 #include "tls/tls_server.h"
117 118
 #endif 
... ...
@@ -123,7 +124,9 @@
123 123
 #include "io_wait.h"
124 124
 #include <fcntl.h> /* must be included after io_wait.h if SIGIO_RT is used */
125 125
 
126
-#define MAX_TCP_CHILDREN 100
126
+#define TCP_PASS_NEW_CONNECTION_ON_DATA /* don't pass a new connection
127
+										   immediately to a child, wait for
128
+										   some data on it first */
127 129
 #define TCP_LISTEN_BACKLOG 1024
128 130
 #define SEND_FD_QUEUE /* queue send fd requests on EAGAIN, instead of sending 
129 131
 							them immediately */
... ...
@@ -132,9 +135,10 @@
132 132
 #ifndef TCP_CHILD_NON_BLOCKING
133 133
 #define TCP_CHILD_NON_BLOCKING
134 134
 #endif
135
-#define MAX_SEND_FD_QUEUE_SIZE	1024  /* alternative: tcp_max_fd_no */
135
+#define MAX_SEND_FD_QUEUE_SIZE	tcp_max_fd_no
136 136
 #define SEND_FD_QUEUE_SIZE		128  /* initial size */
137
-#define MAX_SEND_FD_RETRIES		3	 /* FIXME: increase */
137
+#define MAX_SEND_FD_RETRIES		96	 /* FIXME: not used for now */
138
+#define SEND_FD_QUEUE_TIMEOUT	MS_TO_TICKS(2000)  /* 2 s */
138 139
 #endif
139 140
 
140 141
 
... ...
@@ -167,7 +171,7 @@ struct tcp_conn_alias** tcpconn_aliases_hash=0;
167 167
 struct tcp_connection** tcpconn_id_hash=0;
168 168
 gen_lock_t* tcpconn_lock=0;
169 169
 
170
-struct tcp_child tcp_children[MAX_TCP_CHILDREN];
170
+static struct tcp_child* tcp_children;
171 171
 static int* connection_id=0; /*  unique for each connection, used for 
172 172
 								quickly finding the corresponding connection
173 173
 								for a reply */
... ...
@@ -948,11 +952,44 @@ error:
948 948
 
949 949
 
950 950
 
951
+/* used internally by tcp_main_loop() */
952
+static void tcpconn_destroy(struct tcp_connection* tcpconn)
953
+{
954
+	int fd;
955
+
956
+	TCPCONN_LOCK; /*avoid races w/ tcp_send*/
957
+	tcpconn->refcnt--;
958
+	if (tcpconn->refcnt==0){ 
959
+		DBG("tcpconn_destroy: destroying connection %p, flags %04x\n",
960
+				tcpconn, tcpconn->flags);
961
+		fd=tcpconn->s;
962
+#ifdef USE_TLS
963
+		/*FIXME: lock ->writelock ? */
964
+		if (tcpconn->type==PROTO_TLS)
965
+			tls_close(tcpconn, fd);
966
+#endif
967
+		_tcpconn_rm(tcpconn);
968
+		close(fd);
969
+		tcp_connections_no--;
970
+	}else{
971
+		/* force timeout */
972
+		tcpconn->timeout=0;
973
+		tcpconn->state=S_CONN_BAD;
974
+		DBG("tcpconn_destroy: delaying (%p, flags %04x) ...\n",
975
+				tcpconn, tcpconn->flags);
976
+		
977
+	}
978
+	TCPCONN_UNLOCK;
979
+}
980
+
981
+
982
+
951 983
 #ifdef SEND_FD_QUEUE
952 984
 struct send_fd_info{
953 985
 	struct tcp_connection* tcp_conn;
986
+	ticks_t expire;
954 987
 	int unix_sock;
955
-	int retries;
988
+	unsigned int retries; /* debugging */
956 989
 };
957 990
 
958 991
 struct tcp_send_fd_q{
... ...
@@ -1039,6 +1076,7 @@ inline static int send_fd_queue_add(	struct tcp_send_fd_q* q,
1039 1039
 	}
1040 1040
 	q->crt->tcp_conn=t;
1041 1041
 	q->crt->unix_sock=unix_sock;
1042
+	q->crt->expire=get_ticks_raw()+SEND_FD_QUEUE_TIMEOUT;
1042 1043
 	q->crt->retries=0;
1043 1044
 	q->crt++;
1044 1045
 	return 0;
... ...
@@ -1057,15 +1095,19 @@ inline static void send_fd_queue_run(struct tcp_send_fd_q* q)
1057 1057
 		if (send_fd(p->unix_sock, &(p->tcp_conn),
1058 1058
 					sizeof(struct tcp_connection*), p->tcp_conn->s)<=0){
1059 1059
 			if ( ((errno==EAGAIN)||(errno==EWOULDBLOCK)) && 
1060
-							(p->retries<MAX_SEND_FD_RETRIES)){
1060
+							((s_ticks_t)(p->expire-get_ticks_raw())>0)){
1061 1061
 				/* leave in queue for a future try */
1062 1062
 				*t=*p;
1063 1063
 				t->retries++;
1064 1064
 				t++;
1065 1065
 			}else{
1066 1066
 				LOG(L_ERR, "ERROR: run_send_fd_queue: send_fd failed"
1067
-						   "on %d socket, %ld queue entry, retries %d \n",
1068
-						   p->unix_sock, p-&q->data[0], p->retries);
1067
+						   " on socket %d , queue entry %ld, retries %d,"
1068
+						   " connection %p, tcp socket %d, errno=%d (%s) \n",
1069
+						   p->unix_sock, p-&q->data[0], p->retries,
1070
+						   p->tcp_conn, p->tcp_conn->s, errno,
1071
+						   strerror(errno));
1072
+				tcpconn_destroy(p->tcp_conn);
1069 1073
 			}
1070 1074
 		}
1071 1075
 	}
... ...
@@ -1077,38 +1119,6 @@ inline static void send_fd_queue_run(struct tcp_send_fd_q* q)
1077 1077
 
1078 1078
 
1079 1079
 
1080
-/* used internally by tcp_main_loop() */
1081
-static void tcpconn_destroy(struct tcp_connection* tcpconn)
1082
-{
1083
-	int fd;
1084
-
1085
-	TCPCONN_LOCK; /*avoid races w/ tcp_send*/
1086
-	tcpconn->refcnt--;
1087
-	if (tcpconn->refcnt==0){ 
1088
-		DBG("tcpconn_destroy: destroying connection %p, flags %04x\n",
1089
-				tcpconn, tcpconn->flags);
1090
-		fd=tcpconn->s;
1091
-#ifdef USE_TLS
1092
-		/*FIXME: lock ->writelock ? */
1093
-		if (tcpconn->type==PROTO_TLS)
1094
-			tls_close(tcpconn, fd);
1095
-#endif
1096
-		_tcpconn_rm(tcpconn);
1097
-		close(fd);
1098
-		tcp_connections_no--;
1099
-	}else{
1100
-		/* force timeout */
1101
-		tcpconn->timeout=0;
1102
-		tcpconn->state=S_CONN_BAD;
1103
-		DBG("tcpconn_destroy: delaying (%p, flags %04x) ...\n",
1104
-				tcpconn, tcpconn->flags);
1105
-		
1106
-	}
1107
-	TCPCONN_UNLOCK;
1108
-}
1109
-
1110
-
1111
-
1112 1080
 /* handles io from a tcp child process
1113 1081
  * params: tcp_c - pointer in the tcp_children array, to the entry for
1114 1082
  *                 which an io event was detected 
... ...
@@ -1348,10 +1358,14 @@ inline static int send2child(struct tcp_connection* tcpconn)
1348 1348
 	int i;
1349 1349
 	int min_busy;
1350 1350
 	int idx;
1351
+	static int crt=0; /* current child */
1352
+	int last;
1351 1353
 	
1352 1354
 	min_busy=tcp_children[0].busy;
1353 1355
 	idx=0;
1354
-	for (i=0; i<tcp_children_no; i++){
1356
+	last=crt+tcp_children_no;
1357
+	for (; crt<last; crt++){
1358
+		i=crt%tcp_children_no;
1355 1359
 		if (!tcp_children[i].busy){
1356 1360
 			idx=i;
1357 1361
 			min_busy=0;
... ...
@@ -1361,6 +1375,7 @@ inline static int send2child(struct tcp_connection* tcpconn)
1361 1361
 			idx=i;
1362 1362
 		}
1363 1363
 	}
1364
+	crt=idx+1; /* next time we start with crt%tcp_children_no */
1364 1365
 	
1365 1366
 	tcp_children[idx].busy++;
1366 1367
 	tcp_children[idx].n_reqs++;
... ...
@@ -1387,8 +1402,10 @@ inline static int send2child(struct tcp_connection* tcpconn)
1387 1387
 			tcpconn->s)<=0){
1388 1388
 		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
1389 1389
 			/* FIXME: remove after debugging */
1390
-			LOG(L_WARN, "WARNING: tcp child %d, socket %d: queue full\n",
1391
-					idx, tcp_children[idx].unix_sock);
1390
+			 LOG(L_CRIT, "INFO: tcp child %d, socket %d: queue full,"
1391
+					 	" %d requests queued (total handled %d)\n",
1392
+					idx, tcp_children[idx].unix_sock, min_busy,
1393
+					tcp_children[idx].n_reqs-1);
1392 1394
 			if (send_fd_queue_add(&send2child_q, tcp_children[idx].unix_sock, 
1393 1395
 						tcpconn)!=0){
1394 1396
 				LOG(L_ERR, "ERROR: send2child: queue send op. failed\n");
... ...
@@ -1396,6 +1413,7 @@ inline static int send2child(struct tcp_connection* tcpconn)
1396 1396
 			}
1397 1397
 		}else{
1398 1398
 			LOG(L_ERR, "ERROR: send2child: send_fd failed\n");
1399
+			return -1;
1399 1400
 		}
1400 1401
 	}
1401 1402
 #else
... ...
@@ -1450,6 +1468,11 @@ static inline int handle_new_connect(struct socket_info* si)
1450 1450
 	/* add socket to list */
1451 1451
 	tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
1452 1452
 	if (tcpconn){
1453
+#ifdef TCP_PASS_NEW_CONNECTION_ON_DATA
1454
+		io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn);
1455
+		tcpconn->flags&=~F_CONN_REMOVED;
1456
+		tcpconn_add(tcpconn);
1457
+#else
1453 1458
 		tcpconn->refcnt++; /* safe, not yet available to the
1454 1459
 							  outside world */
1455 1460
 		tcpconn_add(tcpconn);
... ...
@@ -1459,14 +1482,9 @@ static inline int handle_new_connect(struct socket_info* si)
1459 1459
 		if(send2child(tcpconn)<0){
1460 1460
 			LOG(L_ERR,"ERROR: handle_new_connect: no children "
1461 1461
 					"available\n");
1462
-			TCPCONN_LOCK;
1463
-			tcpconn->refcnt--;
1464
-			if (tcpconn->refcnt==0){
1465
-				close(tcpconn->s);
1466
-				_tcpconn_rm(tcpconn);
1467
-			}else tcpconn->timeout=0; /* force expire */
1468
-			TCPCONN_UNLOCK;
1462
+			tcpconn_destroy(tcpconn);
1469 1463
 		}
1464
+#endif
1470 1465
 	}else{ /*tcpconn==0 */
1471 1466
 		LOG(L_ERR, "ERROR: handle_new_connect: tcpconn_new failed, "
1472 1467
 				"closing socket\n");
... ...
@@ -1490,8 +1508,6 @@ static inline int handle_new_connect(struct socket_info* si)
1490 1490
  */
1491 1491
 inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i)
1492 1492
 {
1493
-	int fd;
1494
-	
1495 1493
 	/*  is refcnt!=0 really necessary? 
1496 1494
 	 *  No, in fact it's a bug: I can have the following situation: a send only
1497 1495
 	 *   tcp connection used by n processes simultaneously => refcnt = n. In 
... ...
@@ -1516,6 +1532,8 @@ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i)
1516 1516
 	tcpconn_ref(tcpconn); /* refcnt ++ */
1517 1517
 	if (send2child(tcpconn)<0){
1518 1518
 		LOG(L_ERR,"ERROR: handle_tcpconn_ev: no children available\n");
1519
+		tcpconn_destroy(tcpconn);
1520
+#if 0
1519 1521
 		TCPCONN_LOCK;
1520 1522
 		tcpconn->refcnt--;
1521 1523
 		if (tcpconn->refcnt==0){
... ...
@@ -1524,6 +1542,7 @@ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i)
1524 1524
 			close(fd);
1525 1525
 		}else tcpconn->timeout=0; /* force expire*/
1526 1526
 		TCPCONN_UNLOCK;
1527
+#endif
1527 1528
 	}
1528 1529
 	return 0; /* we are not interested in possibly queued io events, 
1529 1530
 				 the fd was either passed to a child, or closed */
... ...
@@ -1631,6 +1650,14 @@ void tcp_main_loop()
1631 1631
 	struct socket_info* si;
1632 1632
 	int r;
1633 1633
 	
1634
+	/* init send fd queues (here because we want mem. alloc only in the tcp
1635
+	 *  process */
1636
+#ifdef SEND_FD_QUEUE
1637
+	if (init_send_fd_queues()<0){
1638
+		LOG(L_CRIT, "ERROR: init_tcp: could not init send fd queues\n");
1639
+		goto error;
1640
+	}
1641
+#endif
1634 1642
 	/* init io_wait (here because we want the memory allocated only in
1635 1643
 	 * the tcp_main process) */
1636 1644
 	
... ...
@@ -1758,6 +1785,9 @@ void tcp_main_loop()
1758 1758
 			goto error;
1759 1759
 	}
1760 1760
 error:
1761
+#ifdef SEND_FD_QUEUE
1762
+	destroy_send_fd_queues();
1763
+#endif
1761 1764
 	destroy_io_wait(&io_h);
1762 1765
 	LOG(L_CRIT, "ERROR: tcp_main_loop: exiting...");
1763 1766
 	exit(-1);
... ...
@@ -1786,9 +1816,10 @@ void destroy_tcp()
1786 1786
 			lock_dealloc((void*)tcpconn_lock);
1787 1787
 			tcpconn_lock=0;
1788 1788
 		}
1789
-#ifdef SEND_FD_QUEUE
1790
-		destroy_send_fd_queues();
1791
-#endif
1789
+		if (tcp_children){
1790
+			pkg_free(tcp_children);
1791
+			tcp_children=0;
1792
+		}
1792 1793
 }
1793 1794
 
1794 1795
 
... ...
@@ -1834,14 +1865,7 @@ int init_tcp()
1834 1834
 			TCP_ALIAS_HASH_SIZE * sizeof(struct tcp_conn_alias*));
1835 1835
 	memset((void*)tcpconn_id_hash, 0, 
1836 1836
 			TCP_ID_HASH_SIZE * sizeof(struct tcp_connection*));
1837
-	/* init send fd queues */
1838 1837
 	
1839
-#ifdef SEND_FD_QUEUE
1840
-	if (init_send_fd_queues()<0){
1841
-		LOG(L_CRIT, "ERROR: init_tcp: could not init send fd queues\n");
1842
-		goto error;
1843
-	}
1844
-#endif
1845 1838
 	/* fix config variables */
1846 1839
 	/* they can have only positive values due the config parser so we can
1847 1840
 	 * ignore most of them */
... ...
@@ -1916,6 +1940,12 @@ int tcp_init_children()
1916 1916
 	tcp_max_fd_no=process_count*2 +r-1 /* timer */ +3; /* stdin/out/err*/
1917 1917
 	tcp_max_fd_no+=tcp_max_connections;
1918 1918
 	
1919
+	/* alloc the children array */
1920
+	tcp_children=pkg_malloc(sizeof(struct tcp_child)*tcp_children_no);
1921
+	if (tcp_children==0){
1922
+			LOG(L_ERR, "ERROR: tcp_init_children: out of memory\n");
1923
+			goto error;
1924
+	}
1919 1925
 	/* create the tcp sock_info structures */
1920 1926
 	/* copy the sockets --moved to main_loop*/
1921 1927
 	
... ...
@@ -34,6 +34,7 @@
34 34
  *              parameter & they set c->state to S_CONN_EOF on eof (andrei)
35 35
  * 2003-07-04  fixed tcp EOF handling (possible infinite loop) (andrei)
36 36
  * 2005-07-05  migrated to the new io_wait code (andrei)
37
+ * 2006-02-03  use tsend_stream instead of send_all (andrei)
37 38
  */
38 39
 
39 40
 #ifdef USE_TCP
... ...
@@ -66,6 +67,7 @@
66 66
 #define HANDLE_IO_INLINE
67 67
 #include "io_wait.h"
68 68
 #include <fcntl.h> /* must be included after io_wait.h if SIGIO_RT is used */
69
+#include "tsend.h"
69 70
 
70 71
 /* types used in io_wait* */
71 72
 enum fd_types { F_NONE, F_TCPMAIN, F_TCPCONN };
... ...
@@ -554,8 +556,9 @@ void release_tcpconn(struct tcp_connection* c, long state, int unix_sock)
554 554
 		/* errno==EINTR, EWOULDBLOCK a.s.o todo */
555 555
 		response[0]=(long)c;
556 556
 		response[1]=state;
557
-		if (send_all(unix_sock, response, sizeof(response))<=0)
558
-			LOG(L_ERR, "ERROR: release_tcpconn: send_all failed\n");
557
+		
558
+		if (tsend_stream(unix_sock, (char*)response, sizeof(response), -1)<=0)
559
+			LOG(L_ERR, "ERROR: release_tcpconn: tsend_stream failed\n");
559 560
 }
560 561
 
561 562
 
... ...
@@ -31,6 +31,7 @@
31 31
  * --------
32 32
  *  2004-02-26  created by andrei
33 33
  *  2003-03-03  switched to heavy macro use, added tsend_dgram_ev (andrei) 
34
+ *  2006-02-03  tsend* will wait forever if timeout==-1 (andrei)
34 35
  */
35 36
 
36 37
 #include <string.h>
... ...
@@ -58,12 +59,16 @@
58 58
 #define TSEND_POLL(f_name) \
59 59
 poll_loop: \
60 60
 	while(1){ \
61
-		diff=expire-get_ticks_raw(); \
62
-		if (diff<=0){ \
63
-			LOG(L_ERR, "ERROR: " f_name ": send timeout (%d)\n", timeout); \
64
-			goto error; \
61
+		if (timeout==-1) \
62
+			n=poll(&pf, 1, -1); \
63
+		else{ \
64
+			diff=expire-get_ticks_raw(); \
65
+			if (diff<=0){ \
66
+				LOG(L_ERR, "ERROR: " f_name ": send timeout (%d)\n", timeout);\
67
+				goto error; \
68
+			} \
69
+			n=poll(&pf, 1, TICKS_TO_MS((ticks_t)diff)); \
65 70
 		} \
66
-		n=poll(&pf, 1, TICKS_TO_MS((ticks_t)diff)); \
67 71
 		if (n<0){ \
68 72
 			if (errno==EINTR) continue; /* signal, ignore */ \
69 73
 			LOG(L_ERR, "ERROR: " f_name ": poll failed: %s [%d]\n", \
... ...
@@ -100,8 +105,10 @@ poll_loop: \
100 100
 	
101 101
 
102 102
 
103
-/* sends on fd (which must be O_NONBLOCK); if it cannot send any data
103
+/* sends on fd (which must be O_NONBLOCK if you want a finite timeout); if it
104
+ * cannot send any data
104 105
  * in timeout milliseconds it will return ERROR
106
+ * if timeout==-1, it waits forever
105 107
  * returns: -1 on error, or number of bytes written
106 108
  *  (if less than len => couldn't send all)
107 109
  *  bugs: signals will reset the timer