Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a05b64d4 authored by Cody Schuffelen's avatar Cody Schuffelen
Browse files

Add native vsock support to ADB.

vsock is a socket address family for communicating into and out of
virtual machines. Addresses have a port and CID. The CID is unique to
each virtual machine on the computer. The VM host always has CID 2.
http://man7.org/linux/man-pages/man7/vsock.7.html

Inside the android guest, the adb daemon hosts a vsock server with
VMADDR_CID_ANY, automatically using the guest CID. The adb server
can now connect to addresses of the form vsock:cid:port, where the CID
must be specified and the port defaults to 5555.

This is a significant speed improvement for ADB connections in
Cuttlefish, with 150-200 MB/s for `adb push` and 100-150 MB/s for
`adb pull`. It also allows removing some proxying steps from Cuttlefish,
simplifying the full connection path, and removes a dependency on the
unstable ivshmem protocol.

Commands tested against a Cuttlefish VM with CID 3:
adb connect vsock:3:5555
adb -s vsock:3:5555 shell
adb disconnect vsock:3:5555

Supporting "adb disconnect" and "adb -s" required modifying some of the
parts that parse addresses / serials.

push/pull trials with native adb vsock support in cuttlefish:

100m: 1 file pushed. 297.6 MB/s (104857600 bytes in 0.336s)
100m: 1 file pushed. 270.3 MB/s (104857600 bytes in 0.370s)
100m: 1 file pushed. 271.7 MB/s (104857600 bytes in 0.368s)
100m: 1 file pushed. 250.5 MB/s (104857600 bytes in 0.399s)
100m: 1 file pushed. 277.1 MB/s (104857600 bytes in 0.361s)
100m: 1 file pushed. 263.5 MB/s (104857600 bytes in 0.379s)
100m: 1 file pushed. 242.6 MB/s (104857600 bytes in 0.412s)
100m: 1 file pushed. 271.8 MB/s (104857600 bytes in 0.368s)
100m: 1 file pushed. 267.1 MB/s (104857600 bytes in 0.374s)

/data/local/tmp/100m: 1 file pulled. 212.8 MB/s (104857600 bytes in 0.470s)
/data/local/tmp/100m: 1 file pulled. 236.7 MB/s (104857600 bytes in 0.423s)
/data/local/tmp/100m: 1 file pulled. 201.2 MB/s (104857600 bytes in 0.497s)
/data/local/tmp/100m: 1 file pulled. 255.6 MB/s (104857600 bytes in 0.391s)
/data/local/tmp/100m: 1 file pulled. 199.6 MB/s (104857600 bytes in 0.501s)
/data/local/tmp/100m: 1 file pulled. 214.6 MB/s (104857600 bytes in 0.466s)
/data/local/tmp/100m: 1 file pulled. 254.2 MB/s (104857600 bytes in 0.393s)
/data/local/tmp/100m: 1 file pulled. 212.5 MB/s (104857600 bytes in 0.471s)
/data/local/tmp/100m: 1 file pulled. 218.9 MB/s (104857600 bytes in 0.457s)
/data/local/tmp/100m: 1 file pulled. 223.6 MB/s (104857600 bytes in 0.447s)

Bug: 121166534
Change-Id: I50f21fb5c9acafb8daa789df4e28c9e1bbbbf2ef
Test: adb connect/shell/disconnect
parent e41489a9
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -1134,7 +1134,9 @@ bool handle_host_request(const char* service, TransportType type, const char* se
        std::string host;
        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
        std::string error;
        if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
        if (address.starts_with("vsock:")) {
            serial = address;
        } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
            SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                                                           address.c_str(), error.c_str()));
            return true;
+1 −1
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ void qemu_socket_thread(int port) {
        /* This could be an older version of the emulator, that doesn't
         * implement adb QEMUD service. Fall back to the old TCP way. */
        D("adb service is not available. Falling back to TCP socket.");
        std::thread(server_socket_thread, port).detach();
        std::thread(server_socket_thread, android::base::StringPrintf("tcp:%d", port)).detach();
        return;
    }

+116 −3
Original line number Diff line number Diff line
@@ -46,6 +46,11 @@ using android::base::StringPrintf;
#define ADB_WINDOWS 0
#endif

#if ADB_LINUX
#include <sys/socket.h>
#include "sysdeps/vm_sockets.h"
#endif

// Not static because it is used in commandline.c.
int gListenAll = 0;

