]>
Commit | Line | Data |
---|---|---|
fe8ab488 A |
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 | IOReportUnits 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 | if (reporter) delete reporter; | |
62 | } | |
63 | ||
64 | return rval; | |
65 | } | |
66 | ||
67 | bool | |
68 | IOStateReporter::initWith(IOService *reportingService, | |
69 | IOReportCategories categories, | |
70 | int16_t nstates, | |
71 | IOReportUnits 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 | default: | |
782 | break; | |
783 | } | |
784 | ||
785 | break; | |
786 | } | |
787 | ||
788 | element_index++; | |
789 | } | |
790 | } | |
791 | } | |
792 | ||
793 | unlockReporter(); | |
794 | return result; | |
795 | } | |
796 | ||
797 | ||
798 | uint64_t | |
799 | IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id) | |
800 | { | |
801 | int channel_index; | |
802 | uint64_t result = kIOReportInvalidValue; | |
803 | ||
804 | lockReporter(); | |
805 | ||
806 | if (getChannelIndex(channel_id, &channel_index) == kIOReturnSuccess) { | |
807 | ||
808 | result = _lastUpdateTimes[channel_index]; | |
809 | } | |
810 | ||
811 | unlockReporter(); | |
812 | ||
813 | return result; | |
814 | } | |
815 | ||
816 | ||
817 | /* updateChannelValues() is called to refresh state before being | |
818 | reported outside the reporter. In the case of IOStateReporter, | |
819 | this is primarily an update to the "time in state" data. | |
820 | */ | |
821 | IOReturn | |
822 | IOStateReporter::updateChannelValues(int channel_index) | |
823 | { | |
824 | IOReturn kerr, result = kIOReturnError; | |
825 | ||
826 | int state_index, element_idx; | |
827 | uint64_t currentTime; | |
828 | uint64_t last_ch_update_time; | |
829 | uint64_t time_in_state; | |
830 | IOStateReportValues state_values; | |
831 | ||
832 | IOREPORTER_CHECK_LOCK(); | |
833 | ||
834 | if (channel_index < 0 || channel_index >= _nChannels) { | |
835 | result = kIOReturnBadArgument; goto finish; | |
836 | } | |
837 | ||
838 | /* First check to see whether this channel has begun self- | |
839 | calculation of time in state. It's possible this channel | |
840 | has yet to be initialized or that the driver is updating | |
841 | the channel with override/incrementChannelState() which | |
842 | never enable automatic time-in-state updates. In that case, | |
843 | there is nothing to update and we return success. | |
844 | */ | |
845 | last_ch_update_time = _lastUpdateTimes[channel_index]; | |
846 | if (last_ch_update_time == 0) { | |
847 | result = kIOReturnSuccess; goto finish; | |
848 | } | |
849 | ||
850 | // figure out the current state (if any) | |
851 | state_index = _currentStates[channel_index]; | |
852 | ||
853 | // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15] | |
854 | if (channel_index < 0 || channel_index > (_nElements - state_index) | |
855 | / _channelDimension) { | |
856 | result = kIOReturnOverrun; goto finish; | |
857 | } | |
858 | element_idx = channel_index * _channelDimension + state_index; | |
859 | ||
860 | // get the current values | |
861 | kerr = copyElementValues(element_idx,(IOReportElementValues*)&state_values); | |
862 | if (kerr) { | |
863 | result = kerr; goto finish; | |
864 | } | |
865 | ||
866 | // calculate time in state | |
867 | currentTime = mach_absolute_time(); | |
868 | time_in_state = currentTime - last_ch_update_time; | |
869 | state_values.upticks += time_in_state; | |
870 | ||
871 | // and store the values | |
872 | kerr = setElementValues(element_idx, | |
873 | (IOReportElementValues *)&state_values, | |
874 | currentTime); | |
875 | if (kerr) { | |
876 | result = kerr; goto finish; | |
877 | } | |
878 | ||
879 | // Record basis for next "prior time" calculation | |
880 | _lastUpdateTimes[channel_index] = currentTime; | |
881 | ||
882 | ||
883 | // success | |
884 | result = kIOReturnSuccess; | |
885 | ||
886 | finish: | |
887 | return result; | |
888 | } |