2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
28 * 2-Feb-1999 Joe Liu (jliu) created.
32 #include <IOKit/assert.h>
33 #include <IOKit/IOWorkLoop.h>
34 #include <IOKit/network/IOOutputQueue.h>
35 #include <IOKit/network/IOBasicOutputQueue.h>
36 #include <IOKit/network/IOGatedOutputQueue.h>
37 #include <IOKit/network/IONetworkStats.h>
38 #include <IOKit/network/IONetworkController.h>
39 #include "IOMbufQueue.h"
40 #include <libkern/OSAtomic.h>
42 //===========================================================================
44 //===========================================================================
46 #define STATE_IS(bits) (_state == (bits))
47 #define STATE_HAS(bits) ((_state & (bits)) == (bits))
48 #define STATE_SET(bits) (_state |= (bits))
49 #define STATE_CLR(bits) (_state &= ~(bits))
52 #define super OSObject
53 OSDefineMetaClassAndAbstractStructors( IOOutputQueue
, OSObject
)
54 OSMetaClassDefineReservedUnused( IOOutputQueue
, 0);
55 OSMetaClassDefineReservedUnused( IOOutputQueue
, 1);
56 OSMetaClassDefineReservedUnused( IOOutputQueue
, 2);
57 OSMetaClassDefineReservedUnused( IOOutputQueue
, 3);
58 OSMetaClassDefineReservedUnused( IOOutputQueue
, 4);
59 OSMetaClassDefineReservedUnused( IOOutputQueue
, 5);
60 OSMetaClassDefineReservedUnused( IOOutputQueue
, 6);
61 OSMetaClassDefineReservedUnused( IOOutputQueue
, 7);
62 OSMetaClassDefineReservedUnused( IOOutputQueue
, 8);
63 OSMetaClassDefineReservedUnused( IOOutputQueue
, 9);
64 OSMetaClassDefineReservedUnused( IOOutputQueue
, 10);
65 OSMetaClassDefineReservedUnused( IOOutputQueue
, 11);
66 OSMetaClassDefineReservedUnused( IOOutputQueue
, 12);
67 OSMetaClassDefineReservedUnused( IOOutputQueue
, 13);
68 OSMetaClassDefineReservedUnused( IOOutputQueue
, 14);
69 OSMetaClassDefineReservedUnused( IOOutputQueue
, 15);
71 //---------------------------------------------------------------------------
72 // Initialize an IOOutputQueue object.
74 bool IOOutputQueue::init()
76 if (super::init() == false)
79 // Allocate and initialize the callout entry for async service.
81 _callEntry
= thread_call_allocate((thread_call_func_t
) &runServiceThread
,
82 (void *) this); /* param0 */
89 //---------------------------------------------------------------------------
90 // Frees the IOOutputQueue object.
92 void IOOutputQueue::free()
96 cancelServiceThread();
97 thread_call_free(_callEntry
);
104 //---------------------------------------------------------------------------
105 // Schedule a service thread callout, which will run the
106 // serviceThread() method.
108 bool IOOutputQueue::scheduleServiceThread(void * param
= 0)
110 return thread_call_enter1(_callEntry
, (thread_call_param_t
) param
);
113 //---------------------------------------------------------------------------
114 // Cancel any pending service thread callout.
116 bool IOOutputQueue::cancelServiceThread()
121 return thread_call_cancel(_callEntry
);
124 //---------------------------------------------------------------------------
125 // A 'C' glue function that is registered as the service thread callout
126 // handler. This function in turn will call the serviceThread() method.
129 IOOutputQueue::runServiceThread(thread_call_param_t param0
, /* this */
130 thread_call_param_t param1
) /* param */
133 ((IOOutputQueue
*) param0
)->serviceThread(param1
);
136 //---------------------------------------------------------------------------
137 // Must be implemented by a subclass that calls scheduleServiceThread().
138 // The default implementation is a placeholder and performs no action.
140 void IOOutputQueue::serviceThread(void * param
)
144 //---------------------------------------------------------------------------
145 // Return an address of a method that is designated to handle
146 // packets sent to the queue object.
148 IOOutputAction
IOOutputQueue::getOutputHandler() const
150 return (IOOutputAction
) &IOOutputQueue::enqueue
;
153 //---------------------------------------------------------------------------
154 // Return an IONetworkData object containing statistics counters.
156 IONetworkData
* IOOutputQueue::getStatisticsData() const
162 //===========================================================================
163 // IOBasicOutputQueue
164 //===========================================================================
167 #define super IOOutputQueue
168 OSDefineMetaClassAndStructors( IOBasicOutputQueue
, IOOutputQueue
)
170 #define QUEUE_LOCK IOSimpleLockLock(_spinlock)
171 #define QUEUE_UNLOCK IOSimpleLockUnlock(_spinlock)
173 #define kIOOutputQueueSignature ((void *) 0xfacefeed)
175 //---------------------------------------------------------------------------
176 // 'C' function glue to dispatch the IONetworkData notification.
179 IOBasicOutputQueue::dispatchNetworkDataNotification(void * target
,
181 IONetworkData
* data
,
184 IOBasicOutputQueue
* self
= (IOBasicOutputQueue
*) target
;
185 return self
->handleNetworkDataAccess(data
, type
, param
);
188 //---------------------------------------------------------------------------
189 // Initialize an IOBasicOutputQueue object.
191 bool IOBasicOutputQueue::init(OSObject
* target
,
192 IOOutputAction action
,
195 if (super::init() == false)
198 if ((target
== 0) || (action
== 0))
204 // Create a data object for queue statistics.
206 _statsData
= IONetworkData::withInternalBuffer(
207 kIOOutputQueueStatsKey
,
208 sizeof(IOOutputQueueStats
),
209 kIONetworkDataBasicAccessTypes
,
211 (IONetworkData::Action
)
212 &IOBasicOutputQueue::dispatchNetworkDataNotification
,
213 kIOOutputQueueSignature
);
218 _stats
= (IOOutputQueueStats
*) _statsData
->getBuffer();
221 _stats
->capacity
= capacity
;
223 // Create two queue objects.
225 _queues
[0] = IONew(IOMbufQueue
, 1);
226 _queues
[1] = IONew(IOMbufQueue
, 1);
228 IOMbufQueueInit(_queues
[0], capacity
);
229 IOMbufQueueInit(_queues
[1], capacity
);
231 if ( (_queues
[0] == 0) || (_queues
[1] == 0) )
234 _inQueue
= _queues
[0];
236 // Create a spinlock to protect the queue.
238 _spinlock
= IOSimpleLockAlloc();
245 //---------------------------------------------------------------------------
246 // Factory methods that will construct and initialize an IOBasicOutputQueue
250 IOBasicOutputQueue::withTarget(IONetworkController
* target
,
253 IOBasicOutputQueue
* queue
= new IOBasicOutputQueue
;
255 if (queue
&& !queue
->init(target
, target
->getOutputHandler(), capacity
))
264 IOBasicOutputQueue::withTarget(OSObject
* target
,
265 IOOutputAction action
,
268 IOBasicOutputQueue
* queue
= new IOBasicOutputQueue
;
270 if (queue
&& !queue
->init(target
, action
, capacity
))
278 //---------------------------------------------------------------------------
279 // Release all resources previously allocated before calling super::free().
281 void IOBasicOutputQueue::free()
283 cancelServiceThread();
288 IOSimpleLockFree(_spinlock
);
292 if (_queues
[0]) IODelete(_queues
[0], IOMbufQueue
, 1);
293 if (_queues
[1]) IODelete(_queues
[1], IOMbufQueue
, 1);
294 _queues
[0] = _queues
[1] = 0;
298 _statsData
->release();
305 //---------------------------------------------------------------------------
306 // Provide an implementation for the serviceThread() method defined in
307 // IOOutputQueue. This method is called by a callout thread after an
308 // asynchronous service was scheduled.
310 void IOBasicOutputQueue::serviceThread(void * param
)
313 STATE_CLR((UInt32
) param
);
314 STATE_SET(kStateOutputActive
);
319 //---------------------------------------------------------------------------
320 // Add a single packet, or a chain of packets, to the queue object.
321 // This method can support multiple clients threads.
323 UInt32
IOBasicOutputQueue::enqueue(struct mbuf
* m
, void * param
)
329 success
= IOMbufQueueEnqueue(_inQueue
, m
);
331 if ( STATE_IS( kStateRunning
) )
333 STATE_SET( kStateOutputActive
);
339 // Drop the packet if the packet(s) were not queued.
340 // But avoid calling m_free() while holding a simple lock.
341 // This will not be necessary in the future when m_free()
342 // is no longer funneled.
344 if (success
== false)
346 OSAddAtomic( IOMbufFree(m
),
347 (SInt32
*) &_stats
->dropCount
);
353 //---------------------------------------------------------------------------
354 // Responsible for removing all packets from the queue and pass each packet
355 // removed to our target. This method returns when the queue becomes empty
356 // or if the queue is stalled by the target. This method is called with the
359 void IOBasicOutputQueue::dequeue()
361 IOMbufQueue
* outQueue
= _queues
[0];
363 UInt32 myServiceCount
;
365 // Switch the input queue. Work on the real queue, while allowing
366 // clients to continue to queue packets to the "shadow" queue.
368 _inQueue
= _queues
[1];
370 // While dequeue is allowed, and incoming queue has packets.
372 while ( STATE_IS( kStateRunning
| kStateOutputActive
) &&
373 IOMbufQueueGetSize(outQueue
) )
375 myServiceCount
= _serviceCount
;
379 output( outQueue
, &newState
);
383 // If service() was called during the interval when the
384 // spinlock was released, then refuse to honor any
389 if ( myServiceCount
!= _serviceCount
)
390 newState
&= ~kStateOutputStalled
;
392 STATE_SET( newState
);
395 // Absorb new packets added to the shadow queue.
397 IOMbufQueueEnqueue( outQueue
, _inQueue
);
400 _inQueue
= _queues
[0];
402 STATE_CLR( kStateOutputActive
);
404 if ( newState
& kStateOutputServiceMask
)
406 scheduleServiceThread((void *)(newState
& kStateOutputServiceMask
));
409 if (_waitDequeueDone
)
411 // A stop() request is waiting for the transmit thread to
412 // complete transmission. Wake up the waiting thread.
414 _waitDequeueDone
= false;
415 thread_wakeup((void *) &_waitDequeueDone
);
419 //---------------------------------------------------------------------------
420 // Transfer all packets from the given queue to the target. Continue until
421 // the queue becomes empty, or if the target throttle the queue.
423 void IOBasicOutputQueue::output(IOMbufQueue
* queue
, UInt32
* state
)
429 pkt
= IOMbufQueueDequeue(queue
);
432 // Handoff each packet to the controller driver.
434 status
= (_target
->*_action
)( pkt
, 0 );
436 if ( status
== ( kIOOutputStatusAccepted
| kIOOutputCommandNone
) )
438 // Fast-path the typical code path.
439 _stats
->outputCount
++;
443 // Look at the return status and update statistics counters.
445 switch (status
& kIOOutputStatusMask
)
448 case kIOOutputStatusAccepted
:
449 _stats
->outputCount
++;
452 case kIOOutputStatusRetry
:
453 IOMbufQueuePrepend(queue
, pkt
);
454 _stats
->retryCount
++;
458 // Handle the requested action.
460 switch (status
& kIOOutputCommandMask
)
462 case kIOOutputCommandStall
:
463 *state
= kStateOutputStalled
;
464 _stats
->stallCount
++;
472 while ( IOMbufQueueGetSize(queue
) && (*state
== 0) );
475 //---------------------------------------------------------------------------
476 // Start or enable the queue.
478 bool IOBasicOutputQueue::start()
482 STATE_SET( kStateRunning
);
483 STATE_CLR( kStateOutputStalled
);
486 if ( STATE_IS( kStateRunning
) )
488 STATE_SET( kStateOutputActive
);
494 return true; /* always return true */
497 //---------------------------------------------------------------------------
498 // Stop or disable the queue.
500 bool IOBasicOutputQueue::stop()
506 wasRunning
= STATE_HAS( kStateRunning
);
508 STATE_CLR( kStateRunning
);
510 if ( STATE_HAS( kStateOutputActive
) )
512 // If dequeue is active, it means that:
513 // 1. A thread is about to call dequeue().
514 // 2. A thread is in dequeue() and calling the target.
516 // Wait for the dequeue thread to complete processing.
518 _waitDequeueDone
= true;
520 assert_wait((void *) &_waitDequeueDone
, false);
525 thread_block((void (*)(void)) 0);
530 //---------------------------------------------------------------------------
531 // If the queue becomes stalled, then service() must be called by the target
532 // to restart the queue when the target is ready to accept more packets.
534 bool IOBasicOutputQueue::service(UInt32 options
= 0)
536 bool doDequeue
= false;
537 bool async
= (options
& kServiceAsync
);
544 // Clear the stall condition.
546 STATE_CLR( kStateOutputStalled
);
549 if ( ( oldState
& kStateOutputStalled
) &&
550 STATE_IS( kStateRunning
) &&
551 IOMbufQueueGetSize(_queues
[0]) )
554 STATE_SET( kStateOutputActive
);
555 if (async
== false) dequeue();
560 if ( doDequeue
&& async
)
562 scheduleServiceThread();
568 //---------------------------------------------------------------------------
569 // Release all packets held by the queue.
571 UInt32
IOBasicOutputQueue::flush()
576 flushCount
= IOMbufFree( IOMbufQueueDequeueAll( _inQueue
) );
577 OSAddAtomic(flushCount
, (SInt32
*) &_stats
->dropCount
);
583 //---------------------------------------------------------------------------
584 // Change the capacity of the queue.
586 bool IOBasicOutputQueue::setCapacity(UInt32 capacity
)
589 IOMbufQueueSetCapacity(_queues
[1], capacity
);
590 IOMbufQueueSetCapacity(_queues
[0], capacity
);
591 _stats
->capacity
= capacity
;
596 //---------------------------------------------------------------------------
597 // Returns the current queue capacity.
599 UInt32
IOBasicOutputQueue::getCapacity() const
601 return _stats
->capacity
;
604 //---------------------------------------------------------------------------
605 // Returns the current queue size.
607 UInt32
IOBasicOutputQueue::getSize() const
611 size
= IOMbufQueueGetSize(_queues
[0]) + IOMbufQueueGetSize(_queues
[1]);
616 //---------------------------------------------------------------------------
617 // Returns the number of packets dropped by the queue due to over-capacity.
619 UInt32
IOBasicOutputQueue::getDropCount()
621 return _stats
->dropCount
;
624 //---------------------------------------------------------------------------
625 // Returns the number of packet passed to the target.
627 UInt32
IOBasicOutputQueue::getOutputCount()
629 return _stats
->outputCount
;
632 //---------------------------------------------------------------------------
633 // Returns the number of times that a kIOOutputStatusRetry status code
634 // is received from the target.
636 UInt32
IOBasicOutputQueue::getRetryCount()
638 return _stats
->retryCount
;
641 //---------------------------------------------------------------------------
642 // Returns the number of times that a kIOOutputCommandStall action code
643 // is received from the target.
645 UInt32
IOBasicOutputQueue::getStallCount()
647 return _stats
->stallCount
;
650 //---------------------------------------------------------------------------
651 // Returns the current state of the queue object.
653 UInt32
IOBasicOutputQueue::getState() const
658 //---------------------------------------------------------------------------
659 // This method is called by our IONetworkData object when it receives
660 // a read or a reset request. We need to be notified to intervene in
661 // the request handling.
664 IOBasicOutputQueue::handleNetworkDataAccess(IONetworkData
* data
,
668 IOReturn ret
= kIOReturnSuccess
;
670 assert(data
&& (arg
== kIOOutputQueueSignature
));
672 // Check the type of data request.
676 case kIONetworkDataAccessTypeRead
:
677 case kIONetworkDataAccessTypeSerialize
:
678 _stats
->size
= getSize();
682 ret
= kIOReturnNotWritable
;
689 //---------------------------------------------------------------------------
690 // Return an IONetworkData object containing an IOOutputQueueStats structure.
692 IONetworkData
* IOBasicOutputQueue::getStatisticsData() const
697 //===========================================================================
698 // IOGatedOutputQueue
699 //===========================================================================
702 #define super IOBasicOutputQueue
703 OSDefineMetaClassAndStructors( IOGatedOutputQueue
, IOBasicOutputQueue
)
705 //---------------------------------------------------------------------------
706 // Initialize an IOGatedOutputQueue object.
708 bool IOGatedOutputQueue::init(OSObject
* target
,
709 IOOutputAction action
,
710 IOWorkLoop
* workloop
,
713 if (super::init(target
, action
, capacity
) == false)
716 // Verify that the IOWorkLoop provided is valid.
718 if (OSDynamicCast(IOWorkLoop
, workloop
) == 0)
721 // Allocate and attach an IOCommandGate object to the workloop.
723 _gate
= IOCommandGate::commandGate(this);
725 if (!_gate
|| (workloop
->addEventSource(_gate
) != kIOReturnSuccess
))
728 // Allocate and attach an IOInterruptEventSource object to the workloop.
730 _interruptSrc
= IOInterruptEventSource::interruptEventSource(
732 (IOInterruptEventSource::Action
) restartDeferredOutput
735 if ( !_interruptSrc
||
736 (workloop
->addEventSource(_interruptSrc
) != kIOReturnSuccess
) )
742 //---------------------------------------------------------------------------
743 // Factory methods that will construct and initialize an IOGatedOutputQueue
747 IOGatedOutputQueue::withTarget(IONetworkController
* target
,
748 IOWorkLoop
* workloop
,
751 IOGatedOutputQueue
* queue
= new IOGatedOutputQueue
;
753 if (queue
&& !queue
->init(target
, target
->getOutputHandler(), workloop
,
763 IOGatedOutputQueue::withTarget(OSObject
* target
,
764 IOOutputAction action
,
765 IOWorkLoop
* workloop
,
768 IOGatedOutputQueue
* queue
= new IOGatedOutputQueue
;
770 if (queue
&& !queue
->init(target
, action
, workloop
, capacity
))
778 //---------------------------------------------------------------------------
779 // Free the IOGatedOutputQueue object.
781 void IOGatedOutputQueue::free()
783 cancelServiceThread();
793 IOWorkLoop
* wl
= _interruptSrc
->getWorkLoop();
794 if (wl
) wl
->removeEventSource(_interruptSrc
);
795 _interruptSrc
->release();
802 //---------------------------------------------------------------------------
803 // Called by an IOCommandGate object.
805 void IOGatedOutputQueue::gatedOutput(OSObject
* /* owner */,
806 IOGatedOutputQueue
* self
,
814 pkt
= IOMbufQueueDequeue(queue
);
817 // Handoff the packet to the controller driver.
819 status
= ((self
->_target
)->*(self
->_action
))( pkt
, 0 );
821 if ( status
== ( kIOOutputStatusAccepted
| kIOOutputCommandNone
) )
823 // Fast-path the typical code path.
824 self
->_stats
->outputCount
++;
828 // Look at the return status and update statistics counters.
830 switch (status
& kIOOutputStatusMask
)
833 case kIOOutputStatusAccepted
:
834 self
->_stats
->outputCount
++;
837 case kIOOutputStatusRetry
:
838 IOMbufQueuePrepend(queue
, pkt
);
839 self
->_stats
->retryCount
++;
843 // Handle the requested action.
845 switch (status
& kIOOutputCommandMask
)
847 case kIOOutputCommandStall
:
848 *state
= kStateOutputStalled
;
849 self
->_stats
->stallCount
++;
857 while ( IOMbufQueueGetSize(queue
) && (*state
== 0) );
860 //---------------------------------------------------------------------------
861 // Called by our superclass to output all packets in the packet queue given.
864 kStateOutputDeferred
= 0x100
867 void IOGatedOutputQueue::output(IOMbufQueue
* queue
, UInt32
* state
)
869 if ( _gate
->attemptAction((IOCommandGate::Action
)
870 &IOGatedOutputQueue::gatedOutput
,
873 (void *) state
) == kIOReturnCannotLock
)
875 *state
= kStateOutputDeferred
;
879 bool IOGatedOutputQueue::scheduleServiceThread(void * param
)
881 if ( ((UInt32
) param
) & kStateOutputDeferred
)
883 _interruptSrc
->interruptOccurred(0, 0, 0);
888 return super::scheduleServiceThread(param
);
892 void IOGatedOutputQueue::restartDeferredOutput(
894 IOInterruptEventSource
* sender
,
897 IOGatedOutputQueue
* self
= (IOGatedOutputQueue
*) owner
;
898 self
->serviceThread((void *) kStateOutputDeferred
);