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:
parent
6473469039
commit
9261b12988
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user