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 124
 #include "io_wait.h"
124 125
 #include <fcntl.h> /* must be included after io_wait.h if SIGIO_RT is used */
125 126
 
126
-#define MAX_TCP_CHILDREN 100
127
+#define TCP_PASS_NEW_CONNECTION_ON_DATA /* don't pass a new connection
128
+										   immediately to a child, wait for
129
+										   some data on it first */
127 130
 #define TCP_LISTEN_BACKLOG 1024
128 131
 #define SEND_FD_QUEUE /* queue send fd requests on EAGAIN, instead of sending 
129 132
 							them immediately */
... ...
@@ -132,9 +135,10 @@
132 135
 #ifndef TCP_CHILD_NON_BLOCKING
133 136
 #define TCP_CHILD_NON_BLOCKING
134 137
 #endif
135
-#define MAX_SEND_FD_QUEUE_SIZE	1024  /* alternative: tcp_max_fd_no */
138
+#define MAX_SEND_FD_QUEUE_SIZE	tcp_max_fd_no
136 139
 #define SEND_FD_QUEUE_SIZE		128  /* initial size */
137
-#define MAX_SEND_FD_RETRIES		3	 /* FIXME: increase */
140
+#define MAX_SEND_FD_RETRIES		96	 /* FIXME: not used for now */
141
+#define SEND_FD_QUEUE_TIMEOUT	MS_TO_TICKS(2000)  /* 2 s */
138 142
 #endif
139 143
 
140 144
 
... ...
@@ -167,7 +171,7 @@ struct tcp_conn_alias** tcpconn_aliases_hash=0;
167 171
 struct tcp_connection** tcpconn_id_hash=0;
168 172
 gen_lock_t* tcpconn_lock=0;
169 173
 
170
-struct tcp_child tcp_children[MAX_TCP_CHILDREN];
174
+static struct tcp_child* tcp_children;
171 175
 static int* connection_id=0; /*  unique for each connection, used for 
172 176
 								quickly finding the corresponding connection
173 177
 								for a reply */
... ...
@@ -948,11 +952,44 @@ error:
948 952
 
949 953
 
950 954
 
955
+/* used internally by tcp_main_loop() */
956
+static void tcpconn_destroy(struct tcp_connection* tcpconn)
957
+{
958
+	int fd;
959
+
960
+	TCPCONN_LOCK; /*avoid races w/ tcp_send*/
961
+	tcpconn->refcnt--;
962
+	if (tcpconn->refcnt==0){ 
963
+		DBG("tcpconn_destroy: destroying connection %p, flags %04x\n",
964
+				tcpconn, tcpconn->flags);
965
+		fd=tcpconn->s;
966
+#ifdef USE_TLS
967
+		/*FIXME: lock ->writelock ? */
968
+		if (tcpconn->type==PROTO_TLS)
969
+			tls_close(tcpconn, fd);
970
+#endif
971
+		_tcpconn_rm(tcpconn);
972
+		close(fd);
973
+		tcp_connections_no--;
974
+	}else{
975
+		/* force timeout */
976
+		tcpconn->timeout=0;
977
+		tcpconn->state=S_CONN_BAD;
978
+		DBG("tcpconn_destroy: delaying (%p, flags %04x) ...\n",
979
+				tcpconn, tcpconn->flags);
980
+		
981
+	}
982
+	TCPCONN_UNLOCK;
983
+}
984
+
985
+
986
+
951 987
 #ifdef SEND_FD_QUEUE
952 988
 struct send_fd_info{
953 989
 	struct tcp_connection* tcp_conn;
990
+	ticks_t expire;
954 991
 	int unix_sock;
955
-	int retries;
992
+	unsigned int retries; /* debugging */
956 993
 };
957 994
 
958 995
 struct tcp_send_fd_q{
... ...
@@ -1039,6 +1076,7 @@ inline static int send_fd_queue_add(	struct tcp_send_fd_q* q,
1039 1076
 	}
1040 1077
 	q->crt->tcp_conn=t;
1041 1078
 	q->crt->unix_sock=unix_sock;
1079
+	q->crt->expire=get_ticks_raw()+SEND_FD_QUEUE_TIMEOUT;
1042 1080
 	q->crt->retries=0;
1043 1081
 	q->crt++;
1044 1082
 	return 0;
... ...
@@ -1057,15 +1095,19 @@ inline static void send_fd_queue_run(struct tcp_send_fd_q* q)
1057 1095
 		if (send_fd(p->unix_sock, &(p->tcp_conn),
1058 1096
 					sizeof(struct tcp_connection*), p->tcp_conn->s)<=0){
1059 1097
 			if ( ((errno==EAGAIN)||(errno==EWOULDBLOCK)) && 
1060
-							(p->retries<MAX_SEND_FD_RETRIES)){
1098
+							((s_ticks_t)(p->expire-get_ticks_raw())>0)){
1061 1099
 				/* leave in queue for a future try */
1062 1100
 				*t=*p;
1063 1101
 				t->retries++;
1064 1102
 				t++;
1065 1103
 			}else{
1066 1104
 				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);
1105
+						   " on socket %d , queue entry %ld, retries %d,"
1106
+						   " connection %p, tcp socket %d, errno=%d (%s) \n",
1107
+						   p->unix_sock, p-&q->data[0], p->retries,
1108
+						   p->tcp_conn, p->tcp_conn->s, errno,
1109
+						   strerror(errno));
1110
+				tcpconn_destroy(p->tcp_conn);
1069 1111
 			}
1070 1112
 		}
1071 1113
 	}
... ...
@@ -1077,38 +1119,6 @@ inline static void send_fd_queue_run(struct tcp_send_fd_q* q)
1077 1119
 
1078 1120
 
1079 1121
 
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 1122
 /* handles io from a tcp child process
1113 1123
  * params: tcp_c - pointer in the tcp_children array, to the entry for
1114 1124
  *                 which an io event was detected 
... ...
@@ -1348,10 +1358,14 @@ inline static int send2child(struct tcp_connection* tcpconn)
1348 1358
 	int i;
1349 1359
 	int min_busy;
1350 1360
 	int idx;
1361
+	static int crt=0; /* current child */
1362
+	int last;
1351 1363
 	
1352 1364
 	min_busy=tcp_children[0].busy;
1353 1365
 	idx=0;
1354
-	for (i=0; i<tcp_children_no; i++){
1366
+	last=crt+tcp_children_no;
1367
+	for (; crt<last; crt++){
1368
+		i=crt%tcp_children_no;
1355 1369
 		if (!tcp_children[i].busy){
1356 1370
 			idx=i;
1357 1371
 			min_busy=0;
... ...
@@ -1361,6 +1375,7 @@ inline static int send2child(struct tcp_connection* tcpconn)
1361 1375
 			idx=i;
1362 1376
 		}
1363 1377
 	}
1378
+	crt=idx+1; /* next time we start with crt%tcp_children_no */
1364 1379
 	
1365 1380
 	tcp_children[idx].busy++;
1366 1381
 	tcp_children[idx].n_reqs++;
... ...
@@ -1387,8 +1402,10 @@ inline static int send2child(struct tcp_connection* tcpconn)
1387 1402
 			tcpconn->s)<=0){
1388 1403
 		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
1389 1404
 			/* FIXME: remove after debugging */
1390
-			LOG(L_WARN, "WARNING: tcp child %d, socket %d: queue full\n",
1391
-					idx, tcp_children[idx].unix_sock);
1405
+			 LOG(L_CRIT, "INFO: tcp child %d, socket %d: queue full,"
1406
+					 	" %d requests queued (total handled %d)\n",
1407
+					idx, tcp_children[idx].unix_sock, min_busy,
1408
+					tcp_children[idx].n_reqs-1);
1392 1409
 			if (send_fd_queue_add(&send2child_q, tcp_children[idx].unix_sock, 
1393 1410
 						tcpconn)!=0){
1394 1411
 				LOG(L_ERR, "ERROR: send2child: queue send op. failed\n");
... ...
@@ -1396,6 +1413,7 @@ inline static int send2child(struct tcp_connection* tcpconn)
1396 1413
 			}
1397 1414
 		}else{
1398 1415
 			LOG(L_ERR, "ERROR: send2child: send_fd failed\n");
1416
+			return -1;
1399 1417
 		}
