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 #define IOKIT_ENABLE_SHARED_PTR
31 #include <libkern/c++/OSSharedPtr.h>
32 #include <IOKit/IOKernelReportStructs.h>
33 #include <IOKit/IOKernelReporters.h>
34 #include "IOReporterDefs.h"
37 #define super IOReporter
38 OSDefineMetaClassAndStructors(IOStateReporter
, IOReporter
);
42 OSSharedPtr
<IOStateReporter
>
43 IOStateReporter::with(IOService
*reportingService
,
44 IOReportCategories categories
,
46 IOReportUnit unit
/* = kIOReportUnitHWTicks*/)
48 OSSharedPtr
<IOStateReporter
> reporter
;
50 if (nstates
> INT16_MAX
) {
54 reporter
= OSMakeShared
<IOStateReporter
>();
59 if (!reporter
->initWith(reportingService
, categories
, (int16_t) nstates
, unit
)) {
67 IOStateReporter::initWith(IOService
*reportingService
,
68 IOReportCategories categories
,
74 IOReportChannelType channelType
= {
75 .categories
= categories
,
76 .report_format
= kIOReportFormatState
,
77 .nelements
= static_cast<uint16_t>(nstates
),
81 if (super::init(reportingService
, channelType
, unit
) != true) {
82 IORLOG("ERROR super::initWith failed");
87 _currentStates
= NULL
;
88 _lastUpdateTimes
= NULL
;
98 IOStateReporter::free(void)
100 if (_currentStates
) {
101 PREFL_MEMOP_PANIC(_nChannels
, int);
102 IOFree(_currentStates
, (size_t)_nChannels
* sizeof(int));
104 if (_lastUpdateTimes
) {
105 PREFL_MEMOP_PANIC(_nChannels
, uint64_t);
106 IOFree(_lastUpdateTimes
, (size_t)_nChannels
* sizeof(uint64_t));
114 IOStateReporter::handleSwapPrepare(int newNChannels
)
116 IOReturn res
= kIOReturnError
;
117 size_t newCurStatesSize
, newTSSize
;
119 //IORLOG("handleSwapPrepare (state) _nChannels before = %u", _nChannels);
121 IOREPORTER_CHECK_CONFIG_LOCK();
123 if (_swapCurrentStates
|| _swapLastUpdateTimes
) {
124 panic("IOStateReporter::_swap* already in use");
127 // new currentStates buffer
128 PREFL_MEMOP_FAIL(newNChannels
, int);
129 newCurStatesSize
= (size_t)newNChannels
* sizeof(int);
130 _swapCurrentStates
= (int*)IOMalloc(newCurStatesSize
);
131 if (_swapCurrentStates
== NULL
) {
132 res
= kIOReturnNoMemory
; goto finish
;
134 memset(_swapCurrentStates
, -1, newCurStatesSize
); // init w/"no state"
136 // new timestamps buffer
137 PREFL_MEMOP_FAIL(newNChannels
, uint64_t);
138 newTSSize
= (size_t)newNChannels
* sizeof(uint64_t);
139 _swapLastUpdateTimes
= (uint64_t *)IOMalloc(newTSSize
);
140 if (_swapLastUpdateTimes
== NULL
) {
141 res
= kIOReturnNoMemory
; goto finish
;
143 memset(_swapLastUpdateTimes
, 0, newTSSize
);
145 res
= super::handleSwapPrepare(newNChannels
);
149 if (_swapCurrentStates
) {
150 IOFree(_swapCurrentStates
, newCurStatesSize
);
151 _swapCurrentStates
= NULL
;
153 if (_swapLastUpdateTimes
) {
154 IOFree(_swapLastUpdateTimes
, newTSSize
);
155 _swapLastUpdateTimes
= NULL
;
163 IOStateReporter::handleAddChannelSwap(uint64_t channelID
,
164 const OSSymbol
*symChannelName
)
166 IOReturn res
= kIOReturnError
;
169 uint64_t *tmpTimestamps
;
170 bool swapComplete
= false;
172 //IORLOG("IOStateReporter::handleSwap");
174 if (!_swapCurrentStates
|| !_swapLastUpdateTimes
) {
175 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
179 IOREPORTER_CHECK_CONFIG_LOCK();
180 IOREPORTER_CHECK_LOCK();
182 // Copy any existing buffers
183 if (_currentStates
) {
184 PREFL_MEMOP_FAIL(_nChannels
, int);
185 memcpy(_swapCurrentStates
, _currentStates
,
186 (size_t)_nChannels
* sizeof(int));
188 if (!_lastUpdateTimes
) {
189 panic("IOStateReporter::handleAddChannelSwap _lastUpdateTimes unset despite non-NULL _currentStates");
191 PREFL_MEMOP_FAIL(_nChannels
, uint64_t);
192 memcpy(_swapLastUpdateTimes
, _lastUpdateTimes
,
193 (size_t)_nChannels
* sizeof(uint64_t));
196 // Update principal instance variables, keep old values in _swap* for cleanup
197 tmpCurStates
= _currentStates
;
198 _currentStates
= _swapCurrentStates
;
199 _swapCurrentStates
= tmpCurStates
;
201 tmpTimestamps
= _lastUpdateTimes
;
202 _lastUpdateTimes
= _swapLastUpdateTimes
;
203 _swapLastUpdateTimes
= tmpTimestamps
;
209 // invoke superclass(es): base class updates _nChannels & _nElements
210 res
= super::handleAddChannelSwap(channelID
, symChannelName
);
212 IORLOG("handleSwap(state) ERROR super::handleSwap failed!");
216 // Channel added successfully, initialize the new channel's state_ids to 0..nStates-1
217 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
218 handleSetStateID(channelID
, cnt
, (uint64_t)cnt
);
222 if (res
&& swapComplete
) {
223 // unswap so the unused buffers get cleaned up
224 tmpCurStates
= _currentStates
;
225 _currentStates
= _swapCurrentStates
;
226 _swapCurrentStates
= tmpCurStates
;
228 tmpTimestamps
= _lastUpdateTimes
;
229 _lastUpdateTimes
= _swapLastUpdateTimes
;
230 _swapLastUpdateTimes
= tmpTimestamps
;
238 IOStateReporter::handleSwapCleanup(int swapNChannels
)
240 IOREPORTER_CHECK_CONFIG_LOCK();
242 super::handleSwapCleanup(swapNChannels
);
244 if (_swapCurrentStates
) {
245 PREFL_MEMOP_PANIC(swapNChannels
, int);
246 IOFree(_swapCurrentStates
, (size_t)swapNChannels
* sizeof(int));
247 _swapCurrentStates
= NULL
;
249 if (_swapLastUpdateTimes
) {
250 PREFL_MEMOP_PANIC(swapNChannels
, uint64_t);
251 IOFree(_swapLastUpdateTimes
, (size_t)swapNChannels
* sizeof(uint64_t));
252 _swapLastUpdateTimes
= NULL
;
258 IOStateReporter::_getStateIndices(uint64_t channel_id
,
263 IOReturn res
= kIOReturnError
;
265 IOStateReportValues
*values
;
266 int element_index
= 0;
268 IOREPORTER_CHECK_LOCK();
270 if (getChannelIndices(channel_id
,
272 &element_index
) != kIOReturnSuccess
) {
273 res
= kIOReturnBadArgument
;
278 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
279 values
= (IOStateReportValues
*)getElementValues(element_index
+ cnt
);
281 if (values
== NULL
) {
282 res
= kIOReturnError
;
286 if (values
->state_id
== state_id
) {
288 res
= kIOReturnSuccess
;
293 res
= kIOReturnBadArgument
;
301 IOStateReporter::setChannelState(uint64_t channel_id
,
302 uint64_t new_state_id
)
304 IOReturn res
= kIOReturnError
;
305 int channel_index
, new_state_index
;
306 uint64_t last_intransition
= 0;
307 uint64_t prev_state_residency
= 0;
311 if (_getStateIndices(channel_id
, new_state_id
, &channel_index
, &new_state_index
) == kIOReturnSuccess
) {
312 res
= handleSetStateByIndices(channel_index
, new_state_index
,
314 prev_state_residency
);
318 res
= kIOReturnBadArgument
;
326 IOStateReporter::setChannelState(uint64_t channel_id
,
327 uint64_t new_state_id
,
328 uint64_t last_intransition
,
329 uint64_t prev_state_residency
)
331 return setChannelState(channel_id
, new_state_id
);
335 IOStateReporter::overrideChannelState(uint64_t channel_id
,
337 uint64_t time_in_state
,
338 uint64_t intransitions
,
339 uint64_t last_intransition
/*=0*/)
341 IOReturn res
= kIOReturnError
;
342 int channel_index
, state_index
;
346 if (_getStateIndices(channel_id
, state_id
, &channel_index
, &state_index
) == kIOReturnSuccess
) {
347 if (_lastUpdateTimes
[channel_index
]) {
348 panic("overrideChannelState() cannot be used after setChannelState()!\n");
351 res
= handleOverrideChannelStateByIndices(channel_index
, state_index
,
352 time_in_state
, intransitions
,
357 res
= kIOReturnBadArgument
;
366 IOStateReporter::handleOverrideChannelStateByIndices(int channel_index
,
368 uint64_t time_in_state
,
369 uint64_t intransitions
,
370 uint64_t last_intransition
/*=0*/)
372 IOReturn kerr
, result
= kIOReturnError
;
373 IOStateReportValues state_values
;
376 if (channel_index
< 0 || channel_index
>= _nChannels
) {
377 result
= kIOReturnBadArgument
; goto finish
;
380 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
381 / _channelDimension
) {
382 result
= kIOReturnOverrun
; goto finish
;
384 element_index
= channel_index
* _channelDimension
+ state_index
;
386 kerr
= copyElementValues(element_index
, (IOReportElementValues
*)&state_values
);
388 result
= kerr
; goto finish
;
391 // last_intransition = 0 -> no current state ("residency summary only")
392 state_values
.last_intransition
= last_intransition
;
393 state_values
.intransitions
= intransitions
;
394 state_values
.upticks
= time_in_state
;
396 // determines current time for metadata
397 kerr
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
399 result
= kerr
; goto finish
;
403 result
= kIOReturnSuccess
;
411 IOStateReporter::incrementChannelState(uint64_t channel_id
,
413 uint64_t time_in_state
,
414 uint64_t intransitions
,
415 uint64_t last_intransition
/*=0*/)
417 IOReturn res
= kIOReturnError
;
418 int channel_index
, state_index
;
422 if (_getStateIndices(channel_id
, state_id
, &channel_index
, &state_index
) == kIOReturnSuccess
) {
423 if (_lastUpdateTimes
[channel_index
]) {
424 panic("incrementChannelState() cannot be used after setChannelState()!\n");
427 res
= handleIncrementChannelStateByIndices(channel_index
, state_index
,
428 time_in_state
, intransitions
,
433 res
= kIOReturnBadArgument
;
442 IOStateReporter::handleIncrementChannelStateByIndices(int channel_index
,
444 uint64_t time_in_state
,
445 uint64_t intransitions
,
446 uint64_t last_intransition
/*=0*/)
448 IOReturn kerr
, result
= kIOReturnError
;
449 IOStateReportValues state_values
;
452 if (channel_index
< 0 || channel_index
>= _nChannels
) {
453 result
= kIOReturnBadArgument
; goto finish
;
456 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
457 / _channelDimension
) {
458 result
= kIOReturnOverrun
; goto finish
;
460 element_index
= channel_index
* _channelDimension
+ state_index
;
462 kerr
= copyElementValues(element_index
, (IOReportElementValues
*)&state_values
);
468 state_values
.last_intransition
= last_intransition
;
469 state_values
.intransitions
+= intransitions
;
470 state_values
.upticks
+= time_in_state
;
472 // determines current time for metadata
473 kerr
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
480 result
= kIOReturnSuccess
;
488 IOStateReporter::setState(uint64_t new_state_id
)
490 uint64_t last_intransition
= 0;
491 uint64_t prev_state_residency
= 0;
492 IOReturn res
= kIOReturnError
;
493 IOStateReportValues
*values
;
494 int channel_index
= 0, element_index
= 0, new_state_index
= 0;
499 if (_nChannels
== 1) {
500 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
501 new_state_index
= element_index
+ cnt
;
503 values
= (IOStateReportValues
*)getElementValues(new_state_index
);
505 if (values
== NULL
) {
506 res
= kIOReturnError
;
510 if (values
->state_id
== new_state_id
) {
511 res
= handleSetStateByIndices(channel_index
, new_state_index
,
513 prev_state_residency
);
519 res
= kIOReturnBadArgument
;
527 IOStateReporter::setState(uint64_t new_state_id
,
528 uint64_t last_intransition
,
529 uint64_t prev_state_residency
)
531 return setState(new_state_id
);
535 IOStateReporter::setStateID(uint64_t channel_id
,
539 IOReturn res
= kIOReturnError
;
543 res
= handleSetStateID(channel_id
, state_index
, state_id
);
552 IOStateReporter::handleSetStateID(uint64_t channel_id
,
556 IOReturn res
= kIOReturnError
;
557 IOStateReportValues state_values
;
558 int element_index
= 0;
560 IOREPORTER_CHECK_LOCK();
562 if (getFirstElementIndex(channel_id
, &element_index
) == kIOReturnSuccess
) {
563 if (state_index
>= _channelDimension
) {
564 res
= kIOReturnBadArgument
; goto finish
;
566 if (_nElements
- state_index
<= element_index
) {
567 res
= kIOReturnOverrun
; goto finish
;
569 element_index
+= state_index
;
571 if (copyElementValues(element_index
, (IOReportElementValues
*)&state_values
) != kIOReturnSuccess
) {
572 res
= kIOReturnBadArgument
;
576 state_values
.state_id
= state_id
;
578 res
= setElementValues(element_index
, (IOReportElementValues
*)&state_values
);
581 // FIXME: set a bit somewhere (reporter-wide?) that state_ids can no longer be
582 // assumed to be contiguous
588 IOStateReporter::setStateByIndices(int channel_index
,
591 IOReturn res
= kIOReturnError
;
592 uint64_t last_intransition
= 0;
593 uint64_t prev_state_residency
= 0;
597 res
= handleSetStateByIndices(channel_index
, new_state_index
,
598 last_intransition
, prev_state_residency
);
606 IOStateReporter::setStateByIndices(int channel_index
,
608 uint64_t last_intransition
,
609 uint64_t prev_state_residency
)
611 return setStateByIndices(channel_index
, new_state_index
);
615 IOStateReporter::handleSetStateByIndices(int channel_index
,
617 uint64_t last_intransition
,
618 uint64_t prev_state_residency
)
620 IOReturn res
= kIOReturnError
;
622 IOStateReportValues curr_state_values
, new_state_values
;
623 int curr_state_index
= 0;
624 int curr_element_index
, new_element_index
;
625 uint64_t last_ch_update_time
= 0;
626 uint64_t recordTime
= mach_absolute_time();
628 IOREPORTER_CHECK_LOCK();
630 if (channel_index
< 0 || channel_index
>= _nChannels
) {
631 res
= kIOReturnBadArgument
; goto finish
;
634 // if no timestamp provided, last_intransition = time of recording (now)
635 if (last_intransition
== 0) {
636 last_intransition
= recordTime
;
639 // First update target state if different than the current state
640 // _currentStates[] initialized to -1 to detect first state transition
641 curr_state_index
= _currentStates
[channel_index
];
642 if (new_state_index
!= curr_state_index
) {
643 // fetch element data
644 if (channel_index
< 0 || channel_index
> (_nElements
- new_state_index
)
645 / _channelDimension
) {
646 res
= kIOReturnOverrun
; goto finish
;
648 new_element_index
= channel_index
* _channelDimension
+ new_state_index
;
649 if (copyElementValues(new_element_index
,
650 (IOReportElementValues
*)&new_state_values
)) {
651 res
= kIOReturnBadArgument
;
655 // Update new state's transition info
656 new_state_values
.intransitions
+= 1;
657 new_state_values
.last_intransition
= last_intransition
;
659 // and store the values
660 res
= setElementValues(new_element_index
,
661 (IOReportElementValues
*)&new_state_values
,
664 if (res
!= kIOReturnSuccess
) {
668 _currentStates
[channel_index
] = new_state_index
;
671 /* Now update time spent in any previous state
672 * If new_state_index = curr_state_index, this updates time in the
673 * current state. If this is the channel's first state transition,
674 * the last update time will be zero.
676 * Note: While setState() should never be called on a channel being
677 * updated with increment/overrideChannelState(), that's another way
678 * that the last update time might not exist. Regardless, if there
679 * is no basis for determining time spent in previous state, there's
682 last_ch_update_time
= _lastUpdateTimes
[channel_index
];
683 if (last_ch_update_time
!= 0) {
684 if (channel_index
< 0 || channel_index
> (_nElements
- curr_state_index
)
685 / _channelDimension
) {
686 res
= kIOReturnOverrun
; goto finish
;
688 curr_element_index
= channel_index
* _channelDimension
+ curr_state_index
;
689 if (copyElementValues(curr_element_index
,
690 (IOReportElementValues
*)&curr_state_values
)) {
691 res
= kIOReturnBadArgument
;
694 // compute the time spent in previous state, unless provided
695 if (prev_state_residency
== 0) {
696 prev_state_residency
= last_intransition
- last_ch_update_time
;
699 curr_state_values
.upticks
+= prev_state_residency
;
701 res
= setElementValues(curr_element_index
,
702 (IOReportElementValues
*)&curr_state_values
,
705 if (res
!= kIOReturnSuccess
) {
710 // record basis for next "time in prior state" calculation
711 // (also arms a panic in override/incrementChannelState())
712 _lastUpdateTimes
[channel_index
] = last_intransition
;
719 // blocks might make this slightly easier?
721 IOStateReporter::getStateInTransitions(uint64_t channel_id
,
724 return _getStateValue(channel_id
, state_id
, kInTransitions
);
728 IOStateReporter::getStateResidencyTime(uint64_t channel_id
,
731 return _getStateValue(channel_id
, state_id
, kResidencyTime
);
735 IOStateReporter::getStateLastTransitionTime(uint64_t channel_id
,
738 return _getStateValue(channel_id
, state_id
, kLastTransitionTime
);
742 IOStateReporter::_getStateValue(uint64_t channel_id
,
744 enum valueSelector value
)
746 int channel_index
= 0, element_index
= 0, cnt
;
747 IOStateReportValues
*values
= NULL
;
748 uint64_t result
= kIOReportInvalidValue
;
752 if (getChannelIndices(channel_id
, &channel_index
, &element_index
) == kIOReturnSuccess
) {
753 if (updateChannelValues(channel_index
) == kIOReturnSuccess
) {
754 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
755 values
= (IOStateReportValues
*)getElementValues(element_index
);
757 if (state_id
== values
->state_id
) {
760 result
= values
->intransitions
;
763 result
= values
->upticks
;
765 case kLastTransitionTime
:
766 result
= values
->last_intransition
;
786 IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id
)
789 uint64_t result
= kIOReportInvalidValue
;
793 if (getChannelIndex(channel_id
, &channel_index
) == kIOReturnSuccess
) {
794 result
= _lastUpdateTimes
[channel_index
];
803 /* updateChannelValues() is called to refresh state before being
804 * reported outside the reporter. In the case of IOStateReporter,
805 * this is primarily an update to the "time in state" data.
808 IOStateReporter::updateChannelValues(int channel_index
)
810 IOReturn kerr
, result
= kIOReturnError
;
812 int state_index
, element_idx
;
813 uint64_t currentTime
;
814 uint64_t last_ch_update_time
;
815 uint64_t time_in_state
;
816 IOStateReportValues state_values
;
818 IOREPORTER_CHECK_LOCK();
820 if (channel_index
< 0 || channel_index
>= _nChannels
) {
821 result
= kIOReturnBadArgument
; goto finish
;
824 /* First check to see whether this channel has begun self-
825 * calculation of time in state. It's possible this channel
826 * has yet to be initialized or that the driver is updating
827 * the channel with override/incrementChannelState() which
828 * never enable automatic time-in-state updates. In that case,
829 * there is nothing to update and we return success.
831 last_ch_update_time
= _lastUpdateTimes
[channel_index
];
832 if (last_ch_update_time
== 0) {
833 result
= kIOReturnSuccess
; goto finish
;
836 // figure out the current state (if any)
837 state_index
= _currentStates
[channel_index
];
839 // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
840 if (channel_index
< 0 || channel_index
> (_nElements
- state_index
)
841 / _channelDimension
) {
842 result
= kIOReturnOverrun
; goto finish
;
844 element_idx
= channel_index
* _channelDimension
+ state_index
;
846 // get the current values
847 kerr
= copyElementValues(element_idx
, (IOReportElementValues
*)&state_values
);
849 result
= kerr
; goto finish
;
852 // calculate time in state
853 currentTime
= mach_absolute_time();
854 time_in_state
= currentTime
- last_ch_update_time
;
855 state_values
.upticks
+= time_in_state
;
857 // and store the values
858 kerr
= setElementValues(element_idx
,
859 (IOReportElementValues
*)&state_values
,
862 result
= kerr
; goto finish
;
865 // Record basis for next "prior time" calculation
866 _lastUpdateTimes
[channel_index
] = currentTime
;
870 result
= kIOReturnSuccess
;