]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IONetworking/IOOutputQueue.cpp
xnu-123.5.tar.gz
[apple/xnu.git] / iokit / Families / IONetworking / IOOutputQueue.cpp
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/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>
41
42 //===========================================================================
43 // IOOutputQueue
44 //===========================================================================
45
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))
50
51 #undef super
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);
70
71 //---------------------------------------------------------------------------
72 // Initialize an IOOutputQueue object.
73
74 bool IOOutputQueue::init()
75 {
76 if (super::init() == false)
77 return false;
78
79 // Allocate and initialize the callout entry for async service.
80
81 _callEntry = thread_call_allocate((thread_call_func_t) &runServiceThread,
82 (void *) this); /* param0 */
83 if (_callEntry == 0)
84 return false;
85
86 return true;
87 }
88
89 //---------------------------------------------------------------------------
90 // Frees the IOOutputQueue object.
91
92 void IOOutputQueue::free()
93 {
94 if (_callEntry)
95 {
96 cancelServiceThread();
97 thread_call_free(_callEntry);
98 _callEntry = 0;
99 }
100
101 super::free();
102 }
103
104 //---------------------------------------------------------------------------
105 // Schedule a service thread callout, which will run the
106 // serviceThread() method.
107
108 bool IOOutputQueue::scheduleServiceThread(void * param = 0)
109 {
110 return thread_call_enter1(_callEntry, (thread_call_param_t) param);
111 }
112
113 //---------------------------------------------------------------------------
114 // Cancel any pending service thread callout.
115
116 bool IOOutputQueue::cancelServiceThread()
117 {
118 if (_callEntry == 0)
119 return false;
120 else
121 return thread_call_cancel(_callEntry);
122 }
123
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.
127
128 void
129 IOOutputQueue::runServiceThread(thread_call_param_t param0, /* this */
130 thread_call_param_t param1) /* param */
131 {
132 assert(param0);
133 ((IOOutputQueue *) param0)->serviceThread(param1);
134 }
135
136 //---------------------------------------------------------------------------
137 // Must be implemented by a subclass that calls scheduleServiceThread().
138 // The default implementation is a placeholder and performs no action.
139
140 void IOOutputQueue::serviceThread(void * param)
141 {
142 }
143
144 //---------------------------------------------------------------------------
145 // Return an address of a method that is designated to handle
146 // packets sent to the queue object.
147
148 IOOutputAction IOOutputQueue::getOutputHandler() const
149 {
150 return (IOOutputAction) &IOOutputQueue::enqueue;
151 }
152
153 //---------------------------------------------------------------------------
154 // Return an IONetworkData object containing statistics counters.
155
156 IONetworkData * IOOutputQueue::getStatisticsData() const
157 {
158 return 0;
159 }
160
161
162 //===========================================================================
163 // IOBasicOutputQueue
164 //===========================================================================
165
166 #undef super
167 #define super IOOutputQueue
168 OSDefineMetaClassAndStructors( IOBasicOutputQueue, IOOutputQueue )
169
170 #define QUEUE_LOCK IOSimpleLockLock(_spinlock)
171 #define QUEUE_UNLOCK IOSimpleLockUnlock(_spinlock)
172
173 #define kIOOutputQueueSignature ((void *) 0xfacefeed)
174
175 //---------------------------------------------------------------------------
176 // 'C' function glue to dispatch the IONetworkData notification.
177
178 IOReturn
179 IOBasicOutputQueue::dispatchNetworkDataNotification(void * target,
180 void * param,
181 IONetworkData * data,
182 UInt32 type)
183 {
184 IOBasicOutputQueue * self = (IOBasicOutputQueue *) target;
185 return self->handleNetworkDataAccess(data, type, param);
186 }
187
188 //---------------------------------------------------------------------------
189 // Initialize an IOBasicOutputQueue object.
190
191 bool IOBasicOutputQueue::init(OSObject * target,
192 IOOutputAction action,
193 UInt32 capacity = 0)
194 {
195 if (super::init() == false)
196 return false;
197
198 if ((target == 0) || (action == 0))
199 return false;
200
201 _target = target;
202 _action = action;
203
204 // Create a data object for queue statistics.
205
206 _statsData = IONetworkData::withInternalBuffer(
207 kIOOutputQueueStatsKey,
208 sizeof(IOOutputQueueStats),
209 kIONetworkDataBasicAccessTypes,
210 this,
211 (IONetworkData::Action)
212 &IOBasicOutputQueue::dispatchNetworkDataNotification,
213 kIOOutputQueueSignature);
214
215 if (_statsData == 0)
216 return false;
217
218 _stats = (IOOutputQueueStats *) _statsData->getBuffer();
219 assert(_stats);
220
221 _stats->capacity = capacity;
222
223 // Create two queue objects.
224
225 _queues[0] = IONew(IOMbufQueue, 1);
226 _queues[1] = IONew(IOMbufQueue, 1);
227
228 IOMbufQueueInit(_queues[0], capacity);
229 IOMbufQueueInit(_queues[1], capacity);
230
231 if ( (_queues[0] == 0) || (_queues[1] == 0) )
232 return false;
233
234 _inQueue = _queues[0];
235
236 // Create a spinlock to protect the queue.
237
238 _spinlock = IOSimpleLockAlloc();
239 if (_spinlock == 0)
240 return false;
241
242 return true;
243 }
244
245 //---------------------------------------------------------------------------
246 // Factory methods that will construct and initialize an IOBasicOutputQueue
247 // object.
248
249 IOBasicOutputQueue *
250 IOBasicOutputQueue::withTarget(IONetworkController * target,
251 UInt32 capacity = 0)
252 {
253 IOBasicOutputQueue * queue = new IOBasicOutputQueue;
254
255 if (queue && !queue->init(target, target->getOutputHandler(), capacity))
256 {
257 queue->release();
258 queue = 0;
259 }
260 return queue;
261 }
262
263 IOBasicOutputQueue *
264 IOBasicOutputQueue::withTarget(OSObject * target,
265 IOOutputAction action,
266 UInt32 capacity = 0)
267 {
268 IOBasicOutputQueue * queue = new IOBasicOutputQueue;
269
270 if (queue && !queue->init(target, action, capacity))
271 {
272 queue->release();
273 queue = 0;
274 }
275 return queue;
276 }
277
278 //---------------------------------------------------------------------------
279 // Release all resources previously allocated before calling super::free().
280
281 void IOBasicOutputQueue::free()
282 {
283 cancelServiceThread();
284
285 if (_spinlock)
286 {
287 flush();
288 IOSimpleLockFree(_spinlock);
289 _spinlock = 0;
290 }
291
292 if (_queues[0]) IODelete(_queues[0], IOMbufQueue, 1);
293 if (_queues[1]) IODelete(_queues[1], IOMbufQueue, 1);
294 _queues[0] = _queues[1] = 0;
295
296 if (_statsData)
297 {
298 _statsData->release();
299 _statsData = 0;
300 }
301
302 super::free();
303 }
304
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.
309
310 void IOBasicOutputQueue::serviceThread(void * param)
311 {
312 QUEUE_LOCK;
313 STATE_CLR((UInt32) param);
314 STATE_SET(kStateOutputActive);
315 dequeue();
316 QUEUE_UNLOCK;
317 }
318
319 //---------------------------------------------------------------------------
320 // Add a single packet, or a chain of packets, to the queue object.
321 // This method can support multiple clients threads.
322
323 UInt32 IOBasicOutputQueue::enqueue(struct mbuf * m, void * param)
324 {
325 bool success;
326
327 QUEUE_LOCK;
328
329 success = IOMbufQueueEnqueue(_inQueue, m);
330
331 if ( STATE_IS( kStateRunning ) )
332 {
333 STATE_SET( kStateOutputActive );
334 dequeue();
335 }
336
337 QUEUE_UNLOCK;
338
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.
343
344 if (success == false)
345 {
346 OSAddAtomic( IOMbufFree(m),
347 (SInt32 *) &_stats->dropCount );
348 }
349
350 return 0;
351 }
352
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
357 // spinlock held.
358
359 void IOBasicOutputQueue::dequeue()
360 {
361 IOMbufQueue * outQueue = _queues[0];
362 UInt32 newState = 0;
363 UInt32 myServiceCount;
364
365 // Switch the input queue. Work on the real queue, while allowing
366 // clients to continue to queue packets to the "shadow" queue.
367
368 _inQueue = _queues[1];
369
370 // While dequeue is allowed, and incoming queue has packets.
371
372 while ( STATE_IS( kStateRunning | kStateOutputActive ) &&
373 IOMbufQueueGetSize(outQueue) )
374 {
375 myServiceCount = _serviceCount;
376
377 QUEUE_UNLOCK;
378
379 output( outQueue, &newState );
380
381 QUEUE_LOCK;
382
383 // If service() was called during the interval when the
384 // spinlock was released, then refuse to honor any
385 // stall requests.
386
387 if ( newState )
388 {
389 if ( myServiceCount != _serviceCount )
390 newState &= ~kStateOutputStalled;
391
392 STATE_SET( newState );
393 }
394
395 // Absorb new packets added to the shadow queue.
396
397 IOMbufQueueEnqueue( outQueue, _inQueue );
398 }
399
400 _inQueue = _queues[0];
401
402 STATE_CLR( kStateOutputActive );
403
404 if ( newState & kStateOutputServiceMask )
405 {
406 scheduleServiceThread((void *)(newState & kStateOutputServiceMask));
407 }
408
409 if (_waitDequeueDone)
410 {
411 // A stop() request is waiting for the transmit thread to
412 // complete transmission. Wake up the waiting thread.
413
414 _waitDequeueDone = false;
415 thread_wakeup((void *) &_waitDequeueDone);
416 }
417 }
418
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.
422
423 void IOBasicOutputQueue::output(IOMbufQueue * queue, UInt32 * state)
424 {
425 struct mbuf * pkt;
426 UInt32 status;
427
428 do {
429 pkt = IOMbufQueueDequeue(queue);
430 assert(pkt);
431
432 // Handoff each packet to the controller driver.
433
434 status = (_target->*_action)( pkt, 0 );
435
436 if ( status == ( kIOOutputStatusAccepted | kIOOutputCommandNone ) )
437 {
438 // Fast-path the typical code path.
439 _stats->outputCount++;
440 }
441 else
442 {
443 // Look at the return status and update statistics counters.
444
445 switch (status & kIOOutputStatusMask)
446 {
447 default:
448 case kIOOutputStatusAccepted:
449 _stats->outputCount++;
450 break;
451
452 case kIOOutputStatusRetry:
453 IOMbufQueuePrepend(queue, pkt);
454 _stats->retryCount++;
455 break;
456 }
457
458 // Handle the requested action.
459
460 switch (status & kIOOutputCommandMask)
461 {
462 case kIOOutputCommandStall:
463 *state = kStateOutputStalled;
464 _stats->stallCount++;
465 break;
466
467 default:
468 break;
469 }
470 }
471 }
472 while ( IOMbufQueueGetSize(queue) && (*state == 0) );
473 }
474
475 //---------------------------------------------------------------------------
476 // Start or enable the queue.
477
478 bool IOBasicOutputQueue::start()
479 {
480 QUEUE_LOCK;
481
482 STATE_SET( kStateRunning );
483 STATE_CLR( kStateOutputStalled );
484 _serviceCount++;
485
486 if ( STATE_IS( kStateRunning ) )
487 {
488 STATE_SET( kStateOutputActive );
489 dequeue();
490 }
491
492 QUEUE_UNLOCK;
493
494 return true; /* always return true */
495 }
496
497 //---------------------------------------------------------------------------
498 // Stop or disable the queue.
499
500 bool IOBasicOutputQueue::stop()
501 {
502 bool wasRunning;
503
504 QUEUE_LOCK;
505
506 wasRunning = STATE_HAS( kStateRunning );
507
508 STATE_CLR( kStateRunning );
509
510 if ( STATE_HAS( kStateOutputActive ) )
511 {
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.
515 //
516 // Wait for the dequeue thread to complete processing.
517
518 _waitDequeueDone = true;
519
520 assert_wait((void *) &_waitDequeueDone, false);
521 }
522
523 QUEUE_UNLOCK;
524
525 thread_block((void (*)(void)) 0);
526
527 return wasRunning;
528 }
529
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.
533
534 bool IOBasicOutputQueue::service(UInt32 options = 0)
535 {
536 bool doDequeue = false;
537 bool async = (options & kServiceAsync);
538 UInt32 oldState;
539
540 QUEUE_LOCK;
541
542 oldState = _state;
543
544 // Clear the stall condition.
545
546 STATE_CLR( kStateOutputStalled );
547 _serviceCount++;
548
549 if ( ( oldState & kStateOutputStalled ) &&
550 STATE_IS( kStateRunning ) &&
551 IOMbufQueueGetSize(_queues[0]) )
552 {
553 doDequeue = true;
554 STATE_SET( kStateOutputActive );
555 if (async == false) dequeue();
556 }
557
558 QUEUE_UNLOCK;
559
560 if ( doDequeue && async )
561 {
562 scheduleServiceThread();
563 }
564
565 return doDequeue;
566 }
567
568 //---------------------------------------------------------------------------
569 // Release all packets held by the queue.
570
571 UInt32 IOBasicOutputQueue::flush()
572 {
573 UInt32 flushCount;
574
575 QUEUE_LOCK;
576 flushCount = IOMbufFree( IOMbufQueueDequeueAll( _inQueue ) );
577 OSAddAtomic(flushCount, (SInt32 *) &_stats->dropCount);
578 QUEUE_UNLOCK;
579
580 return flushCount;
581 }
582
583 //---------------------------------------------------------------------------
584 // Change the capacity of the queue.
585
586 bool IOBasicOutputQueue::setCapacity(UInt32 capacity)
587 {
588 QUEUE_LOCK;
589 IOMbufQueueSetCapacity(_queues[1], capacity);
590 IOMbufQueueSetCapacity(_queues[0], capacity);
591 _stats->capacity = capacity;
592 QUEUE_UNLOCK;
593 return true;
594 }
595
596 //---------------------------------------------------------------------------
597 // Returns the current queue capacity.
598
599 UInt32 IOBasicOutputQueue::getCapacity() const
600 {
601 return _stats->capacity;
602 }
603
604 //---------------------------------------------------------------------------
605 // Returns the current queue size.
606
607 UInt32 IOBasicOutputQueue::getSize() const
608 {
609 UInt32 size;
610 QUEUE_LOCK;
611 size = IOMbufQueueGetSize(_queues[0]) + IOMbufQueueGetSize(_queues[1]);
612 QUEUE_UNLOCK;
613 return size;
614 }
615
616 //---------------------------------------------------------------------------
617 // Returns the number of packets dropped by the queue due to over-capacity.
618
619 UInt32 IOBasicOutputQueue::getDropCount()
620 {
621 return _stats->dropCount;
622 }
623
624 //---------------------------------------------------------------------------
625 // Returns the number of packet passed to the target.
626
627 UInt32 IOBasicOutputQueue::getOutputCount()
628 {
629 return _stats->outputCount;
630 }
631
632 //---------------------------------------------------------------------------
633 // Returns the number of times that a kIOOutputStatusRetry status code
634 // is received from the target.
635
636 UInt32 IOBasicOutputQueue::getRetryCount()
637 {
638 return _stats->retryCount;
639 }
640
641 //---------------------------------------------------------------------------
642 // Returns the number of times that a kIOOutputCommandStall action code
643 // is received from the target.
644
645 UInt32 IOBasicOutputQueue::getStallCount()
646 {
647 return _stats->stallCount;
648 }
649
650 //---------------------------------------------------------------------------
651 // Returns the current state of the queue object.
652
653 UInt32 IOBasicOutputQueue::getState() const
654 {
655 return _state;
656 }
657
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.
662
663 IOReturn
664 IOBasicOutputQueue::handleNetworkDataAccess(IONetworkData * data,
665 UInt32 accessType,
666 void * arg)
667 {
668 IOReturn ret = kIOReturnSuccess;
669
670 assert(data && (arg == kIOOutputQueueSignature));
671
672 // Check the type of data request.
673
674 switch (accessType)
675 {
676 case kIONetworkDataAccessTypeRead:
677 case kIONetworkDataAccessTypeSerialize:
678 _stats->size = getSize();
679 break;
680
681 default:
682 ret = kIOReturnNotWritable;
683 break;
684 }
685
686 return ret;
687 }
688
689 //---------------------------------------------------------------------------
690 // Return an IONetworkData object containing an IOOutputQueueStats structure.
691
692 IONetworkData * IOBasicOutputQueue::getStatisticsData() const
693 {
694 return _statsData;
695 }
696
697 //===========================================================================
698 // IOGatedOutputQueue
699 //===========================================================================
700
701 #undef super
702 #define super IOBasicOutputQueue
703 OSDefineMetaClassAndStructors( IOGatedOutputQueue, IOBasicOutputQueue )
704
705 //---------------------------------------------------------------------------
706 // Initialize an IOGatedOutputQueue object.
707
708 bool IOGatedOutputQueue::init(OSObject * target,
709 IOOutputAction action,
710 IOWorkLoop * workloop,
711 UInt32 capacity = 0)
712 {
713 if (super::init(target, action, capacity) == false)
714 return false;
715
716 // Verify that the IOWorkLoop provided is valid.
717
718 if (OSDynamicCast(IOWorkLoop, workloop) == 0)
719 return false;
720
721 // Allocate and attach an IOCommandGate object to the workloop.
722
723 _gate = IOCommandGate::commandGate(this);
724
725 if (!_gate || (workloop->addEventSource(_gate) != kIOReturnSuccess))
726 return false;
727
728 // Allocate and attach an IOInterruptEventSource object to the workloop.
729
730 _interruptSrc = IOInterruptEventSource::interruptEventSource(
731 this,
732 (IOInterruptEventSource::Action) restartDeferredOutput
733 );
734
735 if ( !_interruptSrc ||
736 (workloop->addEventSource(_interruptSrc) != kIOReturnSuccess) )
737 return false;
738
739 return true;
740 }
741
742 //---------------------------------------------------------------------------
743 // Factory methods that will construct and initialize an IOGatedOutputQueue
744 // object.
745
746 IOGatedOutputQueue *
747 IOGatedOutputQueue::withTarget(IONetworkController * target,
748 IOWorkLoop * workloop,
749 UInt32 capacity = 0)
750 {
751 IOGatedOutputQueue * queue = new IOGatedOutputQueue;
752
753 if (queue && !queue->init(target, target->getOutputHandler(), workloop,
754 capacity))
755 {
756 queue->release();
757 queue = 0;
758 }
759 return queue;
760 }
761
762 IOGatedOutputQueue *
763 IOGatedOutputQueue::withTarget(OSObject * target,
764 IOOutputAction action,
765 IOWorkLoop * workloop,
766 UInt32 capacity = 0)
767 {
768 IOGatedOutputQueue * queue = new IOGatedOutputQueue;
769
770 if (queue && !queue->init(target, action, workloop, capacity))
771 {
772 queue->release();
773 queue = 0;
774 }
775 return queue;
776 }
777
778 //---------------------------------------------------------------------------
779 // Free the IOGatedOutputQueue object.
780
781 void IOGatedOutputQueue::free()
782 {
783 cancelServiceThread();
784
785 if (_gate)
786 {
787 _gate->release();
788 _gate = 0;
789 }
790
791 if (_interruptSrc)
792 {
793 IOWorkLoop * wl = _interruptSrc->getWorkLoop();
794 if (wl) wl->removeEventSource(_interruptSrc);
795 _interruptSrc->release();
796 _interruptSrc = 0;
797 }
798
799 super::free();
800 }
801
802 //---------------------------------------------------------------------------
803 // Called by an IOCommandGate object.
804
805 void IOGatedOutputQueue::gatedOutput(OSObject * /* owner */,
806 IOGatedOutputQueue * self,
807 IOMbufQueue * queue,
808 UInt32 * state)
809 {
810 struct mbuf * pkt;
811 UInt32 status;
812
813 do {
814 pkt = IOMbufQueueDequeue(queue);
815 assert(pkt);
816
817 // Handoff the packet to the controller driver.
818
819 status = ((self->_target)->*(self->_action))( pkt, 0 );
820
821 if ( status == ( kIOOutputStatusAccepted | kIOOutputCommandNone ) )
822 {
823 // Fast-path the typical code path.
824 self->_stats->outputCount++;
825 }
826 else
827 {
828 // Look at the return status and update statistics counters.
829
830 switch (status & kIOOutputStatusMask)
831 {
832 default:
833 case kIOOutputStatusAccepted:
834 self->_stats->outputCount++;
835 break;
836
837 case kIOOutputStatusRetry:
838 IOMbufQueuePrepend(queue, pkt);
839 self->_stats->retryCount++;
840 break;
841 }
842
843 // Handle the requested action.
844
845 switch (status & kIOOutputCommandMask)
846 {
847 case kIOOutputCommandStall:
848 *state = kStateOutputStalled;
849 self->_stats->stallCount++;
850 break;
851
852 default:
853 break;
854 }
855 }
856 }
857 while ( IOMbufQueueGetSize(queue) && (*state == 0) );
858 }
859
860 //---------------------------------------------------------------------------
861 // Called by our superclass to output all packets in the packet queue given.
862
863 enum {
864 kStateOutputDeferred = 0x100
865 };
866
867 void IOGatedOutputQueue::output(IOMbufQueue * queue, UInt32 * state)
868 {
869 if ( _gate->attemptAction((IOCommandGate::Action)
870 &IOGatedOutputQueue::gatedOutput,
871 (void *) this,
872 (void *) queue,
873 (void *) state) == kIOReturnCannotLock )
874 {
875 *state = kStateOutputDeferred;
876 }
877 }
878
879 bool IOGatedOutputQueue::scheduleServiceThread(void * param)
880 {
881 if ( ((UInt32) param) & kStateOutputDeferred )
882 {
883 _interruptSrc->interruptOccurred(0, 0, 0);
884 return true;
885 }
886 else
887 {
888 return super::scheduleServiceThread(param);
889 }
890 }
891
892 void IOGatedOutputQueue::restartDeferredOutput(
893 OSObject * owner,
894 IOInterruptEventSource * sender,
895 int count)
896 {
897 IOGatedOutputQueue * self = (IOGatedOutputQueue *) owner;
898 self->serviceThread((void *) kStateOutputDeferred);
899 }