1400 1418
 	}
1401 1419
 #else
... ...
@@ -1450,6 +1468,11 @@ static inline int handle_new_connect(struct socket_info* si)
1450 1468
 	/* add socket to list */
1451 1469
 	tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
1452 1470
 	if (tcpconn){
1471
+#ifdef TCP_PASS_NEW_CONNECTION_ON_DATA
1472
+		io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn);
1473
+		tcpconn->flags&=~F_CONN_REMOVED;
1474
+		tcpconn_add(tcpconn);
1475
+#else
1453 1476
 		tcpconn->refcnt++; /* safe, not yet available to the
1454 1477
 							  outside world */
1455 1478
 		tcpconn_add(tcpconn);
... ...
@@ -1459,14 +1482,9 @@ static inline int handle_new_connect(struct socket_info* si)
1459 1482
 		if(send2child(tcpconn)<0){
1460 1483
 			LOG(L_ERR,"ERROR: handle_new_connect: no children "
1461 1484
 					"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;
1485
+			tcpconn_destroy(tcpconn);
1469 1486
 		}
1487
+#endif
1470 1488
 	}else{ /*tcpconn==0 */
1471 1489
 		LOG(L_ERR, "ERROR: handle_new_connect: tcpconn_new failed, "
1472 1490
 				"closing socket\n");
... ...
@@ -1490,8 +1508,6 @@ static inline int handle_new_connect(struct socket_info* si)
1490 1508
  */
1491 1509
 inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i)
1492 1510
 {
1493
-	int fd;
1494
-	
1495 1511
 	/*  is refcnt!=0 really necessary? 
1496 1512
 	 *  No, in fact it's a bug: I can have the following situation: a send only
1497 1513
 	 *   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 1532
 	tcpconn_ref(tcpconn); /* refcnt ++ */
1517 1533
 	if (send2child(tcpconn)<0){
1518 1534
 		LOG(L_ERR,"ERROR: handle_tcpconn_ev: no children available\n");
1535
+		tcpconn_destroy(tcpconn);
1536
+#if 0
1519 1537
 		TCPCONN_LOCK;
1520 1538
 		tcpconn->refcnt--;
1521 1539
 		if (tcpconn->refcnt==0){
... ...
@@ -1524,6 +1542,7 @@ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i)
1524 1542
 			close(fd);
1525 1543
 		}else tcpconn->timeout=0; /* force expire*/
1526 1544
 		TCPCONN_UNLOCK;
1545
+#endif
1527 1546
 	}
1528 1547
 	return 0; /* we are not interested in possibly queued io events, 
1529 1548
 				 the fd was either passed to a child, or closed */
... ...
@@ -1631,6 +1650,14 @@ void tcp_main_loop()
1631 1650
 	struct socket_info* si;
1632 1651
 	int r;
1633 1652
 	
1653
+	/* init send fd queues (here because we want mem. alloc only in the tcp
1654
+	 *  process */
1655
+#ifdef SEND_FD_QUEUE
1656
+	if (init_send_fd_queues()<0){
1657
+		LOG(L_CRIT, "ERROR: init_tcp: could not init send fd queues\n");
1658
+		goto error;
1659
+	}
1660
+#endif
1634 1661
 	/* init io_wait (here because we want the memory allocated only in
1635 1662
 	 * the tcp_main process) */
1636 1663
 	
... ...
@@ -1758,6 +1785,9 @@ void tcp_main_loop()
1758 1785
 			goto error;
1759 1786
 	}
1760 1787
 error:
1788
+#ifdef SEND_FD_QUEUE
1789
+	destroy_send_fd_queues();
1790
+#endif
1761 1791
 	destroy_io_wait(&io_h);
1762 1792
 	LOG(L_CRIT, "ERROR: tcp_main_loop: exiting...");
1763 1793
 	exit(-1);
... ...
@@ -1786,9 +1816,10 @@ void destroy_tcp()
1786 1816
 			lock_dealloc((void*)tcpconn_lock);
1787 1817
 			tcpconn_lock=0;
