]>
Commit | Line | Data |
---|---|---|
1c79356b A |
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 | } |