net: fix failure of launching multiple VMs consecutively
authorMunkyu Im <munkyu.im@samsung.com>
Tue, 24 Nov 2015 07:41:00 +0000 (16:41 +0900)
committerMunkyu Im <munkyu.im@samsung.com>
Wed, 30 Dec 2015 04:50:01 +0000 (13:50 +0900)
There was a gap between check_port_bind_listen()
and ecs_socket_listen()
The socket fd for checking port was closed and
created it again as ECS(Emulator Control Sever) socket.
If user launches both VM1 and VM2 at the same time,
the below case can be happen.

1. VM1: launch and search base port - 26100
2. VM1: close socket fd
3. VM2: launch and search base port - 26100
4. VM2: close socket fd
5. VM1: try to create ecs socket for 26100 + 3
6. VM2: try to create ecs socket for 26100 + 3 (failure)

To fix this problem, ECS re-uses the port created on check_port_bind_listen().

Change-Id: Ib60a4f15d63bba57c68583228753310d5b467677
Signed-off-by: Munkyu Im <munkyu.im@samsung.com>
(cherry picked from commit 6ae322880b2a6f4de42bd25c9f19ccce870136cd)
(cherry picked from commit cd38ec42de2fc356124b2fdc398f2b6339d66176)

tizen/src/ecs/ecs.c
tizen/src/emulator.c
tizen/src/util/sdb.c
tizen/src/util/sdb.h

index 5fc6f77d4fbd7e2af5a4f973d1e6ee918c022d67..05e27661d8783083cd2af2f4b07a7281ec6587d7 100644 (file)
@@ -633,68 +633,16 @@ static int ecs_loop(ECS_State *cs)
 
 #endif
 
