1/*
2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#define IOKIT_ENABLE_SHARED_PTR
30
31#include <libkern/c++/OSSharedPtr.h>
32#include <IOKit/IOKernelReportStructs.h>
33#include <IOKit/IOKernelReporters.h>
34#include "IOReporterDefs.h"
35
36
37#define super IOReporter
38OSDefineMetaClassAndStructors(IOStateReporter, IOReporter);
39
40
41/* static */
42OSSharedPtr<IOStateReporter>
43IOStateReporter::with(IOService *reportingService,
44 IOReportCategories categories,
45 int nstates,
46 IOReportUnit unit /* = kIOReportUnitHWTicks*/)
47{
48 OSSharedPtr<IOStateReporter> reporter;
49
50 if (nstates > INT16_MAX) {
51 return nullptr;
52 }
53
54 reporter = OSMakeShared<IOStateReporter>();
55 if (!reporter) {
56 return nullptr;
57 }
58
59 if (!reporter->initWith(reportingService, categories, nstates: (int16_t) nstates, unit)) {
60 return nullptr;
61 }
62
63 return reporter;
64}
65
66bool
67IOStateReporter::initWith(IOService *reportingService,
68 IOReportCategories categories,
69 int16_t nstates,
70 IOReportUnit unit)
71{
72 bool success = false;
73
74 IOReportChannelType channelType = {
75 .categories = categories,
76 .report_format = kIOReportFormatState,
77 .nelements = static_cast<uint16_t>(nstates),
78 .element_idx = 0
79 };
80
81 if (super::init(reportingService, channelType, unit) != true) {
82 IORLOG("ERROR super::initWith failed");
83 success = false;
84 goto finish;
85 }
86
87 _currentStates = NULL;
88 _lastUpdateTimes = NULL;
89
90 success = true;
91
92finish:
93 return success;
94}
95
96
97void
98IOStateReporter::free(void)
99{
100 if (_currentStates) {
101 PREFL_MEMOP_PANIC(_nChannels, int);
102 IOFreeData(address: _currentStates, size: (size_t)_nChannels * sizeof(int));
103 }
104 if (_lastUpdateTimes) {
105 PREFL_MEMOP_PANIC(_nChannels, uint64_t);
106 IOFreeData(address: _lastUpdateTimes, size: (size_t)_nChannels * sizeof(uint64_t));
107 }
108
109 super::free();
110}
111
112
113IOReturn
114IOStateReporter::handleSwapPrepare(int newNChannels)
115{
116 IOReturn res = kIOReturnError;
117 size_t newCurStatesSize, newTSSize;
118
119 //IORLOG("handleSwapPrepare (state) _nChannels before = %u", _nChannels);
120
121 IOREPORTER_CHECK_CONFIG_LOCK();
122
123 if (_swapCurrentStates || _swapLastUpdateTimes) {
124 panic("IOStateReporter::_swap* already in use");
125 }
126
127 // new currentStates buffer
128 PREFL_MEMOP_FAIL(newNChannels, int);
129 newCurStatesSize = (size_t)newNChannels * sizeof(int);
130 _swapCurrentStates = (int*)IOMallocData(newCurStatesSize);
131 if (_swapCurrentStates == NULL) {
132 res = kIOReturnNoMemory; goto finish;
133 }
134 memset(s: _swapCurrentStates, c: -1, n: newCurStatesSize); // init w/"no state"
135
136 // new timestamps buffer
137 PREFL_MEMOP_FAIL(newNChannels, uint64_t);
138 newTSSize = (size_t)newNChannels * sizeof(uint64_t);
139 _swapLastUpdateTimes = (uint64_t *)IOMallocZeroData(newTSSize);
140 if (_swapLastUpdateTimes == NULL) {
141 res = kIOReturnNoMemory; goto finish;
142 }
143
144 res = super::handleSwapPrepare(newNChannels);
145
146finish:
147 if (res) {
148 if (_swapCurrentStates) {
149 IOFreeData(address: _swapCurrentStates, size: newCurStatesSize);
150 _swapCurrentStates = NULL;
151 }
152 if (_swapLastUpdateTimes) {
153 IOFreeData(address: _swapLastUpdateTimes, size: newTSSize);
154 _swapLastUpdateTimes = NULL;
155 }
156 }
157
158 return res;
159}
160
161IOReturn
162IOStateReporter::handleAddChannelSwap(uint64_t channelID,
163 const OSSymbol *symChannelName)
164{
165 IOReturn res = kIOReturnError;
166 int cnt;
167 int *tmpCurStates;
168 uint64_t *tmpTimestamps;
169 bool swapComplete = false;
170
171 //IORLOG("IOStateReporter::handleSwap");
172
173 if (!_swapCurrentStates || !_swapLastUpdateTimes) {
174 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
175 goto finish;
176 }
177
178 IOREPORTER_CHECK_CONFIG_LOCK();
179 IOREPORTER_CHECK_LOCK();
180
181 // Copy any existing buffers
182 if (_currentStates) {
183 PREFL_MEMOP_FAIL(_nChannels, int);
184 memcpy(dst: _swapCurrentStates, src: _currentStates,
185 n: (size_t)_nChannels * sizeof(int));
186
187 if (!_lastUpdateTimes) {
188 panic("IOStateReporter::handleAddChannelSwap _lastUpdateTimes unset despite non-NULL _currentStates");
189 }
190 PREFL_MEMOP_FAIL(_nChannels, uint64_t);
191 memcpy(dst: _swapLastUpdateTimes, src: _lastUpdateTimes,
192 n: (size_t)_nChannels * sizeof(uint64_t));
193 }
194
195 // Update principal instance variables, keep old values in _swap* for cleanup
196 tmpCurStates = _currentStates;
197 _currentStates = _swapCurrentStates;
198 _swapCurrentStates = tmpCurStates;
199
200 tmpTimestamps = _lastUpdateTimes;
201 _lastUpdateTimes = _swapLastUpdateTimes;
202 _swapLastUpdateTimes = tmpTimestamps;
203
204 swapComplete = true;
205
206 // subclass success
207
208 // invoke superclass(es): base class updates _nChannels & _nElements
209 res = super::handleAddChannelSwap(channel_id: channelID, symChannelName);
210 if (res) {
211 IORLOG("handleSwap(state) ERROR super::handleSwap failed!");
212 goto finish;
213 }
214
215 // Channel added successfully, initialize the new channel's state_ids to 0..nStates-1
216 for (cnt = 0; cnt < _channelDimension; cnt++) {
217 handleSetStateID(channel_id: channelID, state_index: cnt, state_id: (uint64_t)cnt);
218 }
219
220finish:
221 if (res && swapComplete) {
222 // unswap so the unused buffers get cleaned up
223 tmpCurStates = _currentStates;
224 _currentStates = _swapCurrentStates;
225 _swapCurrentStates = tmpCurStates;
226
227 tmpTimestamps = _lastUpdateTimes;
228 _lastUpdateTimes = _swapLastUpdateTimes;
229 _swapLastUpdateTimes = tmpTimestamps;
230 }
231
232 return res;
233}
234
235
236void
237IOStateReporter::handleSwapCleanup(int swapNChannels)
238{
239 IOREPORTER_CHECK_CONFIG_LOCK();
240
241 super::handleSwapCleanup(swapNChannels);
242
243 if (_swapCurrentStates) {
244 PREFL_MEMOP_PANIC(swapNChannels, int);
245 IOFreeData(address: _swapCurrentStates, size: (size_t)swapNChannels * sizeof(int));
246 _swapCurrentStates = NULL;
247 }
248 if (_swapLastUpdateTimes) {
249 PREFL_MEMOP_PANIC(swapNChannels, uint64_t);
250 IOFreeData(address: _swapLastUpdateTimes, size: (size_t)swapNChannels * sizeof(uint64_t));
251 _swapLastUpdateTimes = NULL;
252 }
253}
254
255
256IOReturn
257IOStateReporter::_getStateIndices(uint64_t channel_id,
258 uint64_t state_id,
259 int *channel_index,
260 int *state_index)
261{
262 IOReturn res = kIOReturnError;
263 int cnt;
264 IOStateReportValues *values;
265 int element_index = 0;
266
267 IOREPORTER_CHECK_LOCK();
268
269 if (getChannelIndices(channel_id,
270 channel_index,
271 element_index: &element_index) != kIOReturnSuccess) {
272 res = kIOReturnBadArgument;
273
274 goto finish;
275 }
276
277 for (cnt = 0; cnt < _channelDimension; cnt++) {
278 values = (IOStateReportValues *)getElementValues(element_index: element_index + cnt);
279
280 if (values == NULL) {
281 res = kIOReturnError;
282 goto finish;
283 }
284
285 if (values->state_id == state_id) {
286 *state_index = cnt;
287 res = kIOReturnSuccess;
288 goto finish;
289 }
290 }
291
292 res = kIOReturnBadArgument;
293
294finish:
295 return res;
296}
297
298
299IOReturn
300IOStateReporter::setChannelState(uint64_t channel_id,
301 uint64_t new_state_id)
302{
303 IOReturn res = kIOReturnError;
304 int channel_index, new_state_index;
305 uint64_t last_intransition = 0;
306 uint64_t prev_state_residency = 0;
307
308 lockReporter();
309
310 if (_getStateIndices(channel_id, state_id: new_state_id, channel_index: &channel_index, state_index: &new_state_index) == kIOReturnSuccess) {
311 res = handleSetStateByIndices(channel_index, new_state_index,
312 last_intransition,
313 prev_state_residency);
314 goto finish;
315 }
316
317 res = kIOReturnBadArgument;
318
319finish:
320 unlockReporter();
321 return res;
322}
323
324IOReturn
325IOStateReporter::setChannelState(uint64_t channel_id,
326 uint64_t new_state_id,
327 uint64_t last_intransition,
328 uint64_t prev_state_residency)
329{
330 return setChannelState(channel_id, new_state_id);
331}
332
333IOReturn
334IOStateReporter::overrideChannelState(uint64_t channel_id,
335 uint64_t state_id,
336 uint64_t time_in_state,
337 uint64_t intransitions,
338 uint64_t last_intransition /*=0*/)
339{
340 IOReturn res = kIOReturnError;
341 int channel_index, state_index;
342
343 lockReporter();
344
345 if (_getStateIndices(channel_id, state_id, channel_index: &channel_index, state_index: &state_index) == kIOReturnSuccess) {
346 if (_lastUpdateTimes[channel_index]) {
347 panic("overrideChannelState() cannot be used after setChannelState()!");
348 }
349
350 res = handleOverrideChannelStateByIndices(channel_index, state_index,
351 time_in_state, intransitions,
352 last_intransition);
353 goto finish;
354 }
355
356 res = kIOReturnBadArgument;
357
358finish:
359 unlockReporter();
360 return res;
361}
362
363
364IOReturn
365IOStateReporter::handleOverrideChannelStateByIndices(int channel_index,
366 int state_index,
367 uint64_t time_in_state,
368 uint64_t intransitions,
369 uint64_t last_intransition /*=0*/)
370{
371 IOReturn kerr, result = kIOReturnError;
372 IOStateReportValues state_values;
373 int element_index;
374
375 if (channel_index < 0 || channel_index >= _nChannels) {
376 result = kIOReturnBadArgument; goto finish;
377 }
378
379 if (channel_index < 0 || channel_index > (_nElements - state_index)
380 / _channelDimension) {
381 result = kIOReturnOverrun; goto finish;
382 }
383 element_index = channel_index * _channelDimension + state_index;
384
385 kerr = copyElementValues(element_index, elementValues: (IOReportElementValues*)&state_values);
386 if (kerr) {
387 result = kerr; goto finish;
388 }
389
390 // last_intransition = 0 -> no current state ("residency summary only")
391 state_values.last_intransition = last_intransition;
392 state_values.intransitions = intransitions;
393 state_values.upticks = time_in_state;
394
395 // determines current time for metadata
396 kerr = setElementValues(element_index, values: (IOReportElementValues *)&state_values);
397 if (kerr) {
398 result = kerr; goto finish;
399 }
400
401 // success
402 result = kIOReturnSuccess;
403
404finish:
405 return result;
406}
407
408
409IOReturn
410IOStateReporter::incrementChannelState(uint64_t channel_id,
411 uint64_t state_id,
412 uint64_t time_in_state,
413 uint64_t intransitions,
414 uint64_t last_intransition /*=0*/)
415{
416 IOReturn res = kIOReturnError;
417 int channel_index, state_index;
418
419 lockReporter();
420
421 if (_getStateIndices(channel_id, state_id, channel_index: &channel_index, state_index: &state_index) == kIOReturnSuccess) {
422 if (_lastUpdateTimes[channel_index]) {
423 panic("incrementChannelState() cannot be used after setChannelState()!");
424 }
425
426 res = handleIncrementChannelStateByIndices(channel_index, state_index,
427 time_in_state, intransitions,
428 last_intransition);
429 goto finish;
430 }
431
432 res = kIOReturnBadArgument;
433
434finish:
435 unlockReporter();
436 return res;
437}
438
439
440IOReturn
441IOStateReporter::handleIncrementChannelStateByIndices(int channel_index,
442 int state_index,
443 uint64_t time_in_state,
444 uint64_t intransitions,
445 uint64_t last_intransition /*=0*/)
446{
447 IOReturn kerr, result = kIOReturnError;
448 IOStateReportValues state_values;
449 int element_index;
450
451 if (channel_index < 0 || channel_index >= _nChannels) {
452 result = kIOReturnBadArgument; goto finish;
453 }
454
455 if (channel_index < 0 || channel_index > (_nElements - state_index)
456 / _channelDimension) {
457 result = kIOReturnOverrun; goto finish;
458 }
459 element_index = channel_index * _channelDimension + state_index;
460
461 kerr = copyElementValues(element_index, elementValues: (IOReportElementValues*)&state_values);
462 if (kerr) {
463 result = kerr;
464 goto finish;
465 }
466
467 state_values.last_intransition = last_intransition;
468 state_values.intransitions += intransitions;
469 state_values.upticks += time_in_state;
470
471 // determines current time for metadata
472 kerr = setElementValues(element_index, values: (IOReportElementValues *)&state_values);
473 if (kerr) {
474 result = kerr;
475 goto finish;
476 }
477
478 // success
479 result = kIOReturnSuccess;
480
481finish:
482 return result;
483}
484
485
486IOReturn
487IOStateReporter::setState(uint64_t new_state_id)
488{
489 uint64_t last_intransition = 0;
490 uint64_t prev_state_residency = 0;
491 IOReturn res = kIOReturnError;
492 IOStateReportValues *values;
493 int channel_index = 0, element_index = 0, new_state_index = 0;
494 int cnt;
495
496 lockReporter();
497
498 if (_nChannels == 1) {
499 for (cnt = 0; cnt < _channelDimension; cnt++) {
500 new_state_index = element_index + cnt;
501
502 values = (IOStateReportValues *)getElementValues(element_index: new_state_index);
503
504 if (values == NULL) {
505 res = kIOReturnError;
506 goto finish;
507 }
508
509 if (values->state_id == new_state_id) {
510 res = handleSetStateByIndices(channel_index, new_state_index,
511 last_intransition,
512 prev_state_residency);
513 goto finish;
514 }
515 }
516 }
517
518 res = kIOReturnBadArgument;
519
520finish:
521 unlockReporter();
522 return res;
523}
524
525IOReturn
526IOStateReporter::setState(uint64_t new_state_id,
527 uint64_t last_intransition,
528 uint64_t prev_state_residency)
529{
530 return setState(new_state_id);
531}
532
533IOReturn
534IOStateReporter::setStateID(uint64_t channel_id,
535 int state_index,
536 uint64_t state_id)
537{
538 IOReturn res = kIOReturnError;
539
540 lockReporter();
541
542 res = handleSetStateID(channel_id, state_index, state_id);
543
544 unlockReporter();
545
546 return res;
547}
548
549
550IOReturn
551IOStateReporter::handleSetStateID(uint64_t channel_id,
552 int state_index,
553 uint64_t state_id)
554{
555 IOReturn res = kIOReturnError;
556 IOStateReportValues state_values;
557 int element_index = 0;
558
559 IOREPORTER_CHECK_LOCK();
560
561 if (getFirstElementIndex(channel_id, element_index: &element_index) == kIOReturnSuccess) {
562 if (state_index >= _channelDimension) {
563 res = kIOReturnBadArgument; goto finish;
564 }
565 if (_nElements - state_index <= element_index) {
566 res = kIOReturnOverrun; goto finish;
567 }
568 element_index += state_index;
569
570 if (copyElementValues(element_index, elementValues: (IOReportElementValues *)&state_values) != kIOReturnSuccess) {
571 res = kIOReturnBadArgument;
572 goto finish;
573 }
574
575 state_values.state_id = state_id;
576
577 res = setElementValues(element_index, values: (IOReportElementValues *)&state_values);
578 }
579
580 // FIXME: set a bit somewhere (reporter-wide?) that state_ids can no longer be
581 // assumed to be contiguous
582finish:
583 return res;
584}
585
586IOReturn
587IOStateReporter::setStateByIndices(int channel_index,
588 int new_state_index)
589{
590 IOReturn res = kIOReturnError;
591 uint64_t last_intransition = 0;
592 uint64_t prev_state_residency = 0;
593
594 lockReporter();
595
596 res = handleSetStateByIndices(channel_index, new_state_index,
597 last_intransition, prev_state_residency);
598
599 unlockReporter();
600
601 return res;
602}
603
604IOReturn
605IOStateReporter::setStateByIndices(int channel_index,
606 int new_state_index,
607 uint64_t last_intransition,
608 uint64_t prev_state_residency)
609{
610 return setStateByIndices(channel_index, new_state_index);
611}
612
613IOReturn
614IOStateReporter::handleSetStateByIndices(int channel_index,
615 int new_state_index,
616 uint64_t last_intransition,
617 uint64_t prev_state_residency)
618{
619 IOReturn res = kIOReturnError;
620
621 IOStateReportValues curr_state_values, new_state_values;
622 int curr_state_index = 0;
623 int curr_element_index, new_element_index;
624 uint64_t last_ch_update_time = 0;
625 uint64_t recordTime = mach_absolute_time();
626
627 IOREPORTER_CHECK_LOCK();
628
629 if (channel_index < 0 || channel_index >= _nChannels) {
630 res = kIOReturnBadArgument; goto finish;
631 }
632
633 // if no timestamp provided, last_intransition = time of recording (now)
634 if (last_intransition == 0) {
635 last_intransition = recordTime;
636 }
637
638 // First update target state if different than the current state
639 // _currentStates[] initialized to -1 to detect first state transition
640 curr_state_index = _currentStates[channel_index];
641 if (new_state_index != curr_state_index) {
642 // fetch element data
643 if (channel_index < 0 || channel_index > (_nElements - new_state_index)
644 / _channelDimension) {
645 res = kIOReturnOverrun; goto finish;
646 }
647 new_element_index = channel_index * _channelDimension + new_state_index;
648 if (copyElementValues(element_index: new_element_index,
649 elementValues: (IOReportElementValues *)&new_state_values)) {
650 res = kIOReturnBadArgument;
651 goto finish;
652 }
653
654 // Update new state's transition info
655 new_state_values.intransitions += 1;
656 new_state_values.last_intransition = last_intransition;
657
658 // and store the values
659 res = setElementValues(element_index: new_element_index,
660 values: (IOReportElementValues *)&new_state_values,
661 record_time: recordTime);
662
663 if (res != kIOReturnSuccess) {
664 goto finish;
665 }
666
667 _currentStates[channel_index] = new_state_index;
668 }
669
670 /* Now update time spent in any previous state
671 * If new_state_index = curr_state_index, this updates time in the
672 * current state. If this is the channel's first state transition,
673 * the last update time will be zero.
674 *
675 * Note: While setState() should never be called on a channel being
676 * updated with increment/overrideChannelState(), that's another way
677 * that the last update time might not exist. Regardless, if there
678 * is no basis for determining time spent in previous state, there's
679 * nothing to update!
680 */
681 last_ch_update_time = _lastUpdateTimes[channel_index];
682 if (last_ch_update_time != 0) {
683 if (channel_index < 0 || channel_index > (_nElements - curr_state_index)
684 / _channelDimension) {
685 res = kIOReturnOverrun; goto finish;
686 }
687 curr_element_index = channel_index * _channelDimension + curr_state_index;
688 if (copyElementValues(element_index: curr_element_index,
689 elementValues: (IOReportElementValues *)&curr_state_values)) {
690 res = kIOReturnBadArgument;
691 goto finish;
692 }
693 // compute the time spent in previous state, unless provided
694 if (prev_state_residency == 0) {
695 prev_state_residency = last_intransition - last_ch_update_time;
696 }
697
698 curr_state_values.upticks += prev_state_residency;
699
700 res = setElementValues(element_index: curr_element_index,
701 values: (IOReportElementValues*)&curr_state_values,
702 record_time: recordTime);
703
704 if (res != kIOReturnSuccess) {
705 goto finish;
706 }
707 }
708
709 // record basis for next "time in prior state" calculation
710 // (also arms a panic in override/incrementChannelState())
711 _lastUpdateTimes[channel_index] = last_intransition;
712
713finish:
714 return res;
715}
716
717
718// blocks might make this slightly easier?
719uint64_t
720IOStateReporter::getStateInTransitions(uint64_t channel_id,
721 uint64_t state_id)
722{
723 return _getStateValue(channel_id, state_id, value: kInTransitions);
724}
725
726uint64_t
727IOStateReporter::getStateResidencyTime(uint64_t channel_id,
728 uint64_t state_id)
729{
730 return _getStateValue(channel_id, state_id, value: kResidencyTime);
731}
732
733uint64_t
734IOStateReporter::getStateLastTransitionTime(uint64_t channel_id,
735 uint64_t state_id)
736{
737 return _getStateValue(channel_id, state_id, value: kLastTransitionTime);
738}
739
740uint64_t
741IOStateReporter::_getStateValue(uint64_t channel_id,
742 uint64_t state_id,
743 enum valueSelector value)
744{
745 int channel_index = 0, element_index = 0, cnt;
746 IOStateReportValues *values = NULL;
747 uint64_t result = kIOReportInvalidValue;
748
749 lockReporter();
750
751 if (getChannelIndices(channel_id, channel_index: &channel_index, element_index: &element_index) == kIOReturnSuccess) {
752 if (updateChannelValues(channel_index) == kIOReturnSuccess) {
753 for (cnt = 0; cnt < _channelDimension; cnt++) {
754 values = (IOStateReportValues *)getElementValues(element_index);
755
756 if (state_id == values->state_id) {
757 switch (value) {
758 case kInTransitions:
759 result = values->intransitions;
760 break;
761 case kResidencyTime:
762 result = values->upticks;
763 break;
764 case kLastTransitionTime:
765 result = values->last_intransition;
766 break;
767 default:
768 break;
769 }
770
771 break;
772 }
773
774 element_index++;
775 }
776 }
777 }
778
779 unlockReporter();
780 return result;
781}
782
783
784uint64_t
785IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id)
786{
787 int channel_index;
788 uint64_t result = kIOReportInvalidValue;
789
790 lockReporter();
791
792 if (getChannelIndex(channel_id, channel_index: &channel_index) == kIOReturnSuccess) {
793 result = _lastUpdateTimes[channel_index];
794 }
795
796 unlockReporter();
797
798 return result;
799}
800
801
802/* updateChannelValues() is called to refresh state before being
803 * reported outside the reporter. In the case of IOStateReporter,
804 * this is primarily an update to the "time in state" data.
805 */
806IOReturn
807IOStateReporter::updateChannelValues(int channel_index)
808{
809 IOReturn kerr, result = kIOReturnError;
810
811 int state_index, element_idx;
812 uint64_t currentTime;
813 uint64_t last_ch_update_time;
814 uint64_t time_in_state;
815 IOStateReportValues state_values;
816
817 IOREPORTER_CHECK_LOCK();
818
819 if (channel_index < 0 || channel_index >= _nChannels) {
820 result = kIOReturnBadArgument; goto finish;
821 }
822
823 /* First check to see whether this channel has begun self-
824 * calculation of time in state. It's possible this channel
825 * has yet to be initialized or that the driver is updating
826 * the channel with override/incrementChannelState() which
827 * never enable automatic time-in-state updates. In that case,
828 * there is nothing to update and we return success.
829 */
830 last_ch_update_time = _lastUpdateTimes[channel_index];
831 if (last_ch_update_time == 0) {
832 result = kIOReturnSuccess; goto finish;
833 }
834
835 // figure out the current state (if any)
836 state_index = _currentStates[channel_index];
837
838 // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
839 if (channel_index < 0 || channel_index > (_nElements - state_index)
840 / _channelDimension) {
841 result = kIOReturnOverrun; goto finish;
842 }
843 element_idx = channel_index * _channelDimension + state_index;
844
845 // get the current values
846 kerr = copyElementValues(element_index: element_idx, elementValues: (IOReportElementValues*)&state_values);
847 if (kerr) {
848 result = kerr; goto finish;
849 }
850
851 // calculate time in state
852 currentTime = mach_absolute_time();
853 time_in_state = currentTime - last_ch_update_time;
854 state_values.upticks += time_in_state;
855
856 // and store the values
857 kerr = setElementValues(element_index: element_idx,
858 values: (IOReportElementValues *)&state_values,
859 record_time: currentTime);
860 if (kerr) {
861 result = kerr; goto finish;
862 }
863
864 // Record basis for next "prior time" calculation
865 _lastUpdateTimes[channel_index] = currentTime;
866
867
868 // success
869 result = kIOReturnSuccess;
870
871finish:
872 return result;
873}
874
875/* static */ OSPtr<IOReportLegendEntry>
876IOStateReporter::createLegend(const uint64_t *channelIDs,
877 const char **channelNames,
878 int channelCount,
879 int nstates,
880 IOReportCategories categories,
881 IOReportUnit unit)
882{
883 IOReportChannelType channelType = {
884 .categories = categories,
885 .report_format = kIOReportFormatState,
886 .nelements = static_cast<uint16_t>(nstates),
887 .element_idx = 0
888 };
889
890 return IOReporter::legendWith(channelIDs, channelNames, channelCount, channelType, unit);
891}
892