@@ -174,6 +179,62 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std
            return true;
        }
        return false;
    } else if (address.starts_with("vsock:")) {
#if ADB_LINUX
        std::string spec_str(address);
        std::vector<std::string> fragments = android::base::Split(spec_str, ":");
        unsigned int port_value = port ? *port : 0;
        if (fragments.size() != 2 && fragments.size() != 3) {
            *error = android::base::StringPrintf("expected vsock:cid or vsock:port:cid in '%s'",
                                                 spec_str.c_str());
            errno = EINVAL;
            return false;
        }
        unsigned int cid = 0;
        if (!android::base::ParseUint(fragments[1], &cid)) {
            *error = android::base::StringPrintf("could not parse vsock cid in '%s'",
                                                 spec_str.c_str());
            errno = EINVAL;
            return false;
        }
        if (fragments.size() == 3 && !android::base::ParseUint(fragments[2], &port_value)) {
            *error = android::base::StringPrintf("could not parse vsock port in '%s'",
                                                 spec_str.c_str());
            errno = EINVAL;
            return false;
        }
        if (port_value == 0) {
            *error = android::base::StringPrintf("vsock port was not provided.");
            errno = EINVAL;
            return false;
        }
        fd->reset(socket(AF_VSOCK, SOCK_STREAM, 0));
        if (fd->get() == -1) {
            *error = "could not open vsock socket";
            return false;
        }
        sockaddr_vm addr{};
        addr.svm_family = AF_VSOCK;
        addr.svm_port = port_value;
        addr.svm_cid = cid;
        if (serial) {
            *serial = android::base::StringPrintf("vsock:%u:%d", cid, port_value);
        }
        if (connect(fd->get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
            int error_num = errno;
            *error = android::base::StringPrintf("could not connect to vsock address '%s'",
                                                 spec_str.c_str());
            errno = error_num;
            return false;
        }
        if (port) {
            *port = port_value;
        }
        return true;
#else   // ADB_LINUX
        *error = "vsock is only supported on linux";
        return false;
#endif  // ADB_LINUX
    }

    for (const auto& it : kLocalSocketTypes) {
@@ -187,6 +248,9 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std

            fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
                                           SOCK_STREAM, error));
            if (serial) {
                *serial = address;
            }
            return true;
        }
    }
@@ -196,7 +260,7 @@ bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std
    return false;
}

int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_port) {
    if (spec.starts_with("tcp:")) {
        std::string hostname;
        int port;
@@ -215,10 +279,59 @@ int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_
            return -1;
        }

        if (result >= 0 && port == 0 && resolved_tcp_port) {
            *resolved_tcp_port = adb_socket_get_local_port(result);
        if (result >= 0 && resolved_port) {
            *resolved_port = adb_socket_get_local_port(result);
        }
        return result;
    } else if (spec.starts_with("vsock:")) {
#if ADB_LINUX
        std::string spec_str(spec);
        std::vector<std::string> fragments = android::base::Split(spec_str, ":");
        if (fragments.size() != 2) {
            *error = "given vsock server socket string was invalid";
            return -1;
        }
        int port;
        if (!android::base::ParseInt(fragments[1], &port)) {
            *error = "could not parse vsock port";
            errno = EINVAL;
            return -1;
        } else if (port < 0) {
            *error = "vsock port was negative.";
            errno = EINVAL;
            return -1;
        }
        unique_fd serverfd(socket(AF_VSOCK, SOCK_STREAM, 0));
        if (serverfd == -1) {
            int error_num = errno;
            *error = android::base::StringPrintf("could not create vsock server: '%s'",
                                                 strerror(error_num));
            errno = error_num;
            return -1;
        }
        sockaddr_vm addr{};
        addr.svm_family = AF_VSOCK;
        addr.svm_port = port == 0 ? VMADDR_PORT_ANY : port;
        addr.svm_cid = VMADDR_CID_ANY;
        socklen_t addr_len = sizeof(addr);
        if (bind(serverfd, reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
            return -1;
        }
        if (listen(serverfd, 4)) {
            return -1;
        }
        if (serverfd >= 0 && resolved_port) {
            if (getsockname(serverfd, reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
                *resolved_port = addr.svm_port;
            } else {
                return -1;
            }
        }
        return serverfd.release();
#else   // ADB_LINUX
        *error = "vsock is only supported on linux";
        return -1;
#endif  // ADB_LINUX
    }

    for (const auto& it : kLocalSocketTypes) {
+8 −0
Original line number Diff line number Diff line
@@ -608,6 +608,14 @@ bool parse_host_service(std::string_view* out_serial, std::string_view* out_comm
            return false;
        }
    }
    if (command.starts_with("vsock:")) {
        // vsock serials are vsock:cid:port, which have an extra colon compared to tcp.
        size_t next_colon = command.find(':');
        if (next_colon == std::string::npos) {
            return false;
        }
        consume(next_colon + 1);
    }

    bool found_address = false;
    if (command[0] == '[') {
+49 −0
Original line number Diff line number Diff line
#if __BIONIC__
#include <linux/vm_sockets.h>
#else
/****************************************************************************
 ****************************************************************************
 ***
 ***   This header was automatically generated from a Linux kernel header
 ***   of the same name, to make information necessary for userspace to
 ***   call into the kernel available to libc.  It contains only constants,
 ***   structures, and macros generated from the original header, and thus,
 ***   contains no copyrightable information.
 ***
 ***   Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
 ***
 ****************************************************************************
 ****************************************************************************/
#ifndef _UAPI_VM_SOCKETS_H
#define _UAPI_VM_SOCKETS_H
#include <linux/socket.h>
#define SO_VM_SOCKETS_BUFFER_SIZE 0
#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
#define SO_VM_SOCKETS_TRUSTED 5
#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
#define VMADDR_CID_ANY -1U
#define VMADDR_PORT_ANY -1U
#define VMADDR_CID_HYPERVISOR 0
#define VMADDR_CID_RESERVED 1
#define VMADDR_CID_HOST 2
#define VM_SOCKETS_INVALID_VERSION -1U
#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
struct sockaddr_vm {
    __kernel_sa_family_t svm_family;
    unsigned short svm_reserved1;
    unsigned int svm_port;
    unsigned int svm_cid;
    unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
                           sizeof(unsigned int) - sizeof(unsigned int)];
};
#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
#endif
#endif
Loading