diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp index af461f5d60..98376f4501 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp @@ -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. * * Authors: @@ -16,7 +16,6 @@ #include #include -#include #include #include "TCPEndpoint.h" @@ -221,13 +220,13 @@ EndpointManager::EndpointManager(net_domain* domain) fConnectionHash(this), fLastPort(kFirstEphemeralPort) { - mutex_init(&fLock, "endpoint manager"); + rw_lock_init(&fLock, "TCP endpoint manager"); } EndpointManager::~EndpointManager() { - mutex_destroy(&fLock); + rw_lock_destroy(&fLock); } @@ -246,7 +245,8 @@ EndpointManager::Init() /*! 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* 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)); - MutexLocker _(fLock); + WriteLocker _(fLock); SocketAddressStorage local(AddressModule()); local.SetTo(_local); @@ -287,7 +287,7 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const sockaddr* _local, status_t EndpointManager::SetPassive(TCPEndpoint* endpoint) { - MutexLocker _(fLock); + WriteLocker _(fLock); if (!endpoint->IsBound()) { // if the socket is unbound first bind it to ephemeral @@ -314,7 +314,7 @@ EndpointManager::SetPassive(TCPEndpoint* endpoint) TCPEndpoint* EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) { - MutexLocker _(fLock); + ReadLocker _(fLock); TCPEndpoint *endpoint = _LookupConnection(local, peer); if (endpoint != NULL) { @@ -356,31 +356,31 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) status_t EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address) { - // TODO check the family: - // + // TODO: check the family: // if (!AddressModule()->is_understandable(address)) // return EAFNOSUPPORT; - MutexLocker _(fLock); + WriteLocker locker(fLock); if (AddressModule()->get_port(address) == 0) return _BindToEphemeral(endpoint, address); - return _BindToAddress(endpoint, address); + return _BindToAddress(locker, endpoint, address); } status_t EndpointManager::BindChild(TCPEndpoint* endpoint) { - MutexLocker _(fLock); + WriteLocker _(fLock); 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 -EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address) +EndpointManager::_BindToAddress(WriteLocker& locker, TCPEndpoint* endpoint, + const sockaddr* _address) { ConstSocketAddress address(AddressModule(), _address); uint16 port = address.Port(); @@ -388,8 +388,8 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address) TRACE(("EndpointManager::BindToAddress(%p)\n", endpoint)); T(Bind(endpoint, address, false)); - // TODO this check follows very typical UNIX semantics - // and generally should be improved. + // TODO: this check follows very typical UNIX semantics + // and generally should be improved. if (ntohs(port) <= kLastReservedPort && geteuid() != 0) return B_PERMISSION_DENIED; @@ -414,9 +414,9 @@ EndpointManager::_BindToAddress(TCPEndpoint* endpoint, const sockaddr* _address) && (userState > ESTABLISHED || userState == CLOSED)) { // This is a closing local connection - wait until it's // gone away for real - mutex_unlock(&fLock); + locker.Unlock(); snooze(10000); - mutex_lock(&fLock); + locker.Lock(); // TODO: make this better if (!retrying) { 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 EndpointManager::_BindToEphemeral(TCPEndpoint* endpoint, const sockaddr* address) @@ -509,7 +509,7 @@ EndpointManager::Unbind(TCPEndpoint* endpoint) return B_BAD_VALUE; } - MutexLocker _(fLock); + WriteLocker _(fLock); if (!fEndpointHash.Remove(endpoint)) panic("bound endpoint %p not in hash!", endpoint); diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h index 5fa51df711..40545c3f53 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.h @@ -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. * * Authors: @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -98,15 +99,15 @@ private: const sockaddr* peer); status_t _Bind(TCPEndpoint* endpoint, const sockaddr* address); - status_t _BindToAddress(TCPEndpoint* endpoint, - const sockaddr* address); + status_t _BindToAddress(WriteLocker& locker, + TCPEndpoint* endpoint, const sockaddr* address); status_t _BindToEphemeral(TCPEndpoint* endpoint, const sockaddr* address); typedef OpenHashTable ConnectionTable; typedef MultiHashTable EndpointTable; - mutex fLock; + rw_lock fLock; net_domain* fDomain; ConnectionTable fConnectionHash; EndpointTable fEndpointHash; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 94c9009805..0df9eed324 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -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. * * Authors: @@ -50,30 +50,34 @@ net_socket_module_info *gSocketModule; net_stack_module_info *gStackModule; -// TODO we need to think of a better way to do this. It would be -// nice if we registered a per EndpointManager receiving -// protocol cookie, so we don't have to go through the list -// for each segment. -typedef DoublyLinkedList EndpointManagerList; -static mutex sEndpointManagersLock; -static EndpointManagerList sEndpointManagers; +static EndpointManager* sEndpointManagers[AF_MAX]; +static rw_lock sEndpointManagersLock; // The TCP header length is at most 64 bytes. static const int kMaxOptionSize = 64 - sizeof(tcp_header); -static EndpointManager * -endpoint_manager_for(net_domain *domain) +/*! Returns an endpoint manager for the specified domain, if any. + You need to hold the sEndpointManagersLock when calling this function. +*/ +static inline EndpointManager* +endpoint_manager_for_locked(int family) { - EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); - while (iterator.HasNext()) { - EndpointManager *endpointManager = iterator.Next(); - if (endpointManager->Domain() == domain) - return endpointManager; - } + if (family >= AF_MAX || family < 0) + return NULL; - return NULL; + return sEndpointManagers[family]; +} + + +/*! 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 dump_endpoints(int argc, char** argv) { - EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); - - while (iterator.HasNext()) - iterator.Next()->Dump(); + for (int i = 0; i < AF_MAX; i++) { + EndpointManager* manager = sEndpointManagers[i]; + if (manager != NULL) + manager->Dump(); + } return 0; } @@ -278,14 +283,27 @@ dump_endpoint(int argc, char** argv) // #pragma mark - internal API +/*! Creates a new endpoint manager for the specified domain, or returns + an existing one for this domain. +*/ EndpointManager* get_endpoint_manager(net_domain* domain) { - EndpointManager *endpointManager = endpoint_manager_for(domain); - if (endpointManager) + // See if there is one already + EndpointManager* endpointManager = endpoint_manager_for(domain); + if (endpointManager != NULL) return endpointManager; - endpointManager = new (std::nothrow) EndpointManager(domain); + 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); if (endpointManager == NULL) return NULL; @@ -294,7 +312,7 @@ get_endpoint_manager(net_domain* domain) return NULL; } - sEndpointManagers.Add(endpointManager); + sEndpointManagers[domain->family] = endpointManager; return endpointManager; } @@ -302,9 +320,9 @@ get_endpoint_manager(net_domain* domain) void put_endpoint_manager(EndpointManager* endpointManager) { - // TODO: when the connection and endpoint count reach zero - // we should remove the endpoint manager from the endpoints - // list and delete it. + // TODO: we may want to use reference counting instead of only discarding + // them on unload. But since there is likely only IPv4/v6 there is not much + // point to it. } @@ -386,7 +404,7 @@ add_tcp_header(net_address_module_info* addressModule, 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, segment.acknowledge, segment.urgent_offset, segment.advertised_window)); @@ -680,11 +698,11 @@ tcp_receive_data(net_buffer* buffer) bufferHeader.Remove(headerLength); // we no longer need to keep the header around - MutexLocker _(sEndpointManagersLock); - EndpointManager* endpointManager = endpoint_manager_for(domain); - if (endpointManager == NULL) + if (endpointManager == NULL) { + TRACE((" No endpoint manager!\n")); return B_ERROR; + } int32 segmentAction = DROP; @@ -695,11 +713,11 @@ tcp_receive_data(net_buffer* buffer) else if ((segment.flags & TCP_FLAG_RESET) == 0) segmentAction = DROP | RESET; - if (segmentAction & RESET) { + if ((segmentAction & RESET) != 0) { // send reset endpointManager->ReplyWithReset(segment, buffer); } - if (segmentAction & DROP) + if ((segmentAction & DROP) != 0) gBufferModule->free(buffer); return B_OK; @@ -727,7 +745,7 @@ tcp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code, static status_t tcp_init() { - mutex_init(&sEndpointManagersLock, "endpoint managers lock"); + rw_lock_init(&sEndpointManagersLock, "endpoint managers"); status_t status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 0, @@ -764,7 +782,13 @@ tcp_uninit() { remove_debugger_command("tcp_endpoint", dump_endpoint); 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; }