diff options
author | Yifan Hong <elsk@google.com> | 2021-08-25 17:15:15 -0700 |
---|---|---|
committer | Yifan Hong <elsk@google.com> | 2021-08-25 17:41:40 -0700 |
commit | 95d15e56fc1e45b1aa11e9ad6ce01f77f0a28c36 (patch) | |
tree | a8718a0219512ea367012118daf12efc697a05c6 | |
parent | ab281ec6f1922fc5fd175223d739d01018131777 (diff) | |
download | native-95d15e56fc1e45b1aa11e9ad6ce01f77f0a28c36.tar.gz |
binder: retry connect() on ECONNRESET for non-blocking sockets.
For non-blocking sockets, if connect() returns EAGAIN / EINPROGRESS, the
code calls getsockopt() to get the real error if connect() were called
with a blocking socket fd. If ECONNRESET is seen, also retry.
Test: binderRpcTest
Fixes: 197162885
Change-Id: I7c7b8cb105d0d334b75b883ffcff3b0c62337cf4
-rw-r--r-- | libs/binder/RpcSession.cpp | 56 |
1 files changed, 29 insertions, 27 deletions
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 4c47005c7a..3e9e5a8441 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -484,37 +484,39 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, } if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { - if (errno == ECONNRESET) { + int connErrno = errno; + if (connErrno == EAGAIN || connErrno == EINPROGRESS) { + // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or + // EINPROGRESS (for others). Call poll() and getsockopt() to get the error. + status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT); + if (pollStatus != OK) { + ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s", + statusToString(pollStatus).c_str()); + return pollStatus; + } + // Set connErrno to the errno that connect() would have set if the fd were blocking. + socklen_t connErrnoLen = sizeof(connErrno); + int ret = + getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &connErrno, &connErrnoLen); + if (ret == -1) { + int savedErrno = errno; + ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s. " + "(Original error from connect() is: %s)", + strerror(savedErrno), strerror(connErrno)); + return -savedErrno; + } + // Retrieved the real connErrno as if connect() was called with a blocking socket + // fd. Continue checking connErrno. + } + if (connErrno == ECONNRESET) { ALOGW("Connection reset on %s", addr.toString().c_str()); continue; } - if (errno != EAGAIN && errno != EINPROGRESS) { - int savedErrno = errno; + // connErrno could be zero if getsockopt determines so. Hence zero-check again. + if (connErrno != 0) { ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), - strerror(savedErrno)); - return -savedErrno; - } - // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or - // EINPROGRESS (for others). Call poll() and getsockopt() to get the error. - status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT); - if (pollStatus != OK) { - ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s", - statusToString(pollStatus).c_str()); - return pollStatus; - } - int soError; - socklen_t soErrorLen = sizeof(soError); - int ret = getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &soError, &soErrorLen); - if (ret == -1) { - int savedErrno = errno; - ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s", - strerror(savedErrno)); - return -savedErrno; - } - if (soError != 0) { - ALOGE("After connect(), getsockopt() returns error for socket at %s: %s", - addr.toString().c_str(), strerror(soError)); - return -soError; + strerror(connErrno)); + return -connErrno; } } LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get()); |