* No longer leaks the EndpointManagers at unload.

* Now uses an array instead of a doubly linked list to find the endpoint
  manager for a domain.
* No longer locks the endpoint managers during TCP processing, which actually
  made all TCP input serialized.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29210 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2009-02-15 14:33:41 +00:00
parent b435702774
commit f964cf0772
3 changed files with 86 additions and 61 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -16,7 +16,6 @@
#include <KernelExport.h> #include <KernelExport.h>
#include <NetUtilities.h> #include <NetUtilities.h>
#include <util/AutoLock.h>
#include <tracing.h> #include <tracing.h>
#include "TCPEndpoint.h" #include "TCPEndpoint.h"
@@ -221,13 +220,13 @@ EndpointManager::EndpointManager(net_domain* domain)
fConnectionHash(this), fConnectionHash(this),
fLastPort(kFirstEphemeralPort) fLastPort(kFirstEphemeralPort)
{ {
mutex_init(&fLock, "endpoint manager"); rw_lock_init(&fLock, "TCP endpoint manager");
} }
EndpointManager::~EndpointManager() EndpointManager::~EndpointManager()
{ {
mutex_destroy(&fLock); rw_lock_destroy(&fLock);
} }
@@ -246,7 +245,8 @@ EndpointManager::Init()
/*! Returns the endpoint matching the connection. /*! Returns the endpoint matching the connection.
You must hold the manager's lock when calling this method. You must hold the manager's lock when calling this method (either read or
write).
*/ */
TCPEndpoint* TCPEndpoint*
EndpointManager::_LookupConnection(const sockaddr* local, const sockaddr* peer) EndpointManager::_LookupConnection(const sockaddr* local, const sockaddr* peer)
@@ -261,7 +261,7 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const sockaddr* _local,
{ {
TRACE(("EndpointManager::SetConnection(%p)\n", endpoint)); TRACE(("EndpointManager::SetConnection(%p)\n", endpoint));
MutexLocker _(fLock); WriteLocker _(fLock);
SocketAddressStorage local(AddressModule()); SocketAddressStorage local(AddressModule());
local.SetTo(_local); local.SetTo(_local);
@@ -287,7 +287,7 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const sockaddr* _local,
status_t status_t
EndpointManager::SetPassive(TCPEndpoint* endpoint) EndpointManager::SetPassive(TCPEndpoint* endpoint)
{ {
MutexLocker _(fLock); WriteLocker _(fLock);
if (!endpoint->IsBound()) { if (!endpoint->IsBound()) {
// if the socket is unbound first bind it to ephemeral // if the socket is unbound first bind it to ephemeral
@@ -314,7 +314,7 @@ EndpointManager::SetPassive(TCPEndpoint* endpoint)
TCPEndpoint* TCPEndpoint*
EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) EndpointManager::FindConnection(sockaddr* local, sockaddr* peer)
{ {
MutexLocker _(fLock); ReadLocker _(fLock);
TCPEndpoint *endpoint = _LookupConnection(local, peer); TCPEndpoint *endpoint = _LookupConnection(local, peer);
if (endpoint != NULL) { if (endpoint != NULL) {
@@ -356,31 +356,31 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer)
status_t status_t
EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address) EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address)
{ {
// TODO check the family: // TODO: check the family:
//
// if (!AddressModule()->is_understandable(address)) // if (!AddressModule()->is_understandable(address))
// return EAFNOSUPPORT; // return EAFNOSUPPORT;
MutexLocker _(fLock); WriteLocker locker(fLock);
if (AddressModule()->get_port(address) == 0) if (AddressModule()->get_port(address) == 0)
return _BindToEphemeral(endpoint, address); return _BindToEphemeral(endpoint, address);
return _BindToAddress(endpoint, address); return _BindToAddress(locker, endpoint, address);
} }
status_t status_t
EndpointManager::BindChild(TCPEndpoint* endpoint) EndpointManager::BindChild(TCPEndpoint* endpoint)
{ {
MutexLocker _(fLock); WriteLocker _(fLock);
return _Bind(endpoint, *endpoint->LocalAddress()); return _Bind(endpoint, *endpoint->LocalAddress());
} }
/*! You must hold fLock when calling this method. */ /*! You must have fLock write locked when calling this method. */
status_t status_t
EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address) EndpointManager::_BindToAddress(WriteLocker& locker, TCPEndpoint* endpoint,
const sockaddr* _address)
{ {
ConstSocketAddress address(AddressModule(), _address); ConstSocketAddress address(AddressModule(), _address);
uint16 port = address.Port(); uint16 port = address.Port();
@@ -388,7 +388,7 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint)); TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint));
T(Bind(endpoint, address, false)); T(Bind(endpoint, address, false));
// TODO this check follows very typical UNIX semantics // TODO: this check follows very typical UNIX semantics
// and generally should be improved. // and generally should be improved.
if (ntohs(port) <= kLastReservedPort && geteuid() != 0) if (ntohs(port) <= kLastReservedPort && geteuid() != 0)
return B_PERMISSION_DENIED; return B_PERMISSION_DENIED;
@@ -414,9 +414,9 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
&& (userState > ESTABLISHED || userState == CLOSED)) { && (userState > ESTABLISHED || userState == CLOSED)) {
// This is a closing local connection - wait until it's // This is a closing local connection - wait until it's
// gone away for real // gone away for real
mutex_unlock(&fLock); locker.Unlock();
snooze(10000); snooze(10000);
mutex_lock(&fLock); locker.Lock();
// TODO: make this better // TODO: make this better
if (!retrying) { if (!retrying) {
retrying = true; retrying = true;
@@ -438,7 +438,7 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address)
} }
/*! You must hold fLock when calling this method. */ /*! You must have fLock write locked when calling this method. */
status_t status_t
EndpointManager::_BindToEphemeral(TCPEndpoint* endpoint, EndpointManager::_BindToEphemeral(TCPEndpoint* endpoint,
const sockaddr* address) const sockaddr* address)
@@ -509,7 +509,7 @@ EndpointManager::Unbind(TCPEndpoint* endpoint)
return B_BAD_VALUE; return B_BAD_VALUE;
} }
MutexLocker _(fLock); WriteLocker _(fLock);
if (!fEndpointHash.Remove(endpoint)) if (!fEndpointHash.Remove(endpoint))
panic("bound endpoint %p not in hash!", endpoint); panic("bound endpoint %p not in hash!", endpoint);
@@ -1,5 +1,5 @@
/* /*
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -15,6 +15,7 @@
#include <AddressUtilities.h> #include <AddressUtilities.h>
#include <lock.h> #include <lock.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/MultiHashTable.h> #include <util/MultiHashTable.h>
#include <util/OpenHashTable.h> #include <util/OpenHashTable.h>
@@ -98,15 +99,15 @@ private:
const sockaddr* peer); const sockaddr* peer);
status_t _Bind(TCPEndpoint* endpoint, status_t _Bind(TCPEndpoint* endpoint,
const sockaddr* address); const sockaddr* address);
status_t _BindToAddress(TCPEndpoint* endpoint, status_t _BindToAddress(WriteLocker& locker,
const sockaddr* address); TCPEndpoint* endpoint, const sockaddr* address);
status_t _BindToEphemeral(TCPEndpoint* endpoint, status_t _BindToEphemeral(TCPEndpoint* endpoint,
const sockaddr* address); const sockaddr* address);
typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable; typedef OpenHashTable<ConnectionHashDefinition> ConnectionTable;
typedef MultiHashTable<EndpointHashDefinition> EndpointTable; typedef MultiHashTable<EndpointHashDefinition> EndpointTable;
mutex fLock; rw_lock fLock;
net_domain* fDomain; net_domain* fDomain;
ConnectionTable fConnectionHash; ConnectionTable fConnectionHash;
EndpointTable fEndpointHash; EndpointTable fEndpointHash;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -50,30 +50,34 @@ net_socket_module_info *gSocketModule;
net_stack_module_info *gStackModule; net_stack_module_info *gStackModule;
// TODO we need to think of a better way to do this. It would be static EndpointManager* sEndpointManagers[AF_MAX];
// nice if we registered a per EndpointManager receiving static rw_lock sEndpointManagersLock;
// protocol cookie, so we don't have to go through the list
// for each segment.
typedef DoublyLinkedList<EndpointManager> EndpointManagerList;
static mutex sEndpointManagersLock;
static EndpointManagerList sEndpointManagers;
// The TCP header length is at most 64 bytes. // The TCP header length is at most 64 bytes.
static const int kMaxOptionSize = 64 - sizeof(tcp_header); static const int kMaxOptionSize = 64 - sizeof(tcp_header);
static EndpointManager * /*! Returns an endpoint manager for the specified domain, if any.
endpoint_manager_for(net_domain *domain) You need to hold the sEndpointManagersLock when calling this function.
*/
static inline EndpointManager*
endpoint_manager_for_locked(int family)
{ {
EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); if (family >= AF_MAX || family < 0)
while (iterator.HasNext()) { return NULL;
EndpointManager *endpointManager = iterator.Next();
if (endpointManager->Domain() == domain) return sEndpointManagers[family];
return endpointManager;
} }
return NULL;
/*! Returns an endpoint manager for the specified domain, if any */
static inline EndpointManager*
endpoint_manager_for(net_domain* domain)
{
ReadLocker _(sEndpointManagersLock);
return endpoint_manager_for_locked(domain->family);
} }
@@ -251,10 +255,11 @@ dump_tcp_header(tcp_header &header)
static int static int
dump_endpoints(int argc, char** argv) dump_endpoints(int argc, char** argv)
{ {
EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); for (int i = 0; i < AF_MAX; i++) {
EndpointManager* manager = sEndpointManagers[i];
while (iterator.HasNext()) if (manager != NULL)
iterator.Next()->Dump(); manager->Dump();
}
return 0; return 0;
} }
@@ -278,13 +283,26 @@ dump_endpoint(int argc, char** argv)
// #pragma mark - internal API // #pragma mark - internal API
/*! Creates a new endpoint manager for the specified domain, or returns
an existing one for this domain.
*/
EndpointManager* EndpointManager*
get_endpoint_manager(net_domain* domain) get_endpoint_manager(net_domain* domain)
{ {
// See if there is one already
EndpointManager* endpointManager = endpoint_manager_for(domain); EndpointManager* endpointManager = endpoint_manager_for(domain);
if (endpointManager) if (endpointManager != NULL)
return endpointManager; return endpointManager;
WriteLocker _(sEndpointManagersLock);
endpointManager = endpoint_manager_for_locked(domain->family);
if (endpointManager != NULL)
return endpointManager;
// There is no endpoint manager for this domain yet, so we need
// to create one.
endpointManager = new(std::nothrow) EndpointManager(domain); endpointManager = new(std::nothrow) EndpointManager(domain);
if (endpointManager == NULL) if (endpointManager == NULL)
return NULL; return NULL;
@@ -294,7 +312,7 @@ get_endpoint_manager(net_domain* domain)
return NULL; return NULL;
} }
sEndpointManagers.Add(endpointManager); sEndpointManagers[domain->family] = endpointManager;
return endpointManager; return endpointManager;
} }
@@ -302,9 +320,9 @@ get_endpoint_manager(net_domain* domain)
void void
put_endpoint_manager(EndpointManager* endpointManager) put_endpoint_manager(EndpointManager* endpointManager)
{ {
// TODO: when the connection and endpoint count reach zero // TODO: we may want to use reference counting instead of only discarding
// we should remove the endpoint manager from the endpoints // them on unload. But since there is likely only IPv4/v6 there is not much
// list and delete it. // point to it.
} }
@@ -386,7 +404,7 @@ add_tcp_header(net_address_module_info* addressModule,
optionsLength); optionsLength);
} }
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %lu, " TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %u, "
"win %u\n", buffer, segment.flags, segment.sequence, "win %u\n", buffer, segment.flags, segment.sequence,
segment.acknowledge, segment.urgent_offset, segment.advertised_window)); segment.acknowledge, segment.urgent_offset, segment.advertised_window));
@@ -680,11 +698,11 @@ tcp_receive_data(net_buffer* buffer)
bufferHeader.Remove(headerLength); bufferHeader.Remove(headerLength);
// we no longer need to keep the header around // we no longer need to keep the header around
MutexLocker _(sEndpointManagersLock);
EndpointManager* endpointManager = endpoint_manager_for(domain); EndpointManager* endpointManager = endpoint_manager_for(domain);
if (endpointManager == NULL) if (endpointManager == NULL) {
TRACE((" No endpoint manager!\n"));
return B_ERROR; return B_ERROR;
}
int32 segmentAction = DROP; int32 segmentAction = DROP;
@@ -695,11 +713,11 @@ tcp_receive_data(net_buffer* buffer)
else if ((segment.flags & TCP_FLAG_RESET) == 0) else if ((segment.flags & TCP_FLAG_RESET) == 0)
segmentAction = DROP | RESET; segmentAction = DROP | RESET;
if (segmentAction & RESET) { if ((segmentAction & RESET) != 0) {
// send reset // send reset
endpointManager->ReplyWithReset(segment, buffer); endpointManager->ReplyWithReset(segment, buffer);
} }
if (segmentAction & DROP) if ((segmentAction & DROP) != 0)
gBufferModule->free(buffer); gBufferModule->free(buffer);
return B_OK; return B_OK;
@@ -727,7 +745,7 @@ tcp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code,
static status_t static status_t
tcp_init() tcp_init()
{ {
mutex_init(&sEndpointManagersLock, "endpoint managers lock"); rw_lock_init(&sEndpointManagersLock, "endpoint managers");
status_t status = gStackModule->register_domain_protocols(AF_INET, status_t status = gStackModule->register_domain_protocols(AF_INET,
SOCK_STREAM, 0, SOCK_STREAM, 0,
@@ -764,7 +782,13 @@ tcp_uninit()
{ {
remove_debugger_command("tcp_endpoint", dump_endpoint); remove_debugger_command("tcp_endpoint", dump_endpoint);
remove_debugger_command("tcp_endpoints", dump_endpoints); remove_debugger_command("tcp_endpoints", dump_endpoints);
mutex_destroy(&sEndpointManagersLock);
rw_lock_destroy(&sEndpointManagersLock);
for (int i = 0; i < AF_MAX; i++) {
delete sEndpointManagers[i];
}
return B_OK; return B_OK;
} }