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
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index cc67b6b..de4fff9 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -46,6 +46,11 @@
#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 @@
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 @@
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 @@
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 @@
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) {