Browse code

Improved multihomed performance

The mhomed implementation works by calling a socket()/connect()/getsockname()/close()
to find out the interface in a multihomed system.
Because mhomed works only for UDP sockets, a major performance improvement is shown
if we use the same socket for multiple connect()s, thus completly removing the socket() call
and the close() call.

The CPU load(user+system) shown in a mhomed environment on a stateless router, with a call rate of 6000 calls/s
is 46.1% load in the case of this patched version, versus 63.54% load in the case of the original
version.

Marius Zbihlei authored on 15/01/2010 16:56:29
Showing 1 changed files
... ...
@@ -110,10 +110,14 @@
110 110
 /* return a socket_info_pointer to the sending socket; as opposed to
111 111
  * get_send_socket, which returns process's default socket, get_out_socket
112 112
  * attempts to determine the outbound interface which will be used;
113
- * it creates a temporary connected socket to determine it; it will
113
+ * it uses a temporary connected socket to determine it; it will
114 114
  * be very likely noticeably slower, but it can deal better with
115 115
  * multihomed hosts
116 116
  */
117
+
118
+static int sock_inet = 0;
119
+static int sock_inet6 = 0;
120
+
117 121
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
118 122
 {
119 123
 	int temp_sock;
... ...
@@ -126,33 +130,57 @@ struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
126 126
 		LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
127 127
 		return 0;
128 128
 	}
129
-	
130
-	temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 );
131
-	if (temp_sock==-1) {
132
-		LOG(L_ERR, "ERROR: get_out_socket: socket() failed: %s\n",
133
-				strerror(errno));
129
+
130
+	switch(to->s.sa_family){
131
+	case AF_INET : {
132
+		if(sock_inet <= 0){
133
+			sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
134
+			if (sock_inet==-1) {
135
+				LM_ERR("socket() failed: %s\n", strerror(errno));
136
+				return 0;
137
+			}
138
+		}
139
+		temp_sock = sock_inet;
140
+		break;
141
+	}
142
+	case AF_INET6 : {
143
+		if(sock_inet6 <= 0){
144
+			sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
145
+			if (sock_inet6==-1) {
146
+				LM_ERR("socket() failed: %s\n", strerror(errno));
147
+				return 0;
148
+			}
149
+		}
150
+		temp_sock = sock_inet6;
151
+		break;
152
+ 	}
153
+	default: {
154
+		LM_ERR("Unknow protocol family \n");
134 155
 		return 0;
135 156
 	}
157
+	}
136 158
 	if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
137 159
 		LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
138 160
 				strerror(errno));
161
+		sock_inet = 0;
162
+		sock_inet6 = 0;
139 163
 		goto error;
140 164
 	}
141 165
 	len=sizeof(from);
142 166
 	if (getsockname(temp_sock, &from.s, &len)==-1) {
143 167
 		LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
144 168
 				strerror(errno));
169
+		sock_inet = 0;
170
+		sock_inet6 = 0;
145 171
 		goto error;
146 172
 	}
147 173
 	su2ip_addr(&ip, &from);
148 174
 	si=find_si(&ip, 0, proto);
149 175
 	if (si==0) goto error;
150
-	close(temp_sock);
151 176
 	DBG("DEBUG: get_out_socket: socket determined: %p\n", si );
152 177
 	return si;
153 178
 error:
154 179
 	LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
155
-	close(temp_sock);
156 180
 	return 0;
157 181
 }
158 182