-static int ecs_socket_listen(int port)
-{
-    struct sockaddr_in addr;
-    int slisten = -1;
-    int ret = -1;
-
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-
-    ret = inet_aton(HOST_LISTEN_ADDR, &addr.sin_addr);
-    if (ret == 0) {
-        LOG_SEVERE("inet_aton failure\n");
-        return -1;
-    }
-
-    slisten = qemu_socket(PF_INET, SOCK_STREAM, 0);
-    if (slisten < 0) {
-        LOG_SEVERE("socket creation failed.\n");
-        return slisten;
-    }
+static int socket_initialize(ECS_State *cs) {
+    LOG_INFO("Listen fd is %d\n", emul_vm_base_socket);
 
-    ret = socket_set_fast_reuse(slisten);
-    if (ret != 0) {
-        LOG_SEVERE("It cannot be reach here.\n");
-        closesocket(slisten);
-        return -1;
-    }
-
-    if (bind(slisten, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-        LOG_SEVERE("bind failed : %d\n", errno);
-        closesocket(slisten);
-        return -1;
-    }
-
-    if (listen(slisten,1) != 0) {
-        LOG_SEVERE("listen failed : %d\n", errno);
-        closesocket(slisten);
-        return -1;
-    }
-
-    return slisten;
-}
-
-static int socket_initialize(ECS_State *cs, int port) {
-    int fd = -1;
-
-    fd = ecs_socket_listen(port);
-    if (fd < 0) {
-        return -1;
-    }
-
-    LOG_INFO("Listen fd is %d\n", fd);
-
-    qemu_set_nonblock(fd);
-
-    cs->listen_fd = fd;
+    cs->listen_fd = emul_vm_base_socket;
 
 #ifdef CONFIG_LINUX
     epoll_init(cs);
 #else
     FD_ZERO(&cs->reads);
-    FD_SET(fd, &cs->reads);
+    FD_SET(cs->listen_fd, &cs->reads);
 #endif
 
     make_keep_alive_msg();
@@ -711,7 +659,6 @@ static void* ecs_initialize(void* args) {
     int ret = 1;
     ECS_State *cs = NULL;
     Monitor* mon = NULL;
-    int port = 0;
 
     LOG_INFO("ecs starts initializing.\n");
 
@@ -725,10 +672,7 @@ static void* ecs_initialize(void* args) {
     }
 
     cs->listen_fd = -1;
-
-    port = get_emul_ecs_port();
-
-    ret = socket_initialize(cs, port);
+    ret = socket_initialize(cs);
     if (ret < 0) {
         LOG_SEVERE("Socket initialization is failed.\n");
         ecs_close(cs);
index 6d29799f9583a5649f78eb8f83ff82e196bf2d3d..6b2383aab32270f688b6ed708b506b6b7dd5ac92 100644 (file)
@@ -268,7 +268,7 @@ void prepare_maru_after_device_init(void)
     make_vm_lock_os();
     init_device_hotplug();
     start_ecs();
-    init_sdb(get_emul_vm_base_port() + SDB_UDP_SENSOR_INDEX);
+    init_sdb(get_emul_vm_base_port());
 
     // Only for intent logging of essential information
     get_vm_name();
index 017587cee3eeb32da94b7c5f041df254c0957091..018a24d4c082eeba4a0351cab05e562ebbc21b31 100644 (file)
@@ -49,8 +49,16 @@ MULTI_DEBUG_CHANNEL(qemu, sdb);
 #include "skin/maruskin_server.h"
 #endif
 
-static bool sdb_daemon_is_initialized = false;
+#define SDB_INDEX 1
+#define GDB_INDEX 2
+#define ECS_INDEX 3
+#define SDB_NOTI_PORT_INDEX 3
+#define START_BASE_PORT 26100
+#define SDB_GUEST_PORT  (START_BASE_PORT + SDB_INDEX)
+#define GDB_GUEST_PORT  (START_BASE_PORT + GDB_INDEX)
 
+int emul_vm_base_socket;
+static bool sdb_daemon_is_initialized = false;
 #ifdef _WIN32
 #include "qemu/main-loop.h"
 
@@ -120,60 +128,62 @@ int inet_strtoip(const char*  str, uint32_t  *ip)
 int check_port_bind_listen(uint32_t port)
 {
     struct sockaddr_in addr;
-    int s = 0;
     int ret = -1;
-    socklen_t addrlen = sizeof(addr);
-    memset(&addr, 0, addrlen);
-
     addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     addr.sin_port = htons(port);
 
-    s = qemu_socket(AF_INET, SOCK_STREAM, 0);
-    if (s < 0) {
-        INFO("failed to create a socket\n");
+    if (inet_aton(HOST_LISTEN_ADDR, &addr.sin_addr) == 0) {
+        LOG_SEVERE("inet_aton failure\n");
         return -1;
     }
 
-#ifndef _WIN32
-    int opt = 1;
-    ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
-                    (char *)&opt, sizeof(int));
+    ret = qemu_socket(PF_INET, SOCK_STREAM, 0);
     if (ret < 0) {
-        INFO("setsockopt failure\n");
-        close(s);
+        LOG_SEVERE("socket creation failed.\n");
         return -1;
     }
-#endif
 
-    if ((bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) ||
-        (listen(s, 1) < 0)) {
-        /* failure */
-        ret = -1;
-        INFO("port(%d) listen failure\n", port);
-    } else {
-        /* success */
-        ret = 1;
-        INFO("port(%d) listen success\n", port);
+    qemu_set_nonblock(ret);
+
+    if (socket_set_fast_reuse(ret) != 0) {
+        LOG_SEVERE("It cannot be reach here.\n");
+        closesocket(ret);
+        return -1;
     }
 
-#ifdef _WIN32
-    closesocket(s);
-#else
-    close(s);
-#endif
+    if (bind(ret, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+        LOG_INFO("bind failed for port: %d, errno: %d\n", port, errno);
+        closesocket(ret);
+        return -1;
+    }
+
+    if (listen(ret, 1) != 0) {
+        LOG_SEVERE("listen failed :%s-%d\n", strerror(errno), errno);
+        closesocket(ret);
+        return -1;
+    }
 
     return ret;
 }
 
-#define ECS_INDEX 3
-#define SDB_GUEST_PORT 26101
-#define GDB_GUEST_PORT 26102
+int find_available_port(uint32_t base_port)
+{
+    int ret = 0;
+    /* TODO: additional check for sdb and gdb port using for redirection */
+    ret = check_port_bind_listen(base_port + ECS_INDEX);
+    if (ret > 0) {
+        emul_vm_base_socket = ret;
+    } else {
+        LOG_INFO("socket close. port:%d\n", base_port + ECS_INDEX);
+    }
+    return ret;
+}
+
 void init_vm_base_port(void)
 {
     int   tries     = 10;
     int   success   = 0;
-    uint32_t port = 26100;
+    uint32_t port = START_BASE_PORT;
     int base_port;
 
     base_port = get_emul_vm_base_port();
@@ -181,8 +191,10 @@ void init_vm_base_port(void)
     if (base_port == 0) {
 
         for ( ; tries > 0; tries--, port += 10 ) {
-            if (check_port_bind_listen(port + ECS_INDEX) < 0)
+            if (find_available_port(port) < 0) {
+                LOG_INFO("port(%d) is not available. It can be reserved.\n", port);
                 continue;
+            }
 
             success = 1;
             break;
@@ -203,39 +215,29 @@ void init_vm_base_port(void)
 static void start_sdb_noti_server(int server_port);
 void init_sdb(int server_port)
 {
-    start_sdb_noti_server(server_port);
+    start_sdb_noti_server(server_port + SDB_NOTI_PORT_INDEX);
 
+    /* no need to redir if network is tap */
     if (is_netclient_tap_attached()) {
         return;
     }
 
-    int   tries     = 10;
-    int   success   = 0;
     char buf_sdb[64] = {0,};
     char buf_gdb[64] = {0,};
-    int number;
-
-    number = get_device_serial_number();
 
-    for ( ; tries > 0; tries--, number += 10 ) {
-        sprintf(buf_sdb, "tcp:%d::%d", number, SDB_GUEST_PORT);
-        sprintf(buf_gdb, "tcp:%d::%d", number + 1, GDB_GUEST_PORT);
-        if(net_slirp_redir((char*)buf_sdb) < 0 || net_slirp_redir((char*)buf_gdb) < 0)
-            continue;
-
-        INFO( "SDBD established on port %d\n", number);
-        success = 1;
-        break;
+    sprintf(buf_sdb, "tcp:%d::%d", server_port + SDB_INDEX, SDB_GUEST_PORT);
+    if (net_slirp_redir((char*)buf_sdb) < 0) {
+        LOG_WARNING("failed to redirect sdb port [%s]\n", buf_sdb);
+    } else {
+        INFO("redirect port for sdb [%s]\n", buf_sdb);
     }
 
-    INFO("redirect [%s] success\n", buf_sdb);
-    INFO("redirect [%s] success\n", buf_gdb);
-    if (!success) {
-        INFO( "it seems too many emulator instances are running on this machine. Aborting\n" );
-        exit(1);
+    sprintf(buf_gdb, "tcp:%d::%d", server_port + GDB_INDEX, GDB_GUEST_PORT);
+    if (net_slirp_redir((char*)buf_gdb) < 0) {
+        LOG_WARNING("failed to redirect gdb port [%s]\n", buf_gdb);
+    } else {
+        INFO("redirect port for gdb [%s]\n", buf_gdb);
     }
-
-    INFO( "Port(%d/tcp) listen for SDB\n", number);
 }
 
 /*
index 97ae0912f3f3b52228035bceea74862434c016f0..2dd0cc0c07da39063cc5d3c12302f7436b035b15 100644 (file)
@@ -58,8 +58,10 @@ int inet_strtoip(const char*  str, uint32_t  *ip);
 int socket_send(int fd, const void*  buf, int  buflen);
 void socket_close(int fd);
 int check_port_bind_listen(uint32_t port);
+int find_available_port(uint32_t base_port);
 bool is_sdb_daemon_initialized(void);
 
+extern int emul_vm_base_socket;
 #define STATE_RUNNING 0
 #define STATE_SUSPEND 1
 void notify_all_sdb_clients(int state);