1/*
2 * Copyright (c) 1998-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * IOOutputQueue.cpp
24 *
25 * HISTORY
26 * 2-Feb-1999       Joe Liu (jliu) created.
27 *
28 */
29
30#include <IOKit/assert.h>
31#include <IOKit/IOWorkLoop.h>
32#include <IOKit/network/IOOutputQueue.h>
33#include <IOKit/network/IOBasicOutputQueue.h>
34#include <IOKit/network/IOGatedOutputQueue.h>
35#include <IOKit/network/IONetworkStats.h>
36#include <IOKit/network/IONetworkController.h>
37#include "IOMbufQueue.h"
38#include <libkern/OSAtomic.h>
39
40//===========================================================================
41// IOOutputQueue
42//===========================================================================
43
44#define STATE_IS(bits)     (_state == (bits))
45#define STATE_HAS(bits)    ((_state & (bits)) == (bits))
46#define STATE_SET(bits)    (_state |= (bits))
47#define STATE_CLR(bits)    (_state &= ~(bits))
48
49#undef  super
50#define super OSObject
51OSDefineMetaClassAndAbstractStructors( IOOutputQueue, OSObject )
52OSMetaClassDefineReservedUnused( IOOutputQueue,  1);
53OSMetaClassDefineReservedUnused( IOOutputQueue,  2);
54OSMetaClassDefineReservedUnused( IOOutputQueue,  3);
55OSMetaClassDefineReservedUnused( IOOutputQueue,  4);
56OSMetaClassDefineReservedUnused( IOOutputQueue,  5);
57OSMetaClassDefineReservedUnused( IOOutputQueue,  6);
58OSMetaClassDefineReservedUnused( IOOutputQueue,  7);
59OSMetaClassDefineReservedUnused( IOOutputQueue,  8);
60OSMetaClassDefineReservedUnused( IOOutputQueue,  9);
61OSMetaClassDefineReservedUnused( IOOutputQueue, 10);
62OSMetaClassDefineReservedUnused( IOOutputQueue, 11);
63OSMetaClassDefineReservedUnused( IOOutputQueue, 12);
64OSMetaClassDefineReservedUnused( IOOutputQueue, 13);
65OSMetaClassDefineReservedUnused( IOOutputQueue, 14);
66OSMetaClassDefineReservedUnused( IOOutputQueue, 15);
67
68//---------------------------------------------------------------------------
69// Initialize an IOOutputQueue object.
70
71bool IOOutputQueue::init()
72{
73    if (super::init() == false)
74        return false;
75
76    // Allocate and initialize the callout entry for async service.
77
78    _callEntry = thread_call_allocate((thread_call_func_t) &runServiceThread,
79                                      (void *) this); /* param0 */
80    if (_callEntry == 0)
81        return false;
82
83    return true;
84}
85
86//---------------------------------------------------------------------------
87// Frees the IOOutputQueue object.
88
89void IOOutputQueue::free()
90{
91    if (_callEntry)
92    {
93        cancelServiceThread();
94        thread_call_free(_callEntry);
95        _callEntry = 0;
96    }
97
98    super::free();
99}
100
101//---------------------------------------------------------------------------
102// Schedule a service thread callout, which will run the
103// serviceThread() method.
104
105bool IOOutputQueue::scheduleServiceThread(void * param)
106{
107    return thread_call_enter1(_callEntry, (thread_call_param_t) param);
108}
109
110//---------------------------------------------------------------------------
111// Cancel any pending service thread callout.
112
113bool IOOutputQueue::cancelServiceThread()
114{
115    if (_callEntry == 0)
116        return false;
117    else
118        return thread_call_cancel(_callEntry);
119}
120
121//---------------------------------------------------------------------------
122// A 'C' glue function that is registered as the service thread callout
123// handler. This function in turn will call the serviceThread() method.
124
125void
126IOOutputQueue::runServiceThread(thread_call_param_t param0,  /* this */
127                                thread_call_param_t param1)  /* param */
128{
129    assert(param0);
130    ((IOOutputQueue *) param0)->serviceThread(param1);
131}
132
133//---------------------------------------------------------------------------
134// Must be implemented by a subclass that calls scheduleServiceThread().
135// The default implementation is a placeholder and performs no action.
136
137void IOOutputQueue::serviceThread(void * param)
138{
139}
140
141//---------------------------------------------------------------------------
142// Return an address of a method that is designated to handle
143// packets sent to the queue object.
144
145IOOutputAction IOOutputQueue::getOutputHandler() const
146{
147    return (IOOutputAction) &IOOutputQueue::enqueue;
148}
149
150//---------------------------------------------------------------------------
151// Return an IONetworkData object containing statistics counters.
152
153IONetworkData * IOOutputQueue::getStatisticsData() const
154{
155    return 0;
156}
157
158OSMetaClassDefineReservedUsed( IOOutputQueue,  0);
159//---------------------------------------------------------------------------
160// Retrieve a packet's priority, to be overridden by subclasses
161
162UInt32 IOOutputQueue::getMbufPriority(mbuf_t m)
163{
164    return 0;
165}
166
167//===========================================================================
168// IOBasicOutputQueue
169//===========================================================================
170
171#undef  super
172#define super IOOutputQueue
173OSDefineMetaClassAndStructors( IOBasicOutputQueue, IOOutputQueue )
174
175#define QUEUE_LOCK      IOLockLock(_queueLock)
176#define QUEUE_UNLOCK    IOLockUnlock(_queueLock)
177
178#define kIOOutputQueueSignature      ((void *) 0xfacefeed)
179
180//---------------------------------------------------------------------------
181// 'C' function glue to dispatch the IONetworkData notification.
182
183IOReturn
184IOBasicOutputQueue::dispatchNetworkDataNotification(void *          target,
185                                                    void *          param,
186                                                    IONetworkData * data,
187                                                    UInt32          type)
188{
189    IOBasicOutputQueue * self = (IOBasicOutputQueue *) target;
190    return self->handleNetworkDataAccess(data, type, param);
191}
192
193//---------------------------------------------------------------------------
194// Initialize an IOBasicOutputQueue object.
195
196bool IOBasicOutputQueue::init(OSObject *     target,
197                              IOOutputAction action,
198                              UInt32         capacity,
199                              UInt32         priorities)
200{
201    if (super::init() == false)
202        return false;
203
204    if ((target == 0) || (action == 0) || (priorities == 0) || (priorities > 256))
205        return false;
206
207    _target = target;
208    _action = action;
209
210    // Create a data object for queue statistics.
211
212    _statsData = IONetworkData::withInternalBuffer(
213                   kIOOutputQueueStatsKey,
214                   sizeof(IOOutputQueueStats),
215                   kIONetworkDataBasicAccessTypes,
216                   this,
217                   (IONetworkData::Action)
218                       &IOBasicOutputQueue::dispatchNetworkDataNotification,
219                   kIOOutputQueueSignature);
220
221    if (_statsData == 0)
222        return false;
223
224    _stats = (IOOutputQueueStats *) _statsData->getBuffer();
225    assert(_stats);
226
227    _stats->capacity = capacity;
228
229    // Create queue objects
230    _priorities = priorities;
231    _primaryQueues = IONew(IOMbufQueue, priorities);
232    _shadowQueues = IONew(IOMbufQueue, priorities);
233
234    if ( (_primaryQueues == 0) || (_shadowQueues == 0) )
235        return false;
236
237    // Initialize queues
238    for(UInt32 i = 0; i < priorities; i++)
239    {
240        IOMbufQueueInit(&(_primaryQueues[i]), capacity);
241        IOMbufQueueInit(&(_shadowQueues[i]), capacity);
242    }
243
244    _inQueues = _primaryQueues;
245
246    // Create a lock to protect the queue.
247
248    _queueLock = IOLockAlloc();
249    if (_queueLock == 0)
250        return false;
251
252    return true;
253}
254
255//---------------------------------------------------------------------------
256// Factory methods that will construct and initialize an IOBasicOutputQueue
257// object.
258
259IOBasicOutputQueue *
260IOBasicOutputQueue::withTarget(IONetworkController * target,
261                               UInt32                capacity)
262{
263    return IOBasicOutputQueue::withTarget(target, capacity, 1 /* priorities */);
264}
265
266IOBasicOutputQueue *
267IOBasicOutputQueue::withTarget(IONetworkController * target,
268                               UInt32                capacity,
269                               UInt32                priorities)
270{
271    IOBasicOutputQueue * queue = new IOBasicOutputQueue;
272
273    if (queue && !queue->init(target, target->getOutputHandler(), capacity, priorities))
274    {
275        queue->release();
276        queue = 0;
277    }
278    return queue;
279}
280
281IOBasicOutputQueue *
282IOBasicOutputQueue::withTarget(OSObject *     target,
283                               IOOutputAction action,
284                               UInt32         capacity)
285{
286    return IOBasicOutputQueue::withTarget(target, action, capacity, 1 /* priorities */);
287}
288
289IOBasicOutputQueue *
290IOBasicOutputQueue::withTarget(OSObject *     target,
291                               IOOutputAction action,
292                               UInt32         capacity,
293                               UInt32         priorities)
294{
295    IOBasicOutputQueue * queue = new IOBasicOutputQueue;
296
297    if (queue && !queue->init(target, action, capacity, priorities))
298    {
299        queue->release();
300        queue = 0;
301    }
302    return queue;
303}
304
305//---------------------------------------------------------------------------
306// Release all resources previously allocated before calling super::free().
307
308void IOBasicOutputQueue::free()
309{
310    cancelServiceThread();
311
312    if (_queueLock)
313    {
314        flush();
315        IOLockFree(_queueLock);
316        _queueLock = 0;
317    }
318
319    if(_primaryQueues) IODelete(_primaryQueues, IOMbufQueue, _priorities);
320    if(_shadowQueues) IODelete(_shadowQueues, IOMbufQueue, _priorities);
321    _primaryQueues = _shadowQueues = 0;
322
323    if (_statsData)
324    {
325        _statsData->release();
326        _statsData = 0;
327    }
328
329    super::free();
330}
331
332//---------------------------------------------------------------------------
333// Provide an implementation for the serviceThread() method defined in
334// IOOutputQueue. This method is called by a callout thread after an
335// asynchronous service was scheduled.
336
337void IOBasicOutputQueue::serviceThread(void * param)
338{
339    QUEUE_LOCK;
340    STATE_CLR((uintptr_t) param);
341    STATE_SET(kStateOutputActive);
342    dequeue();
343    QUEUE_UNLOCK;
344}
345
346//---------------------------------------------------------------------------
347// Add a single packet, or a chain of packets, to the queue object.
348// This method can support multiple clients threads.
349
350UInt32 IOBasicOutputQueue::enqueue(mbuf_t m, void * param)
351{
352    bool success;
353
354    UInt32 priority = getMbufPriority(m);
355
356    // Set out-of-bounds priority to lowest
357    if ( priority >= _priorities )
358    {
359        priority = _priorities - 1;
360    }
361
362    QUEUE_LOCK;
363
364	success = IOMbufQueueEnqueue(&(_inQueues[priority]), m);
365
366    if ( STATE_IS( kStateRunning ) )
367    {
368        STATE_SET( kStateOutputActive );
369        dequeue();
370    }
371
372    QUEUE_UNLOCK;
373
374    // Drop the packet if the packet(s) were not queued.
375    // But avoid calling m_free() while holding a simple lock.
376    // This will not be necessary in the future when m_free()
377    // is no longer funneled.
378
379    if (success == false)
380    {
381        OSAddAtomic( IOMbufFree(m),
382                     (SInt32 *) &_stats->dropCount );
383    }
384
385    return 0;
386}
387
388//---------------------------------------------------------------------------
389// Responsible for removing all packets from the queue and pass each packet
390// removed to our target. This method returns when the queue becomes empty
391// or if the queue is stalled by the target. This method is called with the
392// queue lock held.
393
394void IOBasicOutputQueue::dequeue()
395{
396    IOMbufQueue * outQueues = _primaryQueues;
397    UInt32        newState = 0;
398    UInt32        myServiceCount;
399
400    // Switch the input queue. Work on the real queue, while allowing
401    // clients to continue to queue packets to the "shadow" queue.
402
403    _inQueues = _shadowQueues;
404
405    // While dequeue is allowed, and incoming queue has packets.
406
407    UInt32 priority = 0;
408    while ( STATE_IS( kStateRunning | kStateOutputActive ) &&
409            priority < _priorities )
410    {
411        if (IOMbufQueueGetSize(&(outQueues[priority])) > 0)
412        {
413            myServiceCount = _serviceCount;
414
415            QUEUE_UNLOCK;
416
417            output( &(outQueues[priority]), &newState );
418
419            QUEUE_LOCK;
420
421            // If driver called service() while the queue lock was released,
422            // refuse to honor any stall requests and re-attempt transmission.
423
424            if ( newState )
425            {
426                if ( myServiceCount != _serviceCount )
427                    newState &= ~kStateOutputStalled;
428
429                STATE_SET( newState );
430            }
431
432            // Absorb new packets added to the shadow queues.
433            // Must empty all shadow queues since the loop might exit
434            // before servicing all priority levels due to driver stall.
435
436            int newPriority = -1;
437            for (UInt32 i = 0; i < _priorities; i++)
438            {
439                IOMbufQueueEnqueue( &(outQueues[i]), &(_inQueues[i]));
440
441                if ((newPriority < 0) && (i <= priority) &&
442                    (IOMbufQueueGetSize(&(outQueues[i])) > 0))
443                {
444                    newPriority = i;
445                }
446            }
447
448            // Service higher or equal priority mbufs if they arrived while
449            // the queue lock was dropped.
450
451            if (newPriority >= 0)
452            {
453                priority = newPriority;
454                continue;
455            }
456        }
457
458        priority++;
459    }
460
461    _inQueues = _primaryQueues;
462
463    STATE_CLR( kStateOutputActive );
464
465    if ( newState & kStateOutputServiceMask )
466    {
467        scheduleServiceThread(
468            (void *)(uintptr_t) (newState & kStateOutputServiceMask));
469    }
470
471    if (_waitDequeueDone)
472    {
473        // A stop() request is waiting for the transmit thread to
474        // complete transmission. Wake up the waiting thread.
475
476        _waitDequeueDone = false;
477        thread_wakeup((void *) &_waitDequeueDone);
478    }
479}
480
481//---------------------------------------------------------------------------
482// Transfer all packets from the given queue to the target. Continue until
483// the queue becomes empty, or if the target throttle the queue.
484
485void IOBasicOutputQueue::output(IOMbufQueue * queue, UInt32 * state)
486{
487    mbuf_t pkt;
488    UInt32        status;
489
490    do {
491        pkt = IOMbufQueueDequeue(queue);
492        assert(pkt);
493
494        // Handoff each packet to the controller driver.
495
496        status = (_target->*_action)( pkt, 0 );
497
498        if ( status == ( kIOOutputStatusAccepted | kIOOutputCommandNone ) )
499        {
500            // Fast-path the typical code path.
501            _stats->outputCount++;
502        }
503        else
504        {
505            // Look at the return status and update statistics counters.
506
507            switch (status & kIOOutputStatusMask)
508            {
509                default:
510                case kIOOutputStatusAccepted:
511                    _stats->outputCount++;
512                    break;
513
514                case kIOOutputStatusRetry:
515                    IOMbufQueuePrepend(queue, pkt);
516                    _stats->retryCount++;
517                    break;
518            }
519
520            // Handle the requested action.
521
522            switch (status & kIOOutputCommandMask)
523            {
524                case kIOOutputCommandStall:
525                    *state = kStateOutputStalled;
526                    _stats->stallCount++;
527                    break;
528
529                default:
530                    break;
531            }
532        }
533    }
534	while ( IOMbufQueueGetSize(queue) && (*state == 0) );
535}
536
537//---------------------------------------------------------------------------
538// Start or enable the queue.
539
540bool IOBasicOutputQueue::start()
541{
542    QUEUE_LOCK;
543
544    STATE_SET( kStateRunning );
545    STATE_CLR( kStateOutputStalled );
546    _serviceCount++;
547
548    if ( STATE_IS( kStateRunning ) )
549    {
550        STATE_SET( kStateOutputActive );
551        dequeue();
552    }
553
554    QUEUE_UNLOCK;
555
556    return true;   /* always return true */
557}
558
559//---------------------------------------------------------------------------
560// Stop or disable the queue.
561
562bool IOBasicOutputQueue::stop()
563{
564    bool wasRunning;
565
566    QUEUE_LOCK;
567
568    wasRunning = STATE_HAS( kStateRunning );
569
570    STATE_CLR( kStateRunning );
571
572    if ( STATE_HAS( kStateOutputActive ) )
573    {
574        // If dequeue is active, it means that:
575        //   1. A thread is about to call dequeue().
576        //   2. A thread is in dequeue() and calling the target.
577        //
578        // Wait for the dequeue thread to complete processing.
579
580        _waitDequeueDone = true;
581
582        assert_wait((void *) &_waitDequeueDone, false);
583    }
584
585    QUEUE_UNLOCK;
586
587    thread_block((void (*)(void*, int)) 0);
588
589    return wasRunning;
590}
591
592//---------------------------------------------------------------------------
593// If the queue becomes stalled, then service() must be called by the target
594// to restart the queue when the target is ready to accept more packets.
595
596bool IOBasicOutputQueue::service(IOOptionBits options)
597{
598    bool    doDequeue = false;
599    bool    async     = (options & kServiceAsync);
600    UInt32  oldState;
601
602    QUEUE_LOCK;
603
604    oldState = _state;
605
606    // Clear the stall condition.
607
608    STATE_CLR( kStateOutputStalled );
609    _serviceCount++;
610
611    bool workToDo = false;
612    for(UInt32 i = 0; i < _priorities; i++)
613    {
614        if(IOMbufQueueGetSize(&(_primaryQueues[i])) > 0)
615        {
616            workToDo = true;
617            break;
618        }
619    }
620
621    if ( ( oldState & kStateOutputStalled ) &&
622         STATE_IS( kStateRunning )          &&
623         workToDo )
624    {
625        doDequeue = true;
626        STATE_SET( kStateOutputActive );
627        if (async == false) dequeue();
628    }
629
630    QUEUE_UNLOCK;
631
632    if ( doDequeue && async )
633    {
634        scheduleServiceThread();
635    }
636
637    return doDequeue;
638}
639
640//---------------------------------------------------------------------------
641// Release all packets held by the queue.
642
643UInt32 IOBasicOutputQueue::flush()
644{
645    UInt32 flushCount;
646	mbuf_t m;
647    for(UInt32 i = 0; i < _priorities; i++)
648    {
649        QUEUE_LOCK;
650        m = IOMbufQueueDequeueAll( &(_inQueues[i]) );
651        QUEUE_UNLOCK;
652        flushCount = IOMbufFree(m);
653        OSAddAtomic(flushCount, (SInt32 *) &_stats->dropCount);
654    }
655    return flushCount;
656}
657
658//---------------------------------------------------------------------------
659// Change the capacity of the queue.
660
661bool IOBasicOutputQueue::setCapacity(UInt32 capacity)
662{
663    QUEUE_LOCK;
664    for(UInt32 i = 0; i < _priorities; i++)
665    {
666        IOMbufQueueSetCapacity(&(_primaryQueues[i]), capacity);
667        IOMbufQueueSetCapacity(&(_shadowQueues[i]), capacity);
668    }
669    _stats->capacity = capacity * _priorities;
670    QUEUE_UNLOCK;
671    return true;
672}
673
674//---------------------------------------------------------------------------
675// Returns the current queue capacity.
676
677UInt32 IOBasicOutputQueue::getCapacity() const
678{
679    return _stats->capacity;
680}
681
682//---------------------------------------------------------------------------
683// Returns the current queue size.
684
685UInt32 IOBasicOutputQueue::getSize() const
686{
687    UInt32 total = 0;
688    for(UInt32 i = 0; i < _priorities; i++)
689    {
690        total += IOMbufQueueGetSize(&(_primaryQueues[i]));
691    }
692    return total;
693}
694
695//---------------------------------------------------------------------------
696// Returns the number of packets dropped by the queue due to over-capacity.
697
698UInt32 IOBasicOutputQueue::getDropCount()
699{
700    return _stats->dropCount;
701}
702
703//---------------------------------------------------------------------------
704// Returns the number of packet passed to the target.
705
706UInt32 IOBasicOutputQueue::getOutputCount()
707{
708    return _stats->outputCount;
709}
710
711//---------------------------------------------------------------------------
712// Returns the number of times that a kIOOutputStatusRetry status code
713// is received from the target.
714
715UInt32 IOBasicOutputQueue::getRetryCount()
716{
717    return _stats->retryCount;
718}
719
720//---------------------------------------------------------------------------
721// Returns the number of times that a kIOOutputCommandStall action code
722// is received from the target.
723
724UInt32 IOBasicOutputQueue::getStallCount()
725{
726    return _stats->stallCount;
727}
728
729//---------------------------------------------------------------------------
730// Returns the current state of the queue object.
731
732UInt32 IOBasicOutputQueue::getState() const
733{
734    return _state;
735}
736
737//---------------------------------------------------------------------------
738// This method is called by our IONetworkData object when it receives
739// a read or a reset request. We need to be notified to intervene in
740// the request handling.
741
742IOReturn
743IOBasicOutputQueue::handleNetworkDataAccess(IONetworkData * data,
744                                            UInt32          accessType,
745                                            void *          arg)
746{
747    IOReturn ret = kIOReturnSuccess;
748
749    assert(data && (arg == kIOOutputQueueSignature));
750
751    // Check the type of data request.
752
753    switch (accessType)
754    {
755        case kIONetworkDataAccessTypeRead:
756        case kIONetworkDataAccessTypeSerialize:
757        {
758            UInt32 size;
759            QUEUE_LOCK;
760            size = getSize();   // _primaryQueues
761            for(UInt32 i = 0; i < _priorities; i++)
762            {
763                size += IOMbufQueueGetSize(&(_shadowQueues[i]));
764            }
765            QUEUE_UNLOCK;
766            _stats->size = size;
767            break;
768        }
769
770        default:
771            ret = kIOReturnNotWritable;
772            break;
773    }
774
775    return ret;
776}
777
778//---------------------------------------------------------------------------
779// Return an IONetworkData object containing an IOOutputQueueStats structure.
780
781IONetworkData * IOBasicOutputQueue::getStatisticsData() const
782{
783    return _statsData;
784}
785
786//===========================================================================
787// IOGatedOutputQueue
788//===========================================================================
789
790#undef  super
791#define super IOBasicOutputQueue
792OSDefineMetaClassAndStructors( IOGatedOutputQueue, IOBasicOutputQueue )
793
794//---------------------------------------------------------------------------
795// Initialize an IOGatedOutputQueue object.
796
797bool IOGatedOutputQueue::init(OSObject *      target,
798                              IOOutputAction  action,
799                              IOWorkLoop *    workloop,
800                              UInt32          capacity,
801                              UInt32          priorities)
802{
803    if (super::init(target, action, capacity, priorities) == false)
804        return false;
805
806    // Verify that the IOWorkLoop provided is valid.
807
808    if (OSDynamicCast(IOWorkLoop, workloop) == 0)
809        return false;
810
811    // Allocate and attach an IOCommandGate object to the workloop.
812
813    _gate = IOCommandGate::commandGate(this);
814
815    if (!_gate || (workloop->addEventSource(_gate) != kIOReturnSuccess))
816        return false;
817
818    // Allocate and attach an IOInterruptEventSource object to the workloop.
819
820    _interruptSrc = IOInterruptEventSource::interruptEventSource(
821                    this,
822			        (IOInterruptEventSource::Action) restartDeferredOutput
823                    );
824
825    if ( !_interruptSrc ||
826        (workloop->addEventSource(_interruptSrc) != kIOReturnSuccess) )
827        return false;
828
829    return true;
830}
831
832//---------------------------------------------------------------------------
833// Factory methods that will construct and initialize an IOGatedOutputQueue
834// object.
835
836IOGatedOutputQueue *
837IOGatedOutputQueue::withTarget(IONetworkController * target,
838                               IOWorkLoop *          workloop,
839                               UInt32                capacity)
840{
841    return IOGatedOutputQueue::withTarget(target, workloop, capacity, 1 /* priorities */);
842}
843
844IOGatedOutputQueue *
845IOGatedOutputQueue::withTarget(IONetworkController * target,
846                               IOWorkLoop *          workloop,
847                               UInt32                capacity,
848                               UInt32                priorities)
849{
850    IOGatedOutputQueue * queue = new IOGatedOutputQueue;
851
852    if (queue && !queue->init(target, target->getOutputHandler(), workloop,
853                              capacity, priorities))
854    {
855        queue->release();
856        queue = 0;
857    }
858    return queue;
859}
860
861IOGatedOutputQueue *
862IOGatedOutputQueue::withTarget(OSObject *     target,
863                               IOOutputAction action,
864                               IOWorkLoop *   workloop,
865                               UInt32         capacity)
866{
867    return IOGatedOutputQueue::withTarget(target, action, workloop, capacity, 1 /* priorities */);
868}
869
870IOGatedOutputQueue *
871IOGatedOutputQueue::withTarget(OSObject *     target,
872                               IOOutputAction action,
873                               IOWorkLoop *   workloop,
874                               UInt32         capacity,
875                               UInt32         priorities)
876{
877    IOGatedOutputQueue * queue = new IOGatedOutputQueue;
878
879    if (queue && !queue->init(target, action, workloop, capacity, priorities))
880    {
881        queue->release();
882        queue = 0;
883    }
884    return queue;
885}
886
887//---------------------------------------------------------------------------
888// Free the IOGatedOutputQueue object.
889
890void IOGatedOutputQueue::free()
891{
892    cancelServiceThread();
893
894    if (_gate)
895    {
896        IOWorkLoop *wl = _gate->getWorkLoop();
897		if(wl) wl->removeEventSource(_gate);
898		_gate->release();
899        _gate = 0;
900    }
901
902    if (_interruptSrc)
903    {
904        IOWorkLoop * wl = _interruptSrc->getWorkLoop();
905        if (wl) wl->removeEventSource(_interruptSrc);
906        _interruptSrc->release();
907        _interruptSrc = 0;
908    }
909
910    super::free();
911}
912
913//---------------------------------------------------------------------------
914// Called by an IOCommandGate object.
915
916void IOGatedOutputQueue::gatedOutput(OSObject *          /* owner */,
917                                     IOGatedOutputQueue * self,
918                                     IOMbufQueue *        queue,
919                                     UInt32 *             state)
920{
921    mbuf_t pkt;
922    UInt32        status;
923
924    do {
925        pkt = IOMbufQueueDequeue(queue);
926        assert(pkt);
927
928        // Handoff the packet to the controller driver.
929
930        status = ((self->_target)->*(self->_action))( pkt, 0 );
931
932        if ( status == ( kIOOutputStatusAccepted | kIOOutputCommandNone ) )
933        {
934            // Fast-path the typical code path.
935            self->_stats->outputCount++;
936        }
937        else
938        {
939            // Look at the return status and update statistics counters.
940
941            switch (status & kIOOutputStatusMask)
942            {
943                default:
944                case kIOOutputStatusAccepted:
945                    self->_stats->outputCount++;
946                    break;
947
948                case kIOOutputStatusRetry:
949                    IOMbufQueuePrepend(queue, pkt);
950                    self->_stats->retryCount++;
951                    break;
952            }
953
954            // Handle the requested action.
955
956            switch (status & kIOOutputCommandMask)
957            {
958                case kIOOutputCommandStall:
959                    *state = kStateOutputStalled;
960                    self->_stats->stallCount++;
961                    break;
962
963                default:
964                    break;
965            }
966        }
967    }
968	while ( IOMbufQueueGetSize(queue) && (*state == 0) );
969}
970
971//---------------------------------------------------------------------------
972// Called by our superclass to output all packets in the packet queue given.
973
974enum {
975    kStateOutputDeferred = 0x100
976};
977
978void IOGatedOutputQueue::output(IOMbufQueue * queue, UInt32 * state)
979{
980    if ( _gate->attemptAction((IOCommandGate::Action)
981                                &IOGatedOutputQueue::gatedOutput,
982                              (void *) this,
983                              (void *) queue,
984                              (void *) state) == kIOReturnCannotLock )
985    {
986        *state = kStateOutputDeferred;
987    }
988}
989
990bool IOGatedOutputQueue::scheduleServiceThread(void * param)
991{
992    if ( ((uintptr_t) param) & kStateOutputDeferred )
993    {
994        _interruptSrc->interruptOccurred(0, 0, 0);
995        return true;
996    }
997    else
998    {
999        return super::scheduleServiceThread(param);
1000    }
1001}
1002
1003void IOGatedOutputQueue::restartDeferredOutput(
1004                                        OSObject *               owner,
1005                                        IOInterruptEventSource * sender,
1006                                        int                      count)
1007{
1008    IOGatedOutputQueue * self = (IOGatedOutputQueue *) owner;
1009    self->serviceThread((void *) kStateOutputDeferred);
1010}
1011