1 /*
2 Copyright 2013-2017 Skytechnology sp. z o.o.
3
4 This file is part of LizardFS.
5
6 LizardFS is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3.
9
10 LizardFS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/platform.h"
20 #include "common/chunk_connector.h"
21
22 #include <errno.h>
23 #include <algorithm>
24
25 #include "common/exceptions.h"
26 #include "common/mfserr.h"
27 #include "common/sockets.h"
28 #include "common/time_utils.h"
29
timeoutTime(int64_t rtt,uint8_t tryCounter)30 static int64_t timeoutTime(int64_t rtt, uint8_t tryCounter) {
31 return rtt * (1 << (tryCounter / 2)) * 3 / (tryCounter % 2 == 0 ? 3 : 2);
32 }
33
ChunkConnector(uint32_t sourceIp)34 ChunkConnector::ChunkConnector(uint32_t sourceIp) : roundTripTime_ms_(20), sourceIp_(sourceIp) {
35 }
36
startUsingConnection(const NetworkAddress & server,const Timeout & timeout) const37 int ChunkConnector::startUsingConnection(const NetworkAddress& server,
38 const Timeout& timeout) const {
39 int fd = -1;
40 int retries = 0;
41 int err = ETIMEDOUT; // we want to return ETIMEDOUT on timeout.expired()
42 while (!timeout.expired()) {
43 fd = tcpsocket();
44 if (fd < 0) {
45 err = tcpgetlasterror();
46 lzfs_pretty_syslog(LOG_WARNING, "can't create tcp socket: %s", strerr(err));
47 break;
48 }
49 if (sourceIp_) {
50 if (tcpnumbind(fd, sourceIp_, 0) < 0) {
51 err = tcpgetlasterror();
52 lzfs_pretty_syslog(LOG_WARNING, "can't bind to given ip: %s", strerr(err));
53 tcpclose(fd);
54 fd = -1;
55 break;
56 }
57 }
58 int64_t connectTimeout = std::min(
59 timeoutTime(roundTripTime_ms_, retries),
60 timeout.remaining_ms());
61 connectTimeout = std::max(int64_t(1), connectTimeout); // tcpnumtoconnect doesn't like 0
62 if (tcpnumtoconnect(fd, server.ip, server.port, connectTimeout) < 0) {
63 err = tcpgetlasterror();
64 tcpclose(fd);
65 fd = -1;
66 } else {
67 break;
68 }
69 retries++;
70 }
71 if (fd < 0) {
72 throw ChunkserverConnectionException(
73 "Connection error: " + std::string(strerr(err)), server);
74 }
75 if (tcpnodelay(fd) < 0) {
76 lzfs_pretty_syslog(LOG_WARNING, "can't set TCP_NODELAY: %s", strerr(tcpgetlasterror()));
77 }
78 return fd;
79 }
80
endUsingConnection(int fd,const NetworkAddress &) const81 void ChunkConnector::endUsingConnection(int fd, const NetworkAddress& /* server */) const {
82 tcpclose(fd);
83 }
84
ChunkConnectorUsingPool(ConnectionPool & connectionPool,uint32_t sourceIp)85 ChunkConnectorUsingPool::ChunkConnectorUsingPool(ConnectionPool& connectionPool, uint32_t sourceIp)
86 : ChunkConnector(sourceIp),
87 connectionPool_(connectionPool) {
88 }
89
startUsingConnection(const NetworkAddress & server,const Timeout & timeout) const90 int ChunkConnectorUsingPool::startUsingConnection(const NetworkAddress& server,
91 const Timeout& timeout) const {
92 int fd = connectionPool_.getConnection(server);
93 if (fd >= 0) {
94 return fd;
95 } else {
96 return ChunkConnector::startUsingConnection(server, timeout);
97 }
98 }
99
endUsingConnection(int fd,const NetworkAddress & server) const100 void ChunkConnectorUsingPool::endUsingConnection(int fd, const NetworkAddress& server) const {
101 connectionPool_.putConnection(fd, server, kConnectionPoolTimeout_s);
102 }
103