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