diff --git a/src/add-ons/kernel/network/stack/interfaces.cpp b/src/add-ons/kernel/network/stack/interfaces.cpp index 33b60d25f0..dc31f62df6 100644 --- a/src/add-ons/kernel/network/stack/interfaces.cpp +++ b/src/add-ons/kernel/network/stack/interfaces.cpp @@ -73,6 +73,20 @@ grab_device_interface(net_device_interface *interface) } +static void +notify_device_monitors(net_device_interface *interface, int32 event) +{ + DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator(); + while (iterator.HasNext()) { + // when we call Next() the next item in the list is obtained + // so it's safe for the "current" item to remove itself. + net_device_monitor *monitor = iterator.Next(); + + monitor->event(monitor, event); + } +} + + // #pragma mark - interfaces @@ -449,6 +463,8 @@ down_device_interface(net_device_interface *interface) device->flags &= ~IFF_UP; interface->module->down(device); + notify_device_monitors(interface, B_DEVICE_GOING_DOWN); + thread_id reader_thread = interface->reader_thread; // one of the callers must hold a reference to the net_device_interface @@ -684,14 +700,7 @@ device_removed(net_device *device) // ... [see delete_interface()] domain_removed_device_interface(interface); - DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator(); - while (iterator.HasNext()) { - // when we call Next() the next item in the list is obtained - // so it's safe for the "current" item to remove itself. - net_device_monitor *monitor = iterator.Next(); - - monitor->event(monitor, B_DEVICE_BEING_REMOVED); - } + notify_device_monitors(interface, B_DEVICE_BEING_REMOVED); // By now all of the monitors must have removed themselves. If they // didn't, they'll probably wait forever to be callback'ed again. diff --git a/src/add-ons/kernel/network/stack/link.cpp b/src/add-ons/kernel/network/stack/link.cpp index 48c5a3510f..6cb0feb015 100644 --- a/src/add-ons/kernel/network/stack/link.cpp +++ b/src/add-ons/kernel/network/stack/link.cpp @@ -127,19 +127,27 @@ LinkProtocol::ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer) { BenaphoreLocker _(fLock); - if (fMonitoredDevice == NULL) { - if (fFifo.current_bytes == 0) + bigtime_t timeout = socket->receive.timeout; + + if (timeout == 0) + flags |= MSG_DONTWAIT; + else if (timeout != B_INFINITE_TIMEOUT) + timeout += system_time(); + + while (fFifo.IsEmpty()) { + if (fMonitoredDevice == NULL) return ENODEV; + + status_t status = B_WOULD_BLOCK; + if ((flags & MSG_DONTWAIT) == 0) + status = fFifo.Wait(&fLock, timeout); + + if (status < B_OK) + return status; } - net_buffer *buffer; - status_t status = fFifo.Dequeue(&fLock, flags, socket->receive.timeout, - &buffer); - if (status < B_OK) - return status; - - *_buffer = buffer; - return B_OK; + *_buffer = fFifo.Dequeue(flags & MSG_PEEK); + return *_buffer ? B_OK : B_NO_MEMORY; } @@ -148,7 +156,7 @@ LinkProtocol::ReadAvail() const { BenaphoreLocker _(fLock); if (fMonitoredDevice == NULL) - return ECONNRESET; + return ENODEV; return fFifo.current_bytes; } @@ -188,12 +196,14 @@ LinkProtocol::_MonitorEvent(net_device_monitor *monitor, int32 event) { LinkProtocol *protocol = (LinkProtocol *)monitor->cookie; - // We currently maintain the monitor while the device is down - if (event == B_DEVICE_BEING_REMOVED) { + if (event == B_DEVICE_GOING_DOWN) { BenaphoreLocker _(protocol->fLock); protocol->_Unregister(); - notify_socket(protocol->socket, B_SELECT_READ, ECONNRESET); + if (protocol->fFifo.IsEmpty()) { + protocol->fFifo.WakeAll(); + notify_socket(protocol->socket, B_SELECT_READ, ENODEV); + } } } diff --git a/src/add-ons/kernel/network/stack/routes.cpp b/src/add-ons/kernel/network/stack/routes.cpp index 79ae9b66c2..552b804775 100644 --- a/src/add-ons/kernel/network/stack/routes.cpp +++ b/src/add-ons/kernel/network/stack/routes.cpp @@ -519,7 +519,7 @@ invalidate_routes(net_domain *_domain, net_interface *interface) // lock the domain throughout the send_data() routine. // These are the easy solutions, need to think about this. -hugo - if (route->interface == interface) + if (route->interface->index == interface->index) remove_route(domain, route); } } diff --git a/src/add-ons/kernel/network/stack/utility.cpp b/src/add-ons/kernel/network/stack/utility.cpp index 17bced7037..a357cc41bd 100644 --- a/src/add-ons/kernel/network/stack/utility.cpp +++ b/src/add-ons/kernel/network/stack/utility.cpp @@ -24,10 +24,20 @@ static thread_id sTimerThread; static bigtime_t sTimerTimeout; +static inline void +fifo_notify_one_reader(int32 &waiting, sem_id sem) +{ + if (waiting > 0) { + waiting--; + release_sem_etc(sem, 1, B_DO_NOT_RESCHEDULE); + } +} + + template static inline status_t base_fifo_init(FifoType *fifo, const char *name, size_t maxBytes) { - fifo->notify = create_sem(1, name); + fifo->notify = create_sem(0, name); fifo->max_bytes = maxBytes; fifo->current_bytes = 0; fifo->waiting = 0; @@ -45,86 +55,12 @@ base_fifo_enqueue_buffer(FifoType *fifo, net_buffer *buffer) list_add_item(&fifo->buffers, buffer); fifo->current_bytes += buffer->size; - - if (fifo->waiting > 0) { - fifo->waiting--; - release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE); - // we still hold the benaphore lock, so it makes no sense - // to reschedule after having released the sync semaphore - } + fifo_notify_one_reader(fifo->waiting, fifo->notify); return B_OK; } -/*! - Gets the first buffer from the FIFO. If there is no buffer, it - will wait depending on the \a flags and \a timeout. - The following flags are supported (the rest is ignored): - MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your - socket is O_NONBLOCK, you should specify this flag. A \a timeout of - zero is equivalent to this flag, though. - MSG_PEEK - returns a clone of the buffer and keep the original - in the FIFO. -*/ -template static inline ssize_t -base_fifo_dequeue_buffer(FifoType *fifo, benaphore *lock, uint32 flags, - bigtime_t timeout, net_buffer **_buffer) -{ - // this function is called with `lock' held. - bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0; - status_t status; - - while (true) { - net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers); - if (buffer != NULL) { - if ((flags & MSG_PEEK) != 0) { - // we need to clone the buffer for inspection; we can't give a - // handle to a buffer that we're still using - buffer = gNetBufferModule.clone(buffer, false); - if (buffer == NULL) { - status = B_NO_MEMORY; - break; - } - } else { - list_remove_item(&fifo->buffers, buffer); - fifo->current_bytes -= buffer->size; - } - - *_buffer = buffer; - status = B_OK; - break; - } - - if (!dontWait) - fifo->waiting++; - - // we need to wait until a new buffer becomes available - benaphore_unlock(lock); - - if (dontWait) - return B_WOULD_BLOCK; - - status = acquire_sem_etc(fifo->notify, 1, - B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout); - if (status < B_OK) - return status; - - // try again - benaphore_lock(lock); - } - - if ((flags & MSG_PEEK) != 0 && fifo->waiting > 0) { - // another thread is waiting for data, since we didn't eat the - // buffer, it gets it - fifo->waiting--; - release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE); - } - - return status; -} - - template static inline status_t base_fifo_clear(FifoType *fifo) { @@ -265,11 +201,34 @@ Fifo::EnqueueAndNotify(net_buffer *_buffer, net_socket *socket, uint8 event) } -ssize_t -Fifo::Dequeue(benaphore *lock, uint32 flags, bigtime_t timeout, - net_buffer **_buffer) +status_t +Fifo::Wait(benaphore *lock, bigtime_t timeout) { - return base_fifo_dequeue_buffer(this, lock, flags, timeout, _buffer); + waiting++; + benaphore_unlock(lock); + status_t status = acquire_sem_etc(notify, 1, + B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT, timeout); + benaphore_lock(lock); + return status; +} + + +net_buffer * +Fifo::Dequeue(bool clone) +{ + net_buffer *buffer = (net_buffer *)list_get_first_item(&buffers); + + // assert(buffer != NULL); + + if (clone) { + buffer = gNetBufferModule.clone(buffer, false); + fifo_notify_one_reader(waiting, notify); + }else { + list_remove_item(&buffers, buffer); + current_bytes -= buffer->size; + } + + return buffer; } @@ -280,6 +239,13 @@ Fifo::Clear() } +void +Fifo::WakeAll() +{ + release_sem_etc(notify, 0, B_RELEASE_ALL); +} + + status_t init_fifo(net_fifo *fifo, const char *name, size_t maxBytes) { @@ -313,11 +279,68 @@ fifo_enqueue_buffer(net_fifo *fifo, net_buffer *buffer) } -ssize_t fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout, - struct net_buffer **_buffer) +/*! + Gets the first buffer from the FIFO. If there is no buffer, it + will wait depending on the \a flags and \a timeout. + The following flags are supported (the rest is ignored): + MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your + socket is O_NONBLOCK, you should specify this flag. A \a timeout of + zero is equivalent to this flag, though. + MSG_PEEK - returns a clone of the buffer and keep the original + in the FIFO. +*/ +ssize_t +fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout, + net_buffer **_buffer) { BenaphoreLocker locker(fifo->lock); - return base_fifo_dequeue_buffer(fifo, &fifo->lock, flags, timeout, _buffer); + bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0; + status_t status; + + while (true) { + net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers); + if (buffer != NULL) { + if ((flags & MSG_PEEK) != 0) { + // we need to clone the buffer for inspection; we can't give a + // handle to a buffer that we're still using + buffer = gNetBufferModule.clone(buffer, false); + if (buffer == NULL) { + status = B_NO_MEMORY; + break; + } + } else { + list_remove_item(&fifo->buffers, buffer); + fifo->current_bytes -= buffer->size; + } + + *_buffer = buffer; + status = B_OK; + break; + } + + if (!dontWait) + fifo->waiting++; + + locker.Unlock(); + + if (dontWait) + return B_WOULD_BLOCK; + + // we need to wait until a new buffer becomes available + status = acquire_sem_etc(fifo->notify, 1, + B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout); + if (status < B_OK) + return status; + + locker.Lock(); + } + + // if another thread is waiting for data, since we didn't + // eat the buffer, it will get it + if (flags & MSG_PEEK) + fifo_notify_one_reader(fifo->waiting, fifo->notify); + + return status; } diff --git a/src/add-ons/kernel/network/stack/utility.h b/src/add-ons/kernel/network/stack/utility.h index 98d4f0db4d..9daa98f7d0 100644 --- a/src/add-ons/kernel/network/stack/utility.h +++ b/src/add-ons/kernel/network/stack/utility.h @@ -36,11 +36,12 @@ public: status_t Enqueue(net_buffer *buffer); status_t EnqueueAndNotify(net_buffer *_buffer, net_socket *socket, uint8 event); - - ssize_t Dequeue(benaphore *lock, uint32 flags, bigtime_t timeout, - net_buffer **_buffer); + status_t Wait(benaphore *lock, bigtime_t timeout); + net_buffer *Dequeue(bool clone); status_t Clear(); + void WakeAll(); + bool IsEmpty() const { return current_bytes == 0; } //private: