packet capture sockets are now properly notified when the device is going down.

- modified LinkProtocol::ReadData() a bit so it can safely react to external changes such as the device being monitored going down.
 - fixed an issue in invalidate_routes() where in some cases the default route was being kept.
 - fixed another issue related with the fifo implementation, the notifier semaphore was being created with count of 1, instead of 0.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20622 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-09 00:09:00 +00:00
parent 6473469039
commit 9261b12988
5 changed files with 152 additions and 109 deletions

View File

@ -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.

View File

@ -127,19 +127,27 @@ LinkProtocol::ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer)
{
BenaphoreLocker _(fLock);
if (fMonitoredDevice == NULL) {
if (fFifo.current_bytes == 0)
return ENODEV;
}
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);
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);
}
}
}

View File

@ -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);
}
}

View File

@ -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<typename FifoType> 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<typename FifoType> 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<typename FifoType> 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;
}

View File

@ -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: