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