2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <IOKit/IOKernelReportStructs.h>
30 #include <IOKit/IOKernelReporters.h>
31 #include "IOReporterDefs.h"
34 #define super IOReporter
35 OSDefineMetaClassAndStructors(IOStateReporter
, IOReporter
);
40 IOStateReporter::with(IOService
*reportingService
,
41 IOReportCategories categories
,
43 IOReportUnit unit
/* = kIOReportUnitHWTicks*/)
45 IOStateReporter
*reporter
, *rval
= NULL
;
47 // kprintf("%s\n", __func__); // can't IORLOG() from static
49 reporter
= new IOStateReporter
;
54 if (!reporter
->initWith(reportingService
, categories
, nstates
, unit
)) {
63 OSSafeReleaseNULL(reporter
);
70 IOStateReporter::initWith(IOService
*reportingService
,
71 IOReportCategories categories
,
77 IOReportChannelType channelType
= {
78 .categories
= categories
,
79 .report_format
= kIOReportFormatState
,
80 .nelements
= static_cast<uint16_t>(nstates
),
84 if (super::init(reportingService
, channelType
, unit
) != true) {
85 IORLOG("ERROR super::initWith failed");
90 _currentStates
= NULL
;
91 _lastUpdateTimes
= NULL
;
101 IOStateReporter::free(void)
103 if (_currentStates
) {
104 PREFL_MEMOP_PANIC(_nChannels
, int);
105 IOFree(_currentStates
, (size_t)_nChannels
* sizeof(int));
107 if (_lastUpdateTimes
) {
108 PREFL_MEMOP_PANIC(_nChannels
, uint64_t);
109 IOFree(_lastUpdateTimes
, (size_t)_nChannels
* sizeof(uint64_t));
117 IOStateReporter::handleSwapPrepare(int newNChannels
)
119 IOReturn res
= kIOReturnError
;
120 size_t newCurStatesSize
, newTSSize
;
122 //IORLOG("handleSwapPrepare (state) _nChannels before = %u", _nChannels);
124 IOREPORTER_CHECK_CONFIG_LOCK();
126 if (_swapCurrentStates
|| _swapLastUpdateTimes
) {
127 panic("IOStateReporter::_swap* already in use");
130 // new currentStates buffer
131 PREFL_MEMOP_FAIL(newNChannels
, int);
132 newCurStatesSize
= (size_t)newNChannels
* sizeof(int);
133 _swapCurrentStates
= (int*)IOMalloc(newCurStatesSize
);
134 if (_swapCurrentStates
== NULL
) {
135 res
= kIOReturnNoMemory
; goto finish
;
137 memset(_swapCurrentStates
, -1, newCurStatesSize
); // init w/"no state"
139 // new timestamps buffer
140 PREFL_MEMOP_FAIL(newNChannels
, uint64_t);
141 newTSSize
= (size_t)newNChannels
* sizeof(uint64_t);
142 _swapLastUpdateTimes
= (uint64_t *)IOMalloc(newTSSize
);
143 if (_swapLastUpdateTimes
== NULL
) {
144 res
= kIOReturnNoMemory
; goto finish
;
146 memset(_swapLastUpdateTimes
, 0, newTSSize
);
148 res
= super::handleSwapPrepare(newNChannels
);
152 if (_swapCurrentStates
) {
153 IOFree(_swapCurrentStates
, newCurStatesSize
);
154 _swapCurrentStates
= NULL
;
156 if (_swapLastUpdateTimes
) {
157 IOFree(_swapLastUpdateTimes
, newTSSize
);
158 _swapLastUpdateTimes
= NULL
;
166 IOStateReporter::handleAddChannelSwap(uint64_t channelID
,
167 const OSSymbol
*symChannelName
)
169 IOReturn res
= kIOReturnError
;
172 uint64_t *tmpTimestamps
;
173 bool swapComplete
= false;
175 //IORLOG("IOStateReporter::handleSwap");
177 if (!_swapCurrentStates
|| !_swapLastUpdateTimes
) {
178 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
182 IOREPORTER_CHECK_CONFIG_LOCK();
183 IOREPORTER_CHECK_LOCK();
185 // Copy any existing buffers
186 if (_currentStates
) {
187 PREFL_MEMOP_FAIL(_nChannels
, int);
188 memcpy(_swapCurrentStates
, _currentStates
,
189 (size_t)_nChannels
* sizeof(int));
191 if (!_lastUpdateTimes
) {
192 panic("IOStateReporter::handleAddChannelSwap _lastUpdateTimes unset despite non-NULL _currentStates");
194 PREFL_MEMOP_FAIL(_nChannels
, uint64_t);
195 memcpy(_swapLastUpdateTimes
, _lastUpdateTimes
,
196 (size_t)_nChannels
* sizeof(uint64_t));
199 // Update principal instance variables, keep old values in _swap* for cleanup
200 tmpCurStates
= _currentStates
;
201 _currentStates
= _swapCurrentStates
;
202 _swapCurrentStates
= tmpCurStates
;
204 tmpTimestamps
= _lastUpdateTimes
;
205 _lastUpdateTimes
= _swapLastUpdateTimes
;
206 _swapLastUpdateTimes
= tmpTimestamps
;
212 // invoke superclass(es): base class updates _nChannels & _nElements
213 res
= super::handleAddChannelSwap(channelID
, symChannelName
);
215 IORLOG("handleSwap(state) ERROR super::handleSwap failed!");
219 // Channel added successfully, initialize the new channel's state_ids to 0..nStates-1
220 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
221 handleSetStateID(channelID
, cnt
, (uint64_t)cnt
);
225 if (res
&& swapComplete
) {
226 // unswap so the unused buffers get cleaned up
227 tmpCurStates
= _currentStates
;
228 _currentStates
= _swapCurrentStates
;
229 _swapCurrentStates
= tmpCurStates
;
231 tmpTimestamps
= _lastUpdateTimes
;
232 _lastUpdateTimes
= _swapLastUpdateTimes
;
233 _swapLastUpdateTimes
= tmpTimestamps
;
241 IOStateReporter::handleSwapCleanup(int swapNChannels
)
243 IOREPORTER_CHECK_CONFIG_LOCK();
245 super::handleSwapCleanup(swapNChannels
);
247 if (_swapCurrentStates
) {
248 PREFL_MEMOP_PANIC(swapNChannels
, int);
249 IOFree(_swapCurrentStates
, (size_t)swapNChannels
* sizeof(int));
250 _swapCurrentStates
= NULL
;
252 if (_swapLastUpdateTimes
) {
253 PREFL_MEMOP_PANIC(swapNChannels
, uint64_t);
254 IOFree(_swapLastUpdateTimes
, (size_t)swapNChannels
* sizeof(uint64_t));
255 _swapLastUpdateTimes
= NULL
;
261 IOStateReporter::_getStateIndices(uint64_t channel_id
,
266 IOReturn res
= kIOReturnError
;
268 IOStateReportValues
*values
;
269 int element_index
= 0;
271 IOREPORTER_CHECK_LOCK();
273 if (getChannelIndices(channel_id
,
275 &element_index
) != kIOReturnSuccess
) {
276 res
= kIOReturnBadArgument
;
281 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
282 values
= (IOStateReportValues
*)getElementValues(element_index
+ cnt
);
284 if (values
== NULL
) {
285 res
= kIOReturnError
;
289 if (values
->state_id
== state_id
) {
291 res
= kIOReturnSuccess
;
296 res
= kIOReturnBadArgument
;
304 IOStateReporter::setChannelState(uint64_t channel_id
,
305 uint64_t new_state_id
)
307 IOReturn res
= kIOReturnError
;
308 int channel_index
, new_state_index
;
309 uint64_t last_intransition
= 0;
310 uint64_t prev_state_residency
= 0;
314 if (_getStateIndices(channel_id
, new_state_id
, &channel_index
, &new_state_index
) == kIOReturnSuccess
) {
315 res
= handleSetStateByIndices(channel_index
, new_state_index
,
317 prev_state_residency
);
321 res
= kIOReturnBadArgument
;
329 IOStateReporter::setChannelState(uint64_t channel_id
,
330 uint64_t new_state_id
,
331 uint64_t last_intransition
,
332 uint64_t prev_state_residency
)
334 return setChannelState(channel_id
, new_state_id
);
338 IOStateReporter::overrideChannelState(uint64_t channel_id
,
340 uint64_t time_in_state
,
341 uint64_t intransitions
,
342 uint64_t last_intransition
/*=0*/)
344 IOReturn res
= kIOReturnError
;
345 int channel_index
, state_index
;
349 if (_getStateIndices(channel_id
, state_id
, &channel_index
, &state_index
) == kIOReturnSuccess
) {
350 if (_lastUpdateTimes
[channel_index
]) {
351 panic("overrideChannelState() cannot be used after setChannelState()!\n");
354 res
= handleOverrideChannelStateByIndices(channel_index
, state_index
,
355 time_in_state
, intransitions
,
360 res
= kIOReturnBadArgument
;
369 IOStateReporter::handleOverrideChannelStateByIndices(int channel_index
,
371 uint64_t time_in_state
,
372 uint64_t intransitions
,
373 uint64_t last_intransition
/*=0*/)
375 IOReturn kerr
, result
= kIOReturnError
;
376 IOStateReportValues state_values
;
379 if (channel_index
< 0 || channel_index
>= _nChannels
) {
380 result
= kIOReturnBadArgument
; goto finish
;
383 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
384 / _channelDimension
) {
385 result
= kIOReturnOverrun
; goto finish
;
387 element_index
= channel_index
* _channelDimension
+ state_index
;
389 kerr
= copyElementValues(element_index
, (IOReportElementValues
*)&state_values
);
391 result
= kerr
; goto finish
;
394 // last_intransition = 0 -> no current state ("residency summary only")
395 state_values
.last_intransition
= last_intransition
;
396 state_values
.intransitions
= intransitions
;
397 state_values
.upticks
= time_in_state
;
399 // determines current time for metadata
400 kerr
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
402 result
= kerr
; goto finish
;
406 result
= kIOReturnSuccess
;
414 IOStateReporter::incrementChannelState(uint64_t channel_id
,
416 uint64_t time_in_state
,
417 uint64_t intransitions
,
418 uint64_t last_intransition
/*=0*/)
420 IOReturn res
= kIOReturnError
;
421 int channel_index
, state_index
;
425 if (_getStateIndices(channel_id
, state_id
, &channel_index
, &state_index
) == kIOReturnSuccess
) {
426 if (_lastUpdateTimes
[channel_index
]) {
427 panic("incrementChannelState() cannot be used after setChannelState()!\n");
430 res
= handleIncrementChannelStateByIndices(channel_index
, state_index
,
431 time_in_state
, intransitions
,
436 res
= kIOReturnBadArgument
;
445 IOStateReporter::handleIncrementChannelStateByIndices(int channel_index
,
447 uint64_t time_in_state
,
448 uint64_t intransitions
,
449 uint64_t last_intransition
/*=0*/)
451 IOReturn kerr
, result
= kIOReturnError
;
452 IOStateReportValues state_values
;
455 if (channel_index
< 0 || channel_index
>= _nChannels
) {
456 result
= kIOReturnBadArgument
; goto finish
;
459 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
460 / _channelDimension
) {
461 result
= kIOReturnOverrun
; goto finish
;
463 element_index
= channel_index
* _channelDimension
+ state_index
;
465 kerr
= copyElementValues(element_index
, (IOReportElementValues
*)&state_values
);
471 state_values
.last_intransition
= last_intransition
;
472 state_values
.intransitions
+= intransitions
;
473 state_values
.upticks
+= time_in_state
;
475 // determines current time for metadata
476 kerr
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
483 result
= kIOReturnSuccess
;
491 IOStateReporter::setState(uint64_t new_state_id
)
493 uint64_t last_intransition
= 0;
494 uint64_t prev_state_residency
= 0;
495 IOReturn res
= kIOReturnError
;
496 IOStateReportValues
*values
;
497 int channel_index
= 0, element_index
= 0, new_state_index
= 0;
502 if (_nChannels
== 1) {
503 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
504 new_state_index
= element_index
+ cnt
;
506 values
= (IOStateReportValues
*)getElementValues(new_state_index
);
508 if (values
== NULL
) {
509 res
= kIOReturnError
;
513 if (values
->state_id
== new_state_id
) {
514 res
= handleSetStateByIndices(channel_index
, new_state_index
,
516 prev_state_residency
);
522 res
= kIOReturnBadArgument
;
530 IOStateReporter::setState(uint64_t new_state_id
,
531 uint64_t last_intransition
,
532 uint64_t prev_state_residency
)
534 return setState(new_state_id
);
538 IOStateReporter::setStateID(uint64_t channel_id
,
542 IOReturn res
= kIOReturnError
;
546 res
= handleSetStateID(channel_id
, state_index
, state_id
);
555 IOStateReporter::handleSetStateID(uint64_t channel_id
,
559 IOReturn res
= kIOReturnError
;
560 IOStateReportValues state_values
;
561 int element_index
= 0;
563 IOREPORTER_CHECK_LOCK();
565 if (getFirstElementIndex(channel_id
, &element_index
) == kIOReturnSuccess
) {
566 if (state_index
>= _channelDimension
) {
567 res
= kIOReturnBadArgument
; goto finish
;
569 if (_nElements
- state_index
<= element_index
) {
570 res
= kIOReturnOverrun
; goto finish
;
572 element_index
+= state_index
;
574 if (copyElementValues(element_index
, (IOReportElementValues
*)&state_values
) != kIOReturnSuccess
) {
575 res
= kIOReturnBadArgument
;
579 state_values
.state_id
= state_id
;
581 res
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
584 // FIXME: set a bit somewhere (reporter-wide?) that state_ids can no longer be
585 // assumed to be contiguous
591 IOStateReporter::setStateByIndices(int channel_index
,
594 IOReturn res
= kIOReturnError
;
595 uint64_t last_intransition
= 0;
596 uint64_t prev_state_residency
= 0;
600 res
= handleSetStateByIndices(channel_index
, new_state_index
,
601 last_intransition
, prev_state_residency
);
609 IOStateReporter::setStateByIndices(int channel_index
,
611 uint64_t last_intransition
,
612 uint64_t prev_state_residency
)
614 return setStateByIndices(channel_index
, new_state_index
);
618 IOStateReporter::handleSetStateByIndices(int channel_index
,
620 uint64_t last_intransition
,
621 uint64_t prev_state_residency
)
623 IOReturn res
= kIOReturnError
;
625 IOStateReportValues curr_state_values
, new_state_values
;
626 int curr_state_index
= 0;
627 int curr_element_index
, new_element_index
;
628 uint64_t last_ch_update_time
= 0;
629 uint64_t recordTime
= mach_absolute_time();
631 IOREPORTER_CHECK_LOCK();
633 if (channel_index
< 0 || channel_index
>= _nChannels
) {
634 res
= kIOReturnBadArgument
; goto finish
;
637 // if no timestamp provided, last_intransition = time of recording (now)
638 if (last_intransition
== 0) {
639 last_intransition
= recordTime
;
642 // First update target state if different than the current state
643 // _currentStates[] initialized to -1 to detect first state transition
644 curr_state_index
= _currentStates
[channel_index
];
645 if (new_state_index
!= curr_state_index
) {
646 // fetch element data
647 if (channel_index
< 0 || channel_index
> (_nElements
- new_state_index
)
648 / _channelDimension
) {
649 res
= kIOReturnOverrun
; goto finish
;
651 new_element_index
= channel_index
* _channelDimension
+ new_state_index
;
652 if (copyElementValues(new_element_index
,
653 (IOReportElementValues
*)&new_state_values
)) {
654 res
= kIOReturnBadArgument
;
658 // Update new state's transition info
659 new_state_values
.intransitions
+= 1;
660 new_state_values
.last_intransition
= last_intransition
;
662 // and store the values
663 res
= setElementValues(new_element_index
,
664 (IOReportElementValues
*)&new_state_values
,
667 if (res
!= kIOReturnSuccess
) {
671 _currentStates
[channel_index
] = new_state_index
;
674 /* Now update time spent in any previous state
675 * If new_state_index = curr_state_index, this updates time in the
676 * current state. If this is the channel's first state transition,
677 * the last update time will be zero.
679 * Note: While setState() should never be called on a channel being
680 * updated with increment/overrideChannelState(), that's another way
681 * that the last update time might not exist. Regardless, if there
682 * is no basis for determining time spent in previous state, there's
685 last_ch_update_time
= _lastUpdateTimes
[channel_index
];
686 if (last_ch_update_time
!= 0) {
687 if (channel_index
< 0 || channel_index
> (_nElements
- curr_state_index
)
688 / _channelDimension
) {
689 res
= kIOReturnOverrun
; goto finish
;
691 curr_element_index
= channel_index
* _channelDimension
+ curr_state_index
;
692 if (copyElementValues(curr_element_index
,
693 (IOReportElementValues
*)&curr_state_values
)) {
694 res
= kIOReturnBadArgument
;
697 // compute the time spent in previous state, unless provided
698 if (prev_state_residency
== 0) {
699 prev_state_residency
= last_intransition
- last_ch_update_time
;
702 curr_state_values
.upticks
+= prev_state_residency
;
704 res
= setElementValues(curr_element_index
,
705 (IOReportElementValues
*)&curr_state_values
,
708 if (res
!= kIOReturnSuccess
) {
713 // record basis for next "time in prior state" calculation
714 // (also arms a panic in override/incrementChannelState())
715 _lastUpdateTimes
[channel_index
] = last_intransition
;
722 // blocks might make this slightly easier?
724 IOStateReporter::getStateInTransitions(uint64_t channel_id
,
727 return _getStateValue(channel_id
, state_id
, kInTransitions
);
731 IOStateReporter::getStateResidencyTime(uint64_t channel_id
,
734 return _getStateValue(channel_id
, state_id
, kResidencyTime
);
738 IOStateReporter::getStateLastTransitionTime(uint64_t channel_id
,
741 return _getStateValue(channel_id
, state_id
, kLastTransitionTime
);
745 IOStateReporter::_getStateValue(uint64_t channel_id
,
747 enum valueSelector value
)
749 int channel_index
= 0, element_index
= 0, cnt
;
750 IOStateReportValues
*values
= NULL
;
751 uint64_t result
= kIOReportInvalidValue
;
755 if (getChannelIndices(channel_id
, &channel_index
, &element_index
) == kIOReturnSuccess
) {
756 if (updateChannelValues(channel_index
) == kIOReturnSuccess
) {
757 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
758 values
= (IOStateReportValues
*)getElementValues(element_index
);
760 if (state_id
== values
->state_id
) {
763 result
= values
->intransitions
;
766 result
= values
->upticks
;
768 case kLastTransitionTime
:
769 result
= values
->last_intransition
;
789 IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id
)
792 uint64_t result
= kIOReportInvalidValue
;
796 if (getChannelIndex(channel_id
, &channel_index
) == kIOReturnSuccess
) {
797 result
= _lastUpdateTimes
[channel_index
];
806 /* updateChannelValues() is called to refresh state before being
807 * reported outside the reporter. In the case of IOStateReporter,
808 * this is primarily an update to the "time in state" data.
811 IOStateReporter::updateChannelValues(int channel_index
)
813 IOReturn kerr
, result
= kIOReturnError
;
815 int state_index
, element_idx
;
816 uint64_t currentTime
;
817 uint64_t last_ch_update_time
;
818 uint64_t time_in_state
;
819 IOStateReportValues state_values
;
821 IOREPORTER_CHECK_LOCK();
823 if (channel_index
< 0 || channel_index
>= _nChannels
) {
824 result
= kIOReturnBadArgument
; goto finish
;
827 /* First check to see whether this channel has begun self-
828 * calculation of time in state. It's possible this channel
829 * has yet to be initialized or that the driver is updating
830 * the channel with override/incrementChannelState() which
831 * never enable automatic time-in-state updates. In that case,
832 * there is nothing to update and we return success.
834 last_ch_update_time
= _lastUpdateTimes
[channel_index
];
835 if (last_ch_update_time
== 0) {
836 result
= kIOReturnSuccess
; goto finish
;
839 // figure out the current state (if any)
840 state_index
= _currentStates
[channel_index
];
842 // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
843 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
844 / _channelDimension
) {
845 result
= kIOReturnOverrun
; goto finish
;
847 element_idx
= channel_index
* _channelDimension
+ state_index
;
849 // get the current values
850 kerr
= copyElementValues(element_idx
, (IOReportElementValues
*)&state_values
);
852 result
= kerr
; goto finish
;
855 // calculate time in state
856 currentTime
= mach_absolute_time();
857 time_in_state
= currentTime
- last_ch_update_time
;
858 state_values
.upticks
+= time_in_state
;
860 // and store the values
861 kerr
= setElementValues(element_idx
,
862 (IOReportElementValues
*)&state_values
,
865 result
= kerr
; goto finish
;
868 // Record basis for next "prior time" calculation
869 _lastUpdateTimes
[channel_index
] = currentTime
;
873 result
= kIOReturnSuccess
;