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
PeerAddress::ResolveName(const char* name, PeerAddress* address)
AddressResolver::ResolveAddress(const char* name)
{
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
// want to call it unless there is no other choice.
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
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_port = htons(NFS4_PORT);
memcpy(&address->fAddress, &addr, sizeof(addr));
memcpy(&fAddress.fAddress, &addr, sizeof(addr));
fAddress.fProtocol = IPPROTO_TCP;
return B_OK;
}
addrinfo* ai;
status_t result = getaddrinfo(name, NULL, NULL, &ai);
if (result != B_OK)
return result;
status_t result = getaddrinfo(name, NULL, NULL, &fHead);
fCurrent = fHead;
addrinfo* current = ai;
while (current != NULL) {
if (current->ai_family == AF_INET) {
memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in));
return result;
}
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
= htons(NFS4_PORT);
} else if (current->ai_family == AF_INET6) {
memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in6));
= fForcedPort;
} else if (fCurrent->ai_family == AF_INET6) {
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6));
reinterpret_cast<sockaddr_in6*>(&address->fAddress)->sin6_port
= htons(NFS4_PORT);
= fForcedPort;
} else {
current = current->ai_next;
fCurrent = fCurrent->ai_next;
continue;
}
freeaddrinfo(ai);
fCurrent = fCurrent->ai_next;
return B_OK;
}
freeaddrinfo(ai);
return B_NAME_NOT_FOUND;
}
@@ -38,9 +38,33 @@ struct PeerAddress {
const void* InAddr() const;
size_t InAddrSize() const;
};
static status_t ResolveName(const char* name,
PeerAddress* address);
struct addrinfo;
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 {
@@ -257,14 +257,11 @@ FileSystem::Migrate(const RPC::Server* serv)
= reinterpret_cast<FSLocations*>(values[0].fData.fLocations);
RPC::Server* server = fServer;
PeerAddress addr = fServer->ID();
for (uint32 i = 0; i < locs->fCount; i++) {
for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) {
if (PeerAddress::ResolveName(locs->fLocations[i].fLocations[j],
&addr) != B_OK)
continue;
AddressResolver resolver(locs->fLocations[i].fLocations[j]);
if (gRPCServerManager->Acquire(&fServer, addr,
if (gRPCServerManager->Acquire(&fServer, &resolver,
CreateNFS4Server) == B_OK) {
free(const_cast<char*>(fPath));
@@ -312,7 +312,24 @@ ServerManager::~ServerManager()
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*))
{
ASSERT(_server != NULL);
@@ -171,11 +171,13 @@ public:
ServerManager();
~ServerManager();
status_t Acquire(Server** _server, const PeerAddress& address,
status_t Acquire(Server** _server, AddressResolver* resolver,
ProgramData* (*createPrivateData)(Server*));
void Release(Server* server);
private:
status_t _Acquire(Server** _server, const PeerAddress& address,
ProgramData* (*createPrivateData)(Server*));
ServerNode* _Find(const PeerAddress& address);
void _Delete(ServerNode* node);
@@ -72,7 +72,7 @@ CreateNFS4Server(RPC::Server* serv)
// port=X - connect to port X (default: 2049)
// proto=X - user transport protocol X (default: tcp)
static status_t
ParseArguments(const char* _args, PeerAddress* address, char** _path,
ParseArguments(const char* _args, AddressResolver** address, char** _path,
MountConfiguration* conf)
{
if (_args == NULL)
@@ -92,13 +92,15 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path,
return B_MISMATCHED_VALUES;
*path++ = '\0';
status_t result = PeerAddress::ResolveName(args, address);
if (result != B_OK)
return result;
*address = new AddressResolver(args);
if (*address == NULL)
return B_NO_MEMORY;
*_path = strdup(path);
if (*_path == NULL)
if (*_path == NULL) {
delete *address;
return B_NO_MEMORY;
}
conf->fHard = false;
conf->fRetryLimit = 5;
@@ -127,10 +129,10 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path,
conf->fEmulateNamedAttrs = true;
else if (strncmp(options, "port=", 5) == 0) {
options += strlen("port=");
address->SetPort(atoi(options));
(*address)->ForcePort(atoi(options));
} else if (strncmp(options, "proto=", 6) == 0) {
options += strlen("proto=");
address->SetProtocol(options);
(*address)->ForceProtocol(options);
}
options = optionsEnd;
@@ -165,16 +167,17 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
}
locker.Unlock();
PeerAddress address;
AddressResolver* resolver;
MountConfiguration config;
char* path;
result = ParseArguments(args, &address, &path, &config);
result = ParseArguments(args, &resolver, &path, &config);
if (result != B_OK)
return result;
MemoryDeleter pathDeleter(path);
RPC::Server* server;
result = gRPCServerManager->Acquire(&server, address, CreateNFS4Server);
result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server);
delete resolver;
if (result != B_OK)
return result;