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 |
38 | OSDefineMetaClassAndStructors(IOStateReporter, IOReporter); |
39 | |
40 | |
41 | /* static */ |
42 | OSSharedPtr<IOStateReporter> |
43 | IOStateReporter::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 | |
66 | bool |
67 | IOStateReporter::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 | |
92 | finish: |
93 | return success; |
94 | } |
95 | |
96 | |
97 | void |
98 | IOStateReporter::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 | |
113 | IOReturn |
114 | IOStateReporter::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 | |
146 | finish: |
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 | |
161 | IOReturn |
162 | IOStateReporter::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 | |
220 | finish: |
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 | |
236 | void |
237 | IOStateReporter::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 | |
256 | IOReturn |
257 | IOStateReporter::_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 | |
294 | finish: |
295 | return res; |
296 | } |
297 | |
298 | |
299 | IOReturn |
300 | IOStateReporter::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 | |
319 | finish: |
320 | unlockReporter(); |
321 | return res; |
322 | } |
323 | |
324 | IOReturn |
325 | IOStateReporter::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 | |
333 | IOReturn |
334 | IOStateReporter::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 | |
358 | finish: |
359 | unlockReporter(); |
360 | return res; |
361 | } |
362 | |
363 | |
364 | IOReturn |
365 | IOStateReporter::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 | |
404 | finish: |
405 | return result; |
406 | } |
407 | |
408 | |
409 | IOReturn |
410 | IOStateReporter::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 | |
434 | finish: |
435 | unlockReporter(); |
436 | return res; |
437 | } |
438 | |
439 | |
440 | IOReturn |
441 | IOStateReporter::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 | |
481 | finish: |
482 | return result; |
483 | } |
484 | |
485 | |
486 | IOReturn |
487 | IOStateReporter::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 | |
520 | finish: |
521 | unlockReporter(); |
522 | return res; |
523 | } |
524 | |
525 | IOReturn |
526 | IOStateReporter::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 | |
533 | IOReturn |
534 | IOStateReporter::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 | |
550 | IOReturn |
551 | IOStateReporter::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 |
582 | finish: |
583 | return res; |
584 | } |
585 | |
586 | IOReturn |
587 | IOStateReporter::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 | |
604 | IOReturn |
605 | IOStateReporter::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 | |
613 | IOReturn |
614 | IOStateReporter::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 | |
713 | finish: |
714 | return res; |
715 | } |
716 | |
717 | |
718 | // blocks might make this slightly easier? |
719 | uint64_t |
720 | IOStateReporter::getStateInTransitions(uint64_t channel_id, |
721 | uint64_t state_id) |
722 | { |
723 | return _getStateValue(channel_id, state_id, value: kInTransitions); |
724 | } |
725 | |
726 | uint64_t |
727 | IOStateReporter::getStateResidencyTime(uint64_t channel_id, |
728 | uint64_t state_id) |
729 | { |
730 | return _getStateValue(channel_id, state_id, value: kResidencyTime); |
731 | } |
732 | |
733 | uint64_t |
734 | IOStateReporter::getStateLastTransitionTime(uint64_t channel_id, |
735 | uint64_t state_id) |
736 | { |
737 | return _getStateValue(channel_id, state_id, value: kLastTransitionTime); |
738 | } |
739 | |
740 | uint64_t |
741 | IOStateReporter::_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 | |
784 | uint64_t |
785 | IOStateReporter::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 | */ |
806 | IOReturn |
807 | IOStateReporter::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 | |
871 | finish: |
872 | return result; |
873 | } |
874 | |
875 | /* static */ OSPtr<IOReportLegendEntry> |
876 | IOStateReporter::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 | |