Annotation of XNU/iokit/Families/IONetworking/IOOutputQueue.cpp, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1998-2000 Apple Computer, 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:  * Copyright (c) 1998 Apple Computer, Inc.  All rights reserved. 
                     24:  *
                     25:  * IOOutputQueue.cpp
                     26:  *
                     27:  * HISTORY
                     28:  * 2-Feb-1999       Joe Liu (jliu) created.
                     29:  *
                     30:  */
                     31: 
                     32: #include <IOKit/assert.h>
                     33: #include <IOKit/IOWorkLoop.h>
                     34: #include <IOKit/network/IOPacketQueue.h>
                     35: #include <IOKit/network/IOOutputQueue.h>
                     36: #include <IOKit/network/IOOQLockFIFOQueue.h>
                     37: #include <IOKit/network/IOOQGateFIFOQueue.h>
                     38: #include <IOKit/network/IONetworkStats.h>
                     39: #include <IOKit/network/IONetworkController.h>
                     40: 
                     41: //===========================================================================
                     42: // IOOutputQueue class.
                     43: //
                     44: // An object that queues output packets on behalf of its target, usually an 
                     45: // IONetworkController instance, and allows the target to control the packet
                     46: // flow. IOOutputQueue is is an abstract class that provides an interface for
                     47: // its subclasses. Concrete subclasses will complete the implementation while
                     48: // providing unique synchronization options.
                     49: //===========================================================================
                     50: 
                     51: #warning need real atomic operators
                     52: #define ATOMIC_SET(x, v)    ((x) = (v))
                     53: #define ATOMIC_ADD(x, y)    ((x) += (y))
                     54: 
                     55: #undef  super
                     56: #define super OSObject
                     57: OSDefineMetaClass( IOOutputQueue, OSObject )
                     58: OSDefineAbstractStructors( IOOutputQueue, OSObject )
                     59: 
                     60: #define kIOOQParamMagic     ((void *) 0xfeedbeef)
                     61: 
                     62: //---------------------------------------------------------------------------
                     63: // Initialize an IOOutputQueue instance.
                     64: //
                     65: // target: The object that shall receive packets from the queue,
                     66: //   and is usually a subclass of IONetworkController. If the target is
                     67: //   not an IONetworkController instance, then the target must immediately
                     68: //   call registerOutputHandler() after initializing the queue.
                     69: //
                     70: // capacity: The initial capacity of the output queue, defined as
                     71: //   the number of packets that the queue can hold without dropping.
                     72: //
                     73: // Returns true if initialized successfully, false otherwise.
                     74: 
                     75: bool IOOutputQueue::init(OSObject * target,
                     76:                          UInt32     capacity)
                     77: {
                     78: #if 0
                     79:     _target    = 0;
                     80:     _outAction = 0;
                     81:     _stats     = 0;
                     82:     _param     = 0;
                     83:     _callEntry = 0;
                     84:     _queue     = 0;
                     85: #endif
                     86: 
                     87:     if (!super::init())
                     88:         return false;
                     89: 
                     90:     if (OSDynamicCast(IONetworkController, target))
                     91:     {
                     92:         // If the target is a type of IONetworkController, then call
                     93:         // its getOutputHandler() method to obtain a pointer to its
                     94:         // output method.
                     95: 
                     96:         if (registerOutputHandler(target,
                     97:             ((IONetworkController *) target)->getOutputHandler()) == false)
                     98:             return false;
                     99:     }
                    100: 
                    101:     // Allocate an IOPacketQueue (or a subclass).
                    102:     //
                    103:     _queue = createPacketQueue(capacity);
                    104:     if (!_queue)
                    105:         return false;
                    106: 
                    107:     // Allocate and initialize the callout entry.
                    108:     //
                    109:     _callEntry = thread_call_allocate((thread_call_func_t)&runServiceThread,
                    110:                                       (void *)this);
                    111:     if (!_callEntry)
                    112:         return false;
                    113: 
                    114:     // Create a data object for queue statistics.
                    115:     //
                    116:     _statsData = IONetworkData::withName(
                    117:                  kIOOutputQueueStatsKey,
                    118:                  sizeof(IOOutputQueueStats),
                    119:                  0,
                    120:                  kIONDBasicAccessTypes | kIONDAccessTypeReset,
                    121:                  this,
                    122:                  (IONetworkData::Action)
                    123:                      &IOOutputQueue::dataReadAndResetHandler,
                    124:                  kIOOQParamMagic);
                    125:     if (!_statsData)
                    126:         return false;
                    127: 
                    128:     _stats = (IOOutputQueueStats *) _statsData->getBuffer();
                    129: 
                    130:     return true;
                    131: }
                    132: 
                    133: //---------------------------------------------------------------------------
                    134: // Frees the IOOutputQueue instance.
                    135: 
                    136: void IOOutputQueue::free()
                    137: {
                    138:     if (_statsData)
                    139:         _statsData->release();
                    140: 
                    141:     if (_callEntry)
                    142:     {
                    143:         cancelServiceThread();
                    144:         thread_call_free(_callEntry);
                    145:     }
                    146:     
                    147:     if (_queue)
                    148:         _queue->release();
                    149: 
                    150:     super::free();
                    151: }
                    152: 
                    153: //---------------------------------------------------------------------------
                    154: // This method is called by our IONetworkData object when it receives
                    155: // a read or a reset request. We need to be notified in order to intervene
                    156: // in the request handling.
                    157: //
                    158: // data: The IONetworkData object.
                    159: // opFlag: The operation requested.
                    160: // arg: Argument for the request.
                    161: 
                    162: IOReturn IOOutputQueue::dataReadAndResetHandler(IONetworkData * data,
                    163:                                                 UInt32          type,
                    164:                                                 void *          arg)
                    165: {
                    166:     IOReturn ret = kIOReturnSuccess;
                    167: 
                    168:     assert(data && (arg == kIOOQParamMagic));
                    169: 
                    170:     // Check the type of data request.
                    171:     //
                    172:     switch (type)
                    173:     {
                    174:         case kIONDAccessTypeRead:
                    175:         case kIONDAccessTypeSerialize:
                    176:             _stats->capacity = getCapacity();
                    177:             _stats->size     = getSize();
                    178:             _stats->peakSize = getPeakSize();
                    179:             break;
                    180: 
                    181:         case kIONDAccessTypeReset:
                    182:             getPeakSize(true);
                    183:             getDropCount(true);
                    184:             getOutputCount(true);
                    185:             getRetryCount(true);
                    186:             getStallCount(true);
                    187:             break;
                    188: 
                    189:         default:
                    190:             ret = kIOReturnNotWritable;
                    191:             break;
                    192:     }
                    193: 
                    194:     return ret;
                    195: }
                    196: 
                    197: //---------------------------------------------------------------------------
                    198: // Returns the current state of the queue object.
                    199: 
                    200: IOOQState IOOutputQueue::getState() const
                    201: {
                    202:     return _state;
                    203: }
                    204: 
                    205: //---------------------------------------------------------------------------
                    206: // Schedule a service thread callout, which will then execute the
                    207: // serviceThread() method. Subclasses should not override this method.
                    208: 
                    209: bool IOOutputQueue::scheduleServiceThread()
                    210: {
                    211:     thread_call_enter(_callEntry);
                    212:     return true;
                    213: }
                    214: 
                    215: //---------------------------------------------------------------------------
                    216: // Cancel the service thread callout. Subclasses should not override this
                    217: // method.
                    218: 
                    219: bool IOOutputQueue::cancelServiceThread()
                    220: {
                    221:     return thread_call_cancel(_callEntry);
                    222: }
                    223: 
                    224: //---------------------------------------------------------------------------
                    225: // A glue function that is registered as the service thread callout handler.
                    226: // This function in turn will call the serviceThread() method.
                    227: 
                    228: void IOOutputQueue::runServiceThread(IOOutputQueue * self, thread_call_param_t)
                    229: {
                    230:     assert(self);
                    231:     self->serviceThread();
                    232: }
                    233: 
                    234: //---------------------------------------------------------------------------
                    235: // Must be implemented by subclasses that calls scheduleServiceThread().
                    236: // The default implementation is a placeholder and performs no useful action.
                    237: 
                    238: void IOOutputQueue::serviceThread()
                    239: {
                    240: }
                    241: 
                    242: //---------------------------------------------------------------------------
                    243: // Release all packets held in the queue. The size of the queue is reset
                    244: // to zero. The drop packet counter is incremented by the number of packets
                    245: // freed. See getDropCount().
                    246: //
                    247: // Returns the number of packets freed.
                    248: 
                    249: UInt32 IOOutputQueue::flush()
                    250: {
                    251:     UInt32 flushCount = _queue->lockFlush();
                    252:     ATOMIC_ADD(_stats->dropCount, flushCount);
                    253:     return flushCount;
                    254: }
                    255: 
                    256: //---------------------------------------------------------------------------
                    257: // capacity: The new capacity of the queue.
                    258: //
                    259: // Returns true if the new capacity was accepted, false otherwise.
                    260: 
                    261: bool IOOutputQueue::setCapacity(UInt32 capacity)
                    262: {
                    263:     return _queue->setCapacity(capacity);
                    264: }
                    265: 
                    266: //---------------------------------------------------------------------------
                    267: // Returns the current queue capacity.
                    268: 
                    269: UInt32 IOOutputQueue::getCapacity() const
                    270: {
                    271:     return _queue->getCapacity();
                    272: }
                    273: 
                    274: //---------------------------------------------------------------------------
                    275: // Returns the current queue size.
                    276: 
                    277: UInt32 IOOutputQueue::getSize() const
                    278: {
                    279:     return _queue->getSize();
                    280: }
                    281: 
                    282: //---------------------------------------------------------------------------
                    283: // reset: Resets the counter if true.
                    284: //
                    285: // Return the peak queue size.
                    286: 
                    287: UInt32 IOOutputQueue::getPeakSize(bool reset = false)
                    288: {
                    289:     return _queue->getPeakSize(reset);
                    290: }
                    291: 
                    292: //---------------------------------------------------------------------------
                    293: // This method returns the number of times that a kIOOQReturnStatusDropped 
                    294: // status code is received from the target's output handler, indicating
                    295: // that the packet given was dropped. This count is also incremented when
                    296: // the queue drops a packet due to overcapacity, or by an explicit flush()
                    297: // call.
                    298: //
                    299: // reset: Resets the counter if true.
                    300: //
                    301: // Returns the number of dropped packets.
                    302: //
                    303: // FIXME - need atomic exchange
                    304: 
                    305: UInt32 IOOutputQueue::getDropCount(bool reset = false)
                    306: {
                    307:     UInt32 count = _stats->dropCount;
                    308:     if (reset)
                    309:         _stats->dropCount = 0;
                    310:     return count;
                    311: }
                    312: 
                    313: //---------------------------------------------------------------------------
                    314: // This method returns the number of times that a kIOOQReturnStatusSuccess 
                    315: // status code is received from the target's output handler, indicating
                    316: // that the packet given was accepted, and is ready to be (or already has been)
                    317: // transmitted.
                    318: //
                    319: // reset: Resets the counter if true.
                    320: //
                    321: // Returns the number of packets accepted by our target.
                    322: //
                    323: // FIXME - need atomic exchange
                    324: 
                    325: UInt32 IOOutputQueue::getOutputCount(bool reset = false)
                    326: {
                    327:     UInt32 count = _stats->outputCount;
                    328:     if (reset)
                    329:         _stats->outputCount = 0;
                    330:     return count;
                    331: }
                    332: 
                    333: //---------------------------------------------------------------------------
                    334: // This method returns the number of times that a kIOOQReturnStatusRetry
                    335: // status code is received from the target's output handler, indicating
                    336: // that the target is temporarily unable to handle the packet given, and
                    337: // the queue should try to resend the same packet at some later time.
                    338: //
                    339: // reset: Resets the counter if true.
                    340: //
                    341: // Returns the number of retries issued by the target.
                    342: //
                    343: // FIXME - need atomic exchange
                    344: 
                    345: UInt32 IOOutputQueue::getRetryCount(bool reset = false)
                    346: {
                    347:     UInt32 count = _stats->retryCount;
                    348:     if (reset)
                    349:         _stats->retryCount = 0;
                    350:     return count;
                    351: }
                    352: 
                    353: //---------------------------------------------------------------------------
                    354: // Each time the queue is stalled, when a kIOOQReturnActionStall action code
                    355: // is received from the target's output handler, a counter is incremented.
                    356: // This method returns the value stored in this counter.
                    357: //
                    358: // reset: Resets the counter if true.
                    359: //
                    360: // Returns the number of times that the queue was stalled.
                    361: //
                    362: // FIXME - need atomic exchange
                    363: 
                    364: UInt32 IOOutputQueue::getStallCount(bool reset = false)
                    365: {
                    366:     UInt32 count = _stats->stallCount;
                    367:     if (reset)
                    368:         _stats->stallCount = 0;
                    369:     return count;
                    370: }
                    371: 
                    372: //---------------------------------------------------------------------------
                    373: // Allows subclasses to override the default action, which is to allocate
                    374: // and return an IOPacketQueue instance. The returned object is used to 
                    375: // implement the queueing behavior of the IOOutputQueue.
                    376: //
                    377: // capacity: The initial capacity of the queue.
                    378: //
                    379: // Returns a newly allocated and initialized IOPacketQueue instance.
                    380: 
                    381: IOPacketQueue * IOOutputQueue::createPacketQueue(UInt32 capacity) const
                    382: {
                    383:     return IOPacketQueue::withCapacity(capacity);
                    384: }
                    385: 
                    386: //---------------------------------------------------------------------------
                    387: // Return an address of a method that is designated to handle
                    388: // packets sent to the queue object.
                    389: //
                    390: // Returns the address of the output packet handler.
                    391: 
                    392: IOOutputAction IOOutputQueue::getOutputHandler() const
                    393: {
                    394:     return (IOOutputAction) &IOOutputQueue::enqueue;
                    395: }
                    396: 
                    397: //---------------------------------------------------------------------------
                    398: // Register the target object and method to call to handle packets
                    399: // removed from the queue.
                    400: //
                    401: // handler: Pointer to an initialized IOOutputHandler structure passed in
                    402: //          by the caller.
                    403: //
                    404: // Returns true if the structure given was accepted, otherwise return false.
                    405: 
                    406: bool IOOutputQueue::registerOutputHandler(OSObject *      target,
                    407:                                           IOOutputAction  action)
                    408: {
                    409:     if (!target || !action)
                    410:         return false;
                    411: 
                    412:     // Cache the structure fields to instance variables.
                    413:     //
                    414:     _target    = target;
                    415:     _outAction = action;
                    416: 
                    417:     return true;
                    418: }
                    419: 
                    420: //---------------------------------------------------------------------------
                    421: // Return an IONetworkData object containing an IOOutputQueueStats structure.
                    422: 
                    423: IONetworkData * IOOutputQueue::getStatisticsData() const
                    424: {
                    425:     return _statsData;
                    426: }
                    427: 
                    428: //===========================================================================
                    429: // IOOQLockFIFOQueue
                    430: //===========================================================================
                    431: 
                    432: #undef  super
                    433: #define super IOOutputQueue
                    434: OSDefineMetaClassAndStructors( IOOQLockFIFOQueue, IOOutputQueue )
                    435: 
                    436: #define LOCK     IOTakeLock(_mutex)
                    437: #define UNLOCK   IOUnlock(_mutex)
                    438: 
                    439: //---------------------------------------------------------------------------
                    440: // Initialize an IOOQLockFIFOQueue instance.
                    441: //
                    442: // target: The object that shall receive packets from the queue,
                    443: //   and is usually a subclass of IONetworkController. If the target is
                    444: //   not an IONetworkController instance, then the target must immediately
                    445: //   call registerOutputHandler() after initializing the queue.
                    446: //
                    447: // capacity: The initial capacity of the output queue, defined as
                    448: //   the number of packets that the queue can hold without dropping.
                    449: //
                    450: // Returns true if initialized successfully, false otherwise.
                    451: 
                    452: bool IOOQLockFIFOQueue::init(OSObject * target, UInt32 capacity = 0)
                    453: {
                    454:     if (!super::init(target, capacity))
                    455:         return false;
                    456: 
                    457:     // Allocate the mutex lock.
                    458:     //
                    459:     _mutex = IOLockAlloc();
                    460:     if (!_mutex)
                    461:         return false;
                    462: 
                    463:     ATOMIC_SET(_state, kIOOQStateStopped);
                    464: 
                    465:     return true;
                    466: }
                    467: 
                    468: //---------------------------------------------------------------------------
                    469: // Factory method that will construct and initialize an IOOQLockFIFOQueue 
                    470: // instance.
                    471: 
                    472: IOOQLockFIFOQueue * IOOQLockFIFOQueue::withTarget(OSObject * target,
                    473:                                                   UInt32     capacity = 0)
                    474: {
                    475:     IOOQLockFIFOQueue * queue = new IOOQLockFIFOQueue;
                    476:     
                    477:     if (queue && !queue->init(target, capacity))
                    478:     {
                    479:         queue->release();
                    480:         queue = 0;
                    481:     }
                    482:     return queue;
                    483: }
                    484: 
                    485: //---------------------------------------------------------------------------
                    486: // Frees the IOOQLockFIFOQueue instance.
                    487: 
                    488: void IOOQLockFIFOQueue::free()
                    489: {
                    490:     cancelServiceThread();
                    491: 
                    492:     if (_mutex) IOLockFree(_mutex);
                    493: 
                    494:     super::free();
                    495: }
                    496: 
                    497: //---------------------------------------------------------------------------
                    498: // Provide an implementation for the interface defined in IOOutputQueue.
                    499: // This method is called by a callout thread when service() is performed 
                    500: // asynchronously.
                    501: 
                    502: void IOOQLockFIFOQueue::serviceThread()
                    503: {
                    504:     // Bump the enqueue count, and service the queue if 'serviceActive'
                    505:     // is false.
                    506: 
                    507:     ATOMIC_ADD(_enqueueCount, 1);
                    508: 
                    509:     if ((_state == kIOOQStateRunning) && !_serviceActive)
                    510:     {
                    511:         dequeue();
                    512:     }
                    513: }
                    514: 
                    515: //---------------------------------------------------------------------------
                    516: // Handles packet (or a packet chain) sent to the queue. This method can
                    517: // handle calls from multiple simultaneous client threads. A client thread
                    518: // that calls enqueue() will acquire a mutex lock and call dequeue() if it
                    519: // detects that no other thread is actively dequeueing packets from the queue.
                    520: // The dequeue() method will return when the queue becomes empty, or if the 
                    521: // target stalls the queue. This method may block its caller.
                    522: //
                    523: // m: The packet (or a packet chain) to be queued for transmission.
                    524: //
                    525: // Returns: The number of dropped packets.
                    526: 
                    527: UInt32 IOOQLockFIFOQueue::enqueue(struct mbuf * m)
                    528: {
                    529:     UInt32 dropped = _queue->lockEnqueue(m);
                    530: 
                    531:     // Increment the dropped packet counter.
                    532:     if (dropped)
                    533:         ATOMIC_ADD(_stats->dropCount, dropped);
                    534: 
                    535:     // Bump the enqueue count, and service the queue if 'serviceActive'
                    536:     // is false.
                    537: 
                    538:     ATOMIC_ADD(_enqueueCount, 1);
                    539: 
                    540:     if ((_state == kIOOQStateRunning) && !_serviceActive)
                    541:     {
                    542:         dequeue();
                    543:     }
                    544: 
                    545:     return dropped;
                    546: }
                    547: 
                    548: //---------------------------------------------------------------------------
                    549: // Responsible for removing all packets from the queue and calling
                    550: // the target's output handler. This method returns when the queue becomes
                    551: // empty or if the queue's state is no longer kIOOQStateRunning. The target's
                    552: // output handler is called for every packet removed from the queue. Only a
                    553: // single packet is sent to the target for every call, the packets are never
                    554: // chained. A mutex lock enforces single threaded access to the target's
                    555: // output handler.
                    556: 
                    557: void IOOQLockFIFOQueue::dequeue()
                    558: {
                    559:     struct mbuf *  m;
                    560:     UInt32         r;
                    561:     UInt32         dequeueCount;
                    562: 
                    563:     LOCK;
                    564: 
                    565:     do {
                    566:         // Take a snapshot of the current enqueueCount.
                    567:         //
                    568:         dequeueCount = _enqueueCount;
                    569: 
                    570:         _serviceActive = true;  // -- mark dequeuing active --
                    571: 
                    572:         while ((_state == kIOOQStateRunning) &&
                    573:                 _queue->getSize() && (m = _queue->lockDequeue()))
                    574:         {
                    575:             ATOMIC_SET(_state, kIOOQStateStalled);
                    576: 
                    577:             // Call the target's output handler.
                    578:             //
                    579:             r = (_target->*_outAction)(m);
                    580: 
                    581:             // Decide the fate of the dequeued packet and
                    582:             // update statistics counters.
                    583:             //
                    584:             switch (r & kIOOQReturnStatusMask)
                    585:             {
                    586:                 default:
                    587:                 case kIOOQReturnStatusDropped:
                    588:                     ATOMIC_ADD(_stats->dropCount, 1);
                    589:                     break;
                    590: 
                    591:                 case kIOOQReturnStatusSuccess:
                    592:                     ATOMIC_ADD(_stats->outputCount, 1);
                    593:                     break;
                    594: 
                    595:                 case kIOOQReturnStatusRetry:
                    596:                     _queue->lockPrepend(m);
                    597:                     ATOMIC_ADD(_stats->retryCount, 1);
                    598:                     break;
                    599:             }
                    600: 
                    601:             // Handle the requested action.
                    602:             //
                    603:             switch (r & kIOOQReturnActionMask)
                    604:             {
                    605:                 default:
                    606:                 case kIOOQReturnActionNone:
                    607:                     ATOMIC_SET(_state, kIOOQStateRunning);
                    608:                     break;
                    609: 
                    610:                 case kIOOQReturnActionStall:
                    611:                     ATOMIC_ADD(_stats->stallCount, 1);
                    612:                     break;
                    613:             }
                    614: 
                    615:             // Consume all enqueueCounts before looping.
                    616:             //
                    617:             dequeueCount = _enqueueCount;
                    618: 
                    619:         } /* while [ running and queue has packets ] */
                    620: 
                    621:         _serviceActive = false; // -- mark dequeuing inactive --
                    622: 
                    623:     } while (dequeueCount != _enqueueCount);
                    624: 
                    625:     UNLOCK;
                    626: }
                    627: 
                    628: //---------------------------------------------------------------------------
                    629: // This method is called by the target to start the queue. Once started,
                    630: // the queue will be allowed to call the target's output handler.
                    631: // Before that, with the queue stopped, the queue will absorb incoming
                    632: // packets sent to the enqueue() method, but no packets will be dequeued,
                    633: // and the target's output handler will not be called.
                    634: //
                    635: // Returns true if the queue was successfully started, false otherwise.
                    636: 
                    637: bool IOOQLockFIFOQueue::start()
                    638: {
                    639:     service(true);
                    640:     return true;    // always return success
                    641: }
                    642: 
                    643: //---------------------------------------------------------------------------
                    644: // Stops the queue to prevent it from calling the target's output handler.
                    645: // This call is synchronous the caller may block. The target's output handler 
                    646: // must never call this method, or any other queue methods.
                    647: 
                    648: void IOOQLockFIFOQueue::stop()
                    649: {
                    650:     LOCK;
                    651:     ATOMIC_SET(_state, kIOOQStateStopped);
                    652:     UNLOCK;
                    653: }
                    654: 
                    655: //---------------------------------------------------------------------------
                    656: // If the queue becomes stalled, then service() must be called to restart
                    657: // the queue when the target is ready to accept more packets. If the queue
                    658: // is not empty, this method will also call dequeue(). Note that if the
                    659: // target never sends a kIOOQReturnActionStall action code to the queue,
                    660: // then the queue will never stall on its own accord. Calling this method
                    661: // on a running queue that is not stalled is harmless.
                    662: //
                    663: // sync: True if the service action should be performed synchronously,
                    664: //   false to perform the action asynchronously without blocking the caller,
                    665: //   but with a much higher latency cost.
                    666: //
                    667: // Returns true if the queue needed servicing, false otherwise.
                    668: 
                    669: bool IOOQLockFIFOQueue::service(bool sync = true)
                    670: {
                    671:     bool started = false;
                    672: 
                    673:     ATOMIC_SET(_state, kIOOQStateRunning);
                    674: 
                    675:     if (_queue->getSize())
                    676:     {
                    677:         ATOMIC_ADD(_enqueueCount, 1);
                    678: 
                    679:         if (!_serviceActive)
                    680:         {
                    681:             if (sync)
                    682:                 dequeue();
                    683:             else
                    684:                 scheduleServiceThread();
                    685: 
                    686:             started = true;
                    687:         }
                    688:     }
                    689: 
                    690:     return started;
                    691: }
                    692: 
                    693: //===========================================================================
                    694: // IOOQGateFIFOQueue
                    695: //===========================================================================
                    696: 
                    697: #undef  super
                    698: #define super IOOutputQueue
                    699: OSDefineMetaClassAndStructors( IOOQGateFIFOQueue, IOOutputQueue )
                    700: 
                    701: #define GATED_DEQUEUE   _gate->runCommand()
                    702: 
                    703: //---------------------------------------------------------------------------
                    704: // Initialize an IOOQGateFIFOQueue instance.
                    705: //
                    706: // target: The object that shall receive packets from the queue,
                    707: //   and is usually a subclass of IONetworkController. If the target is
                    708: //   not an IONetworkController instance, then the target must immediately
                    709: //   call registerOutputHandler() after initializing the queue.
                    710: //
                    711: // workloop: A workloop object provided by the target that the
                    712: //   queue will use to add an internal IOCommandGate as an event source.
                    713: //
                    714: // capacity: The initial capacity of the output queue, defined as
                    715: //   the number of packets that the queue can hold without dropping.
                    716: //
                    717: // Returns true if initialized successfully, false otherwise.
                    718: 
                    719: bool IOOQGateFIFOQueue::init(OSObject *        target,
                    720:                              IOWorkLoop *      workloop,
                    721:                              UInt32            capacity = 0)
                    722: {
                    723:     if (!super::init(target, capacity))
                    724:         return false;
                    725: 
                    726:     // Verify that the IOWorkLoop is valid.
                    727:     //
                    728:     if (!OSDynamicCast(IOWorkLoop, workloop))
                    729:         return false;
                    730: 
                    731:     // Allocate and attach an IOCommandGate instance to the workloop.
                    732:     // Set the default action to gatedDequeue().
                    733:     //
                    734:     _gate = IOCommandGate::commandGate(this,
                    735:                (IOCommandGate::Action) &IOOQGateFIFOQueue:: gatedDequeue);
                    736:     if (!_gate || (workloop->addEventSource(_gate) != kIOReturnSuccess))
                    737:         return false;
                    738: 
                    739:     ATOMIC_SET(_state, kIOOQStateStopped);
                    740: 
                    741:     return true;
                    742: }
                    743: 
                    744: //---------------------------------------------------------------------------
                    745: // Factory method that will construct and initialize an IOOQGateFIFOQueue 
                    746: // instance.
                    747: //
                    748: // Returns an IOOQGateFIFOQueue instance upon success, or 0 otherwise.
                    749: 
                    750: IOOQGateFIFOQueue *
                    751: IOOQGateFIFOQueue::withTarget(OSObject *   target,
                    752:                               IOWorkLoop * workloop,
                    753:                               UInt32       capacity = 0)
                    754: {
                    755:     IOOQGateFIFOQueue * queue = new IOOQGateFIFOQueue;
                    756:     
                    757:     if (queue && !queue->init(target, workloop, capacity))
                    758:     {
                    759:         queue->release();
                    760:         queue = 0;
                    761:     }
                    762:     return queue;
                    763: }
                    764: 
                    765: //---------------------------------------------------------------------------
                    766: // Frees the IOOQGateFIFOQueue instance.
                    767: 
                    768: void IOOQGateFIFOQueue::free()
                    769: {
                    770:     cancelServiceThread();
                    771: 
                    772:     if (_gate)
                    773:         _gate->release();
                    774: 
                    775:     super::free();
                    776: }
                    777: 
                    778: //---------------------------------------------------------------------------
                    779: // Provide an implementation for the interface defined in IOOutputQueue.
                    780: // This method is called by a callout thread when service() is performed 
                    781: // asynchronously.
                    782: 
                    783: void IOOQGateFIFOQueue::serviceThread()
                    784: {
                    785:     // Bump the enqueue count, and service the queue if 'serviceActive'
                    786:     // is false.
                    787: 
                    788:     ATOMIC_ADD(_enqueueCount, 1);
                    789: 
                    790:     if ((_state == kIOOQStateRunning) && !_serviceActive)
                    791:     {
                    792:         GATED_DEQUEUE;
                    793:     }
                    794: }
                    795: 
                    796: //---------------------------------------------------------------------------
                    797: // Handles packet (or a packet chain) sent to the queue. This method can
                    798: // handle calls from multiple simultaneous client threads. A client thread
                    799: // that calls enqueue() will acquire a mutex lock and call dequeue() if it
                    800: // detects that no other thread is actively dequeueing packets from the queue.
                    801: // The dequeue() method will return when the queue becomes empty, or if the 
                    802: // target stalls the queue. This method may block its caller.
                    803: //
                    804: // m: The packet (or a packet chain) to be queued for transmission.
                    805: 
                    806: UInt32 IOOQGateFIFOQueue::enqueue(struct mbuf * m)
                    807: {
                    808:     UInt32 dropped = _queue->lockEnqueue(m);
                    809: 
                    810:     // Increment the dropped packet counter.
                    811:     if (dropped)
                    812:         ATOMIC_ADD(_stats->dropCount, dropped);
                    813: 
                    814:     // Bump the enqueue count, and service the queue if 'serviceActive'
                    815:     // is false.
                    816: 
                    817:     ATOMIC_ADD(_enqueueCount, 1);
                    818: 
                    819:     if ((_state == kIOOQStateRunning) && !_serviceActive)
                    820:     {
                    821:         GATED_DEQUEUE;
                    822:     }
                    823: 
                    824:     return dropped;
                    825: }
                    826: 
                    827: //---------------------------------------------------------------------------
                    828: // Responsible for removing all packets from the queue and calling
                    829: // the target's output handler. This method returns when the queue becomes
                    830: // empty or if the queue's state is no longer kIOOQStateRunning. The target's
                    831: // output handler is called for every packet removed from the queue. Only a
                    832: // single packet is sent to the target for every call, the packets are never
                    833: // chained. An IOCommandGate attached to an workloop provided by the target
                    834: // ensures mutual exclusion between the dequeueing action (and calls to the
                    835: // target's output handler), and any other action performed by the workloop's
                    836: // thread.
                    837: 
                    838: void IOOQGateFIFOQueue::dequeue()
                    839: {
                    840:     GATED_DEQUEUE;
                    841: }
                    842: 
                    843: void IOOQGateFIFOQueue::gatedDequeue()
                    844: {
                    845:     struct mbuf *  m;
                    846:     UInt32         r;
                    847:     UInt32         dequeueCount;
                    848: 
                    849:     do {
                    850:         // Take a snapshot of the current enqueueCount.
                    851:         //
                    852:         dequeueCount = _enqueueCount;
                    853: 
                    854:         _serviceActive = true;  // -- mark dequeuing active --
                    855: 
                    856:         while ((_state == kIOOQStateRunning) &&
                    857:                 _queue->getSize() && (m = _queue->lockDequeue()))
                    858:         {
                    859:             ATOMIC_SET(_state, kIOOQStateStalled);
                    860: 
                    861:             // Call the controller's output handler.
                    862:             //
                    863:             r = (_target->*_outAction)(m);
                    864: 
                    865:             // Decide what should happen to the dequeued packet and
                    866:             // update statistics counters.
                    867:             //
                    868:             switch (r & kIOOQReturnStatusMask)
                    869:             {
                    870:                 default:
                    871:                 case kIOOQReturnStatusDropped:
                    872:                     ATOMIC_ADD(_stats->dropCount, 1);
                    873:                     break;
                    874: 
                    875:                 case kIOOQReturnStatusSuccess:
                    876:                     ATOMIC_ADD(_stats->outputCount, 1);
                    877:                     break;
                    878: 
                    879:                 case kIOOQReturnStatusRetry:
                    880:                     _queue->lockPrepend(m);
                    881:                     ATOMIC_ADD(_stats->retryCount, 1);
                    882:                     break;
                    883:             }
                    884: 
                    885:             // Handle the requested action.
                    886:             //
                    887:             switch (r & kIOOQReturnActionMask)
                    888:             {
                    889:                 default:
                    890:                 case kIOOQReturnActionNone:
                    891:                     ATOMIC_SET(_state, kIOOQStateRunning);
                    892:                     break;
                    893: 
                    894:                 case kIOOQReturnActionStall:
                    895:                     ATOMIC_ADD(_stats->stallCount, 1);
                    896:                     break;
                    897:             }
                    898: 
                    899:             // Consume all enqueueCounts before looping.
                    900:             //
                    901:             dequeueCount = _enqueueCount;
                    902: 
                    903:         } /* while [ running and queue has packets ] */
                    904: 
                    905:         _serviceActive = false; // -- mark dequeuing inactive --
                    906: 
                    907:     } while (dequeueCount != _enqueueCount);
                    908: }
                    909: 
                    910: //---------------------------------------------------------------------------
                    911: // This method is called by the target to start the queue. Once started,
                    912: // the queue will be allowed to call the target's output handler.
                    913: // Before that, with the queue stopped, the queue will absorb incoming
                    914: // packets sent to the enqueue() method, but no packets will be dequeued,
                    915: // and the target's output handler will not be called.
                    916: //
                    917: // Returns true if the queue was successfully started, false otherwise.
                    918: 
                    919: bool IOOQGateFIFOQueue::start()
                    920: {
                    921:     service(true);
                    922:     return true;    // always return success
                    923: }
                    924: 
                    925: //---------------------------------------------------------------------------
                    926: // Stops the queue to prevent it from calling the target's output handler.
                    927: // This call is synchronous the caller may block. The target's output handler 
                    928: // must never call this method, or any other queue methods.
                    929: 
                    930: void IOOQGateFIFOQueue::stop()
                    931: {   
                    932:     _gate->runAction( (IOCommandGate::Action) &IOOQGateFIFOQueue::gatedStop );
                    933: }
                    934: 
                    935: void IOOQGateFIFOQueue::gatedStop()
                    936: {
                    937:     ATOMIC_SET(_state, kIOOQStateStopped);
                    938: }
                    939: 
                    940: //---------------------------------------------------------------------------
                    941: // If the queue becomes stalled, then service() must be called to restart
                    942: // the queue when the target is ready to accept more packets. If the queue
                    943: // is not empty, this method will also call dequeue(). Note that if the
                    944: // target never sends a kIOOQReturnActionStall action code to the queue,
                    945: // then the queue will never stall on its own accord. Calling this method
                    946: // on a running queue that is not stalled is harmless.
                    947: //
                    948: // sync: True if the service action should be performed synchronously,
                    949: //   false to perform the action asynchronously without blocking the caller,
                    950: //   but with a much higher latency cost.
                    951: //
                    952: // Returns true if the queue needed servicing, false otherwise.
                    953: 
                    954: bool IOOQGateFIFOQueue::service(bool sync = true)
                    955: {
                    956:     bool started = false;
                    957: 
                    958:     ATOMIC_SET(_state, kIOOQStateRunning);
                    959: 
                    960:     if (_queue->getSize())
                    961:     {
                    962:         ATOMIC_ADD(_enqueueCount, 1);
                    963: 
                    964:         if (!_serviceActive)
                    965:         {
                    966:             if (sync)
                    967:                 GATED_DEQUEUE;
                    968:             else
                    969:                 scheduleServiceThread();
                    970: 
                    971:             started = true;
                    972:         }
                    973:     }
                    974: 
                    975:     return started;
                    976: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.