even more TCP fixage.
- don't try to remove spawned sockets from Endpoints hashtable. - return B_WOULD_BLOCK (EAGAIN) when we time out in acquire_sem(). - use B_RELEASE_IF_WAITING_ONLY in the TCP WaitList. - fixed a off by one issue in ReadData() which could result in more than needed iterations (and waiting). - implemented prepending new buffers to a net_buffer. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20639 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -319,25 +319,27 @@ EndpointManager::Unbind(TCPEndpoint *endpoint)
|
|||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
RecursiveLocker locker(&fLock);
|
||||||
|
|
||||||
TCPEndpoint *other = _LookupEndpoint(gAddressModule->get_port(
|
if (!endpoint->fSpawned) {
|
||||||
(sockaddr *)&endpoint->socket->address));
|
TCPEndpoint *other = _LookupEndpoint(gAddressModule->get_port(
|
||||||
if (other != endpoint) {
|
(sockaddr *)&endpoint->socket->address));
|
||||||
// remove endpoint from the list of endpoints with the same port
|
if (other != endpoint) {
|
||||||
while (other != NULL && other->fEndpointNextWithSamePort != endpoint) {
|
// remove endpoint from the list of endpoints with the same port
|
||||||
other = other->fEndpointNextWithSamePort;
|
while (other != NULL && other->fEndpointNextWithSamePort != endpoint) {
|
||||||
|
other = other->fEndpointNextWithSamePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other != NULL)
|
||||||
|
other->fEndpointNextWithSamePort = endpoint->fEndpointNextWithSamePort;
|
||||||
|
else
|
||||||
|
panic("bound endpoint %p not in hash!", endpoint);
|
||||||
|
} else {
|
||||||
|
// we need to replace the first endpoint in the list
|
||||||
|
hash_remove(fEndpointHash, endpoint);
|
||||||
|
|
||||||
|
other = endpoint->fEndpointNextWithSamePort;
|
||||||
|
if (other != NULL)
|
||||||
|
hash_insert(fEndpointHash, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (other != NULL)
|
|
||||||
other->fEndpointNextWithSamePort = endpoint->fEndpointNextWithSamePort;
|
|
||||||
else
|
|
||||||
panic("bound endpoint %p not in hash!", endpoint);
|
|
||||||
} else {
|
|
||||||
// we need to replace the first endpoint in the list
|
|
||||||
hash_remove(fEndpointHash, endpoint);
|
|
||||||
|
|
||||||
other = endpoint->fEndpointNextWithSamePort;
|
|
||||||
if (other != NULL)
|
|
||||||
hash_insert(fEndpointHash, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint->fEndpointNextWithSamePort = NULL;
|
endpoint->fEndpointNextWithSamePort = NULL;
|
||||||
|
|||||||
@@ -49,8 +49,9 @@
|
|||||||
//#define TRACE_TCP
|
//#define TRACE_TCP
|
||||||
|
|
||||||
#ifdef TRACE_TCP
|
#ifdef TRACE_TCP
|
||||||
// the space after 'this' is important in order for this to work with cpp 2.95
|
// the space before ', ##args' is important in order for this to work with cpp 2.95
|
||||||
# define TRACE(format, args...) dprintf("TCP:%p:" format "\n", this , ##args)
|
# define TRACE(format, args...) dprintf("TCP [%llu] %p (%12s) " format "\n", \
|
||||||
|
system_time(), this, name_for_state(fState) , ##args)
|
||||||
#else
|
#else
|
||||||
# define TRACE(args...) do { } while (0)
|
# define TRACE(args...) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
@@ -79,8 +80,17 @@ absolute_timeout(bigtime_t timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline status_t
|
||||||
|
posix_error(status_t error)
|
||||||
|
{
|
||||||
|
if (error == B_TIMED_OUT)
|
||||||
|
return B_WOULD_BLOCK;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
WaitList::WaitList(const char *name)
|
WaitList::WaitList(const char *name)
|
||||||
: fWaiting(0)
|
|
||||||
{
|
{
|
||||||
fSem = create_sem(0, name);
|
fSem = create_sem(0, name);
|
||||||
}
|
}
|
||||||
@@ -100,15 +110,13 @@ WaitList::InitCheck() const
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
WaitList::Wait(RecursiveLocker &locker, bigtime_t timeout)
|
WaitList::Wait(RecursiveLocker &locker, bigtime_t timeout, bool wakeNext)
|
||||||
{
|
{
|
||||||
// this function is called with `locker' held.
|
|
||||||
fWaiting++;
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
status_t status = acquire_sem_etc(fSem, 1, B_ABSOLUTE_TIMEOUT |
|
status_t status = acquire_sem_etc(fSem, 1, B_ABSOLUTE_TIMEOUT
|
||||||
B_CAN_INTERRUPT, timeout);
|
| B_CAN_INTERRUPT, timeout);
|
||||||
locker.Lock();
|
locker.Lock();
|
||||||
if (status == B_OK)
|
if (wakeNext && status == B_OK)
|
||||||
Signal();
|
Signal();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -117,14 +125,8 @@ WaitList::Wait(RecursiveLocker &locker, bigtime_t timeout)
|
|||||||
void
|
void
|
||||||
WaitList::Signal()
|
WaitList::Signal()
|
||||||
{
|
{
|
||||||
// the same locker used with Wait() must be held
|
release_sem_etc(fSem, 1, B_DO_NOT_RESCHEDULE
|
||||||
// when calling this function.
|
| B_RELEASE_IF_WAITING_ONLY);
|
||||||
|
|
||||||
if (fWaiting == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fWaiting--;
|
|
||||||
release_sem_etc(fSem, 1, B_DO_NOT_RESCHEDULE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -152,7 +154,8 @@ TCPEndpoint::TCPEndpoint(net_socket *socket)
|
|||||||
fRoundTripTime(TCP_INITIAL_RTT),
|
fRoundTripTime(TCP_INITIAL_RTT),
|
||||||
fState(CLOSED),
|
fState(CLOSED),
|
||||||
fFlags(0), //FLAG_OPTION_WINDOW_SHIFT),
|
fFlags(0), //FLAG_OPTION_WINDOW_SHIFT),
|
||||||
fError(B_OK)
|
fError(B_OK),
|
||||||
|
fSpawned(false)
|
||||||
{
|
{
|
||||||
//gStackModule->init_timer(&fTimer, _TimeWait, this);
|
//gStackModule->init_timer(&fTimer, _TimeWait, this);
|
||||||
|
|
||||||
@@ -228,7 +231,7 @@ TCPEndpoint::Close()
|
|||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
TRACE("Close(): Entering state %s", name_for_state(fState));
|
TRACE("Close() after Shutdown()");
|
||||||
|
|
||||||
if (socket->options & SO_LINGER) {
|
if (socket->options & SO_LINGER) {
|
||||||
TRACE("Close(): Lingering for %i secs", socket->linger);
|
TRACE("Close(): Lingering for %i secs", socket->linger);
|
||||||
@@ -277,13 +280,8 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
|||||||
TRACE("Connect() on address %s",
|
TRACE("Connect() on address %s",
|
||||||
AddressString(gDomain, address, true).Data());
|
AddressString(gDomain, address, true).Data());
|
||||||
|
|
||||||
if (address->sa_family != AF_INET)
|
|
||||||
return EAFNOSUPPORT;
|
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
RecursiveLocker locker(&fLock);
|
||||||
|
|
||||||
TRACE(" Connect(): in state %s", name_for_state(fState));
|
|
||||||
|
|
||||||
// Can only call connect() from CLOSED or LISTEN states
|
// Can only call connect() from CLOSED or LISTEN states
|
||||||
// otherwise endpoint is considered already connected
|
// otherwise endpoint is considered already connected
|
||||||
if (fState == LISTEN) {
|
if (fState == LISTEN) {
|
||||||
@@ -355,7 +353,7 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
|||||||
|
|
||||||
TRACE(" Connect(): Connection complete: %s", strerror(status));
|
TRACE(" Connect(): Connection complete: %s", strerror(status));
|
||||||
|
|
||||||
return status;
|
return posix_error(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -506,7 +504,7 @@ TCPEndpoint::SendData(net_buffer *buffer)
|
|||||||
while (fSendQueue.Free() < chunk->size) {
|
while (fSendQueue.Free() < chunk->size) {
|
||||||
status_t status = fSendList.Wait(lock, timeout);
|
status_t status = fSendList.Wait(lock, timeout);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return posix_error(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check state!
|
// TODO: check state!
|
||||||
@@ -569,7 +567,7 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
|||||||
// we need to wait until the connection becomes established
|
// we need to wait until the connection becomes established
|
||||||
status_t status = fSendList.Wait(locker, timeout);
|
status_t status = fSendList.Wait(locker, timeout);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return posix_error(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,8 +587,8 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fReceiveQueue.Available() > dataNeeded ||
|
if (fReceiveQueue.Available() >= dataNeeded ||
|
||||||
(fReceiveQueue.PushedData() > 0
|
((fReceiveQueue.PushedData() > 0)
|
||||||
&& (fReceiveQueue.PushedData() >= fReceiveQueue.Available())))
|
&& (fReceiveQueue.PushedData() >= fReceiveQueue.Available())))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -603,7 +601,7 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
|||||||
if (flags & MSG_DONTWAIT)
|
if (flags & MSG_DONTWAIT)
|
||||||
return B_WOULD_BLOCK;
|
return B_WOULD_BLOCK;
|
||||||
|
|
||||||
status_t status = fReceiveList.Wait(locker, timeout);
|
status_t status = fReceiveList.Wait(locker, timeout, false);
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
// The Open Group base specification mentions that EINTR should be
|
// The Open Group base specification mentions that EINTR should be
|
||||||
// returned if the recv() is interrupted before _any data_ is
|
// returned if the recv() is interrupted before _any data_ is
|
||||||
@@ -613,7 +611,7 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
|||||||
&& fReceiveQueue.Available() > 0)
|
&& fReceiveQueue.Available() > 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
return status;
|
return posix_error(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,8 +705,8 @@ TCPEndpoint::UpdateTimeWait()
|
|||||||
int32
|
int32
|
||||||
TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE("ListenReceive(): Connection in state %s received packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
TRACE("ListenReceive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
||||||
name_for_state(fState), buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
||||||
|
|
||||||
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
||||||
// but the error behaviour differs
|
// but the error behaviour differs
|
||||||
@@ -733,6 +731,8 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol;
|
TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol;
|
||||||
|
|
||||||
|
endpoint->fSpawned = true;
|
||||||
|
|
||||||
// TODO: proper error handling!
|
// TODO: proper error handling!
|
||||||
|
|
||||||
endpoint->fRoute = gDatalinkModule->get_route(gDomain,
|
endpoint->fRoute = gDatalinkModule->get_route(gDomain,
|
||||||
@@ -796,6 +796,9 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
int32
|
int32
|
||||||
TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
|
TRACE("SynchronizeReceive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
||||||
|
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
||||||
|
|
||||||
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||||
&& (fInitialSendSequence >= segment.acknowledge
|
&& (fInitialSendSequence >= segment.acknowledge
|
||||||
|| fSendMax < segment.acknowledge))
|
|| fSendMax < segment.acknowledge))
|
||||||
@@ -855,8 +858,8 @@ TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buf
|
|||||||
int32
|
int32
|
||||||
TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE("Receive(): Connection in state %s received packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
TRACE("Receive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
||||||
name_for_state(fState), buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
||||||
|
|
||||||
// TODO: rethink locking!
|
// TODO: rethink locking!
|
||||||
|
|
||||||
@@ -1146,7 +1149,7 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
|
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
|
||||||
action |= ACKNOWLEDGE;
|
action |= ACKNOWLEDGE;
|
||||||
|
|
||||||
TRACE("Receive():Entering state %s, segment action %ld", name_for_state(fState), action);
|
TRACE("Receive() Action %ld", action);
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,11 @@ public:
|
|||||||
|
|
||||||
status_t InitCheck() const;
|
status_t InitCheck() const;
|
||||||
|
|
||||||
status_t Wait(RecursiveLocker &, bigtime_t timeout);
|
status_t Wait(RecursiveLocker &, bigtime_t timeout, bool wakeNext = true);
|
||||||
void Signal();
|
void Signal();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sem_id fSem;
|
sem_id fSem;
|
||||||
int32 fWaiting;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -143,6 +142,8 @@ class TCPEndpoint : public net_protocol {
|
|||||||
uint32 fFlags;
|
uint32 fFlags;
|
||||||
status_t fError;
|
status_t fError;
|
||||||
|
|
||||||
|
bool fSpawned;
|
||||||
|
|
||||||
// timer
|
// timer
|
||||||
net_timer fRetransmitTimer;
|
net_timer fRetransmitTimer;
|
||||||
net_timer fPersistTimer;
|
net_timer fPersistTimer;
|
||||||
|
|||||||
@@ -632,13 +632,47 @@ prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer)
|
|||||||
if (node->header_space < size) {
|
if (node->header_space < size) {
|
||||||
// we need to prepend a new buffer
|
// we need to prepend a new buffer
|
||||||
|
|
||||||
// TODO: implement me!
|
size_t bytesLeft = size;
|
||||||
panic("prepending buffer not implemented\n");
|
do {
|
||||||
|
if (node->header_space == 0) {
|
||||||
|
size_t headerSpace = BUFFER_SIZE - sizeof(data_header);
|
||||||
|
// no more space, need another data node
|
||||||
|
data_header *header = create_data_header(headerSpace);
|
||||||
|
if (header == NULL) {
|
||||||
|
// TODO: free up headers we already allocated!
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_node *previous = node;
|
||||||
|
node = (data_node *)add_data_node(header);
|
||||||
|
|
||||||
|
init_data_node(node, headerSpace);
|
||||||
|
node->header_space = header->data_space;
|
||||||
|
header->first_node = node;
|
||||||
|
|
||||||
|
list_insert_item_before(&buffer->buffers, previous, node);
|
||||||
|
} else {
|
||||||
|
size_t willConsume = min_c(size, node->header_space);
|
||||||
|
|
||||||
|
node->header_space -= willConsume;
|
||||||
|
node->start -= willConsume;
|
||||||
|
node->used += willConsume;
|
||||||
|
bytesLeft -= willConsume;
|
||||||
|
}
|
||||||
|
} while (bytesLeft > 0);
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
for (node = (data_node *)list_get_first_item(&buffer->buffers);
|
||||||
|
node != NULL; node = (data_node *)
|
||||||
|
list_get_next_item(&buffer->buffers, node)) {
|
||||||
|
node->offset = offset;
|
||||||
|
offset += node->used;
|
||||||
|
}
|
||||||
|
|
||||||
if (_contiguousBuffer)
|
if (_contiguousBuffer)
|
||||||
*_contiguousBuffer = NULL;
|
*_contiguousBuffer = NULL;
|
||||||
|
|
||||||
return B_ERROR;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the data fits into this buffer
|
// the data fits into this buffer
|
||||||
|
|||||||
Reference in New Issue
Block a user