1788 1818
 		}
1789
-#ifdef SEND_FD_QUEUE
1790
-		destroy_send_fd_queues();
1791
-#endif
1819
+		if (tcp_children){
1820
+			pkg_free(tcp_children);
1821
+			tcp_children=0;
1822
+		}
1792 1823
 }
1793 1824
 
1794 1825
 
... ...
@@ -1834,14 +1865,7 @@ int init_tcp()
1834 1865
 			TCP_ALIAS_HASH_SIZE * sizeof(struct tcp_conn_alias*));
1835 1866
 	memset((void*)tcpconn_id_hash, 0, 
1836 1867
 			TCP_ID_HASH_SIZE * sizeof(struct tcp_connection*));
1837
-	/* init send fd queues */
1838 1868
 	
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 1869
 	/* fix config variables */
1846 1870
 	/* they can have only positive values due the config parser so we can
1847 1871
 	 * ignore most of them */
... ...
@@ -1916,6 +1940,12 @@ int tcp_init_children()
1916 1940
 	tcp_max_fd_no=process_count*2 +r-1 /* timer */ +3; /* stdin/out/err*/
1917 1941
 	tcp_max_fd_no+=tcp_max_connections;
1918 1942
 	
1943
+	/* alloc the children array */
1944
+	tcp_children=pkg_malloc(sizeof(struct tcp_child)*tcp_children_no);
1945
+	if (tcp_children==0){
1946
+			LOG(L_ERR, "ERROR: tcp_init_children: out of memory\n");
1947
+			goto error;
1948
+	}
1919 1949
 	/* create the tcp sock_info structures */
1920 1950
 	/* copy the sockets --moved to main_loop*/
1921 1951
 	
... ...
@@ -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 67
 #define HANDLE_IO_INLINE
67 68
 #include "io_wait.h"
68 69
 #include <fcntl.h> /* must be included after io_wait.h if SIGIO_RT is used */
70
+#include "tsend.h"
69 71
 
70 72
 /* types used in io_wait* */
71 73
 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 556
 		/* errno==EINTR, EWOULDBLOCK a.s.o todo */
555 557
 		response[0]=(long)c;
556 558
 		response[1]=state;
557
-		if (send_all(unix_sock, response, sizeof(response))<=0)
558
-			LOG(L_ERR, "ERROR: release_tcpconn: send_all failed\n");
559
+		
560
+		if (tsend_stream(unix_sock, (char*)response, sizeof(response), -1)<=0)
561
+			LOG(L_ERR, "ERROR: release_tcpconn: tsend_stream failed\n");
559 562
 }
560 563
 
561 564
 
... ...
@@ -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 59
 #define TSEND_POLL(f_name) \
59 60
 poll_loop: \
60 61
 	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; \
62
+		if (timeout==-1) \
63
+			n=poll(&pf, 1, -1); \
64
+		else{ \
65
+			diff=expire-get_ticks_raw(); \
66
+			if (diff<=0){ \
67
+				LOG(L_ERR, "ERROR: " f_name ": send timeout (%d)\n", timeout);\
68
+				goto error; \
69
+			} \
70
+			n=poll(&pf, 1, TICKS_TO_MS((ticks_t)diff)); \
65 71
 		} \
66
-		n=poll(&pf, 1, TICKS_TO_MS((ticks_t)diff)); \
67 72
 		if (n<0){ \
68 73
 			if (errno==EINTR) continue; /* signal, ignore */ \
69 74
 			LOG(L_ERR, "ERROR: " f_name ": poll failed: %s [%d]\n", \
... ...
@@ -100,8 +105,10 @@ poll_loop: \
100 105
 	
101 106
 
102 107
 
103
-/* sends on fd (which must be O_NONBLOCK); if it cannot send any data
108
+/* sends on fd (which must be O_NONBLOCK if you want a finite timeout); if it
109
+ * cannot send any data
104 110
  * in timeout milliseconds it will return ERROR
111
+ * if timeout==-1, it waits forever
105 112
  * returns: -1 on error, or number of bytes written
106 113
  *  (if less than len => couldn't send all)
107 114
  *  bugs: signals will reset the timer