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