nfs4: Try connecting to all getaddrinfo() results before giving up

This commit is contained in:
Pawel Dziepak
2013-02-05 03:23:56 +01:00
parent 15a18a6b89
commit a0d5a922d6
6 changed files with 140 additions and 39 deletions
@@ -189,17 +189,36 @@ PeerAddress::InAddrSize() const
} }
AddressResolver::AddressResolver(const char* name)
:
fHead(NULL),
fCurrent(NULL),
fForcedPort(htons(NFS4_PORT)),
fForcedProtocol(IPPROTO_TCP)
{
fStatus = ResolveAddress(name);
}
AddressResolver::~AddressResolver()
{
freeaddrinfo(fHead);
}
status_t status_t
PeerAddress::ResolveName(const char* name, PeerAddress* address) AddressResolver::ResolveAddress(const char* name)
{ {
ASSERT(name != NULL); ASSERT(name != NULL);
ASSERT(address != NULL);
address->fProtocol = IPPROTO_TCP; if (fHead != NULL) {
freeaddrinfo(fHead);
fHead = NULL;
fCurrent = NULL;
}
// getaddrinfo() is very expensive when called from kernel, so we do not // getaddrinfo() is very expensive when called from kernel, so we do not
// want to call it unless there is no other choice. // want to call it unless there is no other choice.
struct sockaddr_in addr; struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
if (inet_aton(name, &addr.sin_addr) == 1) { if (inet_aton(name, &addr.sin_addr) == 1) {
@@ -207,35 +226,74 @@ PeerAddress::ResolveName(const char* name, PeerAddress* address)
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons(NFS4_PORT); addr.sin_port = htons(NFS4_PORT);
memcpy(&address->fAddress, &addr, sizeof(addr)); memcpy(&fAddress.fAddress, &addr, sizeof(addr));
fAddress.fProtocol = IPPROTO_TCP;
return B_OK; return B_OK;
} }
addrinfo* ai; status_t result = getaddrinfo(name, NULL, NULL, &fHead);
status_t result = getaddrinfo(name, NULL, NULL, &ai); fCurrent = fHead;
if (result != B_OK)
return result;
addrinfo* current = ai; return result;
while (current != NULL) { }
if (current->ai_family == AF_INET) {
memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in));
void
AddressResolver::ForceProtocol(const char* protocol)
{
ASSERT(protocol != NULL);
if (strcmp(protocol, "tcp") == 0)
fForcedProtocol = IPPROTO_TCP;
else if (strcmp(protocol, "udp") == 0)
fForcedProtocol = IPPROTO_UDP;
fAddress.SetProtocol(protocol);
}
void
AddressResolver::ForcePort(uint16 port)
{
fForcedPort = htons(port);
fAddress.SetPort(port);
}
status_t
AddressResolver::GetNextAddress(PeerAddress* address)
{
ASSERT(address != NULL);
if (fStatus != B_OK)
return fStatus;
if (fHead == NULL) {
*address = fAddress;
fStatus = B_NAME_NOT_FOUND;
return B_OK;
}
address->fProtocol = fForcedProtocol;
while (fCurrent != NULL) {
if (fCurrent->ai_family == AF_INET) {
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in));
reinterpret_cast<sockaddr_in*>(&address->fAddress)->sin_port reinterpret_cast<sockaddr_in*>(&address->fAddress)->sin_port
= htons(NFS4_PORT); = fForcedPort;
} else if (current->ai_family == AF_INET6) { } else if (fCurrent->ai_family == AF_INET6) {
memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in6)); memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6));
reinterpret_cast<sockaddr_in6*>(&address->fAddress)->sin6_port reinterpret_cast<sockaddr_in6*>(&address->fAddress)->sin6_port
= htons(NFS4_PORT); = fForcedPort;
} else { } else {
current = current->ai_next; fCurrent = fCurrent->ai_next;
continue; continue;
} }
freeaddrinfo(ai); fCurrent = fCurrent->ai_next;
return B_OK; return B_OK;
} }
freeaddrinfo(ai);
return B_NAME_NOT_FOUND; return B_NAME_NOT_FOUND;
} }
@@ -38,9 +38,33 @@ struct PeerAddress {
const void* InAddr() const; const void* InAddr() const;
size_t InAddrSize() const; size_t InAddrSize() const;
};
static status_t ResolveName(const char* name, struct addrinfo;
PeerAddress* address);
class AddressResolver {
public:
AddressResolver(const char* name);
~AddressResolver();
status_t GetNextAddress(PeerAddress* address);
void ForceProtocol(const char* protocol);
void ForcePort(uint16 port);
protected:
status_t ResolveAddress(const char* name);
private:
addrinfo* fHead;
addrinfo* fCurrent;
PeerAddress fAddress;
uint16 fForcedPort;
int fForcedProtocol;
status_t fStatus;
}; };
class ConnectionBase { class ConnectionBase {
@@ -257,14 +257,11 @@ FileSystem::Migrate(const RPC::Server* serv)
= reinterpret_cast<FSLocations*>(values[0].fData.fLocations); = reinterpret_cast<FSLocations*>(values[0].fData.fLocations);
RPC::Server* server = fServer; RPC::Server* server = fServer;
PeerAddress addr = fServer->ID();
for (uint32 i = 0; i < locs->fCount; i++) { for (uint32 i = 0; i < locs->fCount; i++) {
for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) { for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) {
if (PeerAddress::ResolveName(locs->fLocations[i].fLocations[j], AddressResolver resolver(locs->fLocations[i].fLocations[j]);
&addr) != B_OK)
continue;
if (gRPCServerManager->Acquire(&fServer, addr, if (gRPCServerManager->Acquire(&fServer, &resolver,
CreateNFS4Server) == B_OK) { CreateNFS4Server) == B_OK) {
free(const_cast<char*>(fPath)); free(const_cast<char*>(fPath));
@@ -312,7 +312,24 @@ ServerManager::~ServerManager()
status_t status_t
ServerManager::Acquire(Server** _server, const PeerAddress& address, ServerManager::Acquire(Server** _server, AddressResolver* resolver,
ProgramData* (*createPrivateData)(Server*))
{
PeerAddress address;
status_t result;
while ((result = resolver->GetNextAddress(&address)) == B_OK) {
result = _Acquire(_server, address, createPrivateData);
if (result == B_OK)
break;
}
return result;
}
status_t
ServerManager::_Acquire(Server** _server, const PeerAddress& address,
ProgramData* (*createPrivateData)(Server*)) ProgramData* (*createPrivateData)(Server*))
{ {
ASSERT(_server != NULL); ASSERT(_server != NULL);
@@ -171,11 +171,13 @@ public:
ServerManager(); ServerManager();
~ServerManager(); ~ServerManager();
status_t Acquire(Server** _server, const PeerAddress& address, status_t Acquire(Server** _server, AddressResolver* resolver,
ProgramData* (*createPrivateData)(Server*)); ProgramData* (*createPrivateData)(Server*));
void Release(Server* server); void Release(Server* server);
private: private:
status_t _Acquire(Server** _server, const PeerAddress& address,
ProgramData* (*createPrivateData)(Server*));
ServerNode* _Find(const PeerAddress& address); ServerNode* _Find(const PeerAddress& address);
void _Delete(ServerNode* node); void _Delete(ServerNode* node);
@@ -72,7 +72,7 @@ CreateNFS4Server(RPC::Server* serv)
// port=X - connect to port X (default: 2049) // port=X - connect to port X (default: 2049)
// proto=X - user transport protocol X (default: tcp) // proto=X - user transport protocol X (default: tcp)
static status_t static status_t
ParseArguments(const char* _args, PeerAddress* address, char** _path, ParseArguments(const char* _args, AddressResolver** address, char** _path,
MountConfiguration* conf) MountConfiguration* conf)
{ {
if (_args == NULL) if (_args == NULL)
@@ -92,13 +92,15 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path,
return B_MISMATCHED_VALUES; return B_MISMATCHED_VALUES;
*path++ = '\0'; *path++ = '\0';
status_t result = PeerAddress::ResolveName(args, address); *address = new AddressResolver(args);
if (result != B_OK) if (*address == NULL)
return result; return B_NO_MEMORY;
*_path = strdup(path); *_path = strdup(path);
if (*_path == NULL) if (*_path == NULL) {
delete *address;
return B_NO_MEMORY; return B_NO_MEMORY;
}
conf->fHard = false; conf->fHard = false;
conf->fRetryLimit = 5; conf->fRetryLimit = 5;
@@ -127,10 +129,10 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path,
conf->fEmulateNamedAttrs = true; conf->fEmulateNamedAttrs = true;
else if (strncmp(options, "port=", 5) == 0) { else if (strncmp(options, "port=", 5) == 0) {
options += strlen("port="); options += strlen("port=");
address->SetPort(atoi(options)); (*address)->ForcePort(atoi(options));
} else if (strncmp(options, "proto=", 6) == 0) { } else if (strncmp(options, "proto=", 6) == 0) {
options += strlen("proto="); options += strlen("proto=");
address->SetProtocol(options); (*address)->ForceProtocol(options);
} }
options = optionsEnd; options = optionsEnd;
@@ -165,16 +167,17 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
} }
locker.Unlock(); locker.Unlock();
PeerAddress address; AddressResolver* resolver;
MountConfiguration config; MountConfiguration config;
char* path; char* path;
result = ParseArguments(args, &address, &path, &config); result = ParseArguments(args, &resolver, &path, &config);
if (result != B_OK) if (result != B_OK)
return result; return result;
MemoryDeleter pathDeleter(path); MemoryDeleter pathDeleter(path);
RPC::Server* server; RPC::Server* server;
result = gRPCServerManager->Acquire(&server, address, CreateNFS4Server); result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server);
delete resolver;
if (result != B_OK) if (result != B_OK)
return result; return result;