1# Copyright 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging, os, re, struct, time 6 7from autotest_lib.client.bin import test 8from autotest_lib.client.bin import utils 9from autotest_lib.client.common_lib import error 10from autotest_lib.client.common_lib.cros import chrome 11from autotest_lib.client.cros import cros_logging 12 13# Kernel 3.8 to 3.14 has cur_delay_info, 3.18+ has frequency_info. 14CLOCK_PATHS = [ 15 '/sys/kernel/debug/dri/0/i915_frequency_info', 16 '/sys/kernel/debug/dri/0/i915_cur_delayinfo' 17] 18# Kernel 3.8 has i915_fbc, kernel > 3.8 i915_fbc_status. 19FBC_PATHS = [ 20 '/sys/kernel/debug/dri/0/i915_fbc', 21 '/sys/kernel/debug/dri/0/i915_fbc_status' 22] 23GEM_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_active'] 24PSR_PATHS = ['/sys/kernel/debug/dri/0/i915_edp_psr_status'] 25RC6_PATHS = ['/sys/kernel/debug/dri/0/i915_drpc_info'] 26 27 28class graphics_Idle(test.test): 29 """Class for graphics_Idle. See 'control' for details.""" 30 version = 1 31 _gpu_type = None 32 _cpu_type = None 33 _board = None 34 35 def run_once(self, arc_mode=None): 36 # We use kiosk mode to make sure Chrome is idle. 37 with chrome.Chrome( 38 logged_in=False, extra_browser_args=['--kiosk'], 39 arc_mode=arc_mode): 40 # Try to protect against runaway previous tests. 41 if not utils.wait_for_idle_cpu(20.0, 0.1): 42 logging.warning('Could not get idle CPU before running tests.') 43 self._gpu_type = utils.get_gpu_family() 44 self._cpu_type = utils.get_cpu_soc_family() 45 self._board = utils.get_board() 46 errors = '' 47 errors += self.verify_graphics_dvfs() 48 errors += self.verify_graphics_fbc() 49 errors += self.verify_graphics_psr() 50 errors += self.verify_graphics_gem_idle() 51 errors += self.verify_graphics_i915_min_clock() 52 errors += self.verify_graphics_rc6() 53 errors += self.verify_lvds_downclock() 54 errors += self.verify_short_blanking() 55 if errors: 56 raise error.TestFail('Failed: %s' % errors) 57 58 def get_valid_path(self, paths): 59 for path in paths: 60 if os.path.exists(path): 61 return path 62 logging.error('Error: %s not found.', ' '.join(paths)) 63 return None 64 65 def handle_error(self, message, path=None): 66 logging.error('Error: %s', message) 67 # For debugging show the content of the file. 68 if path is not None: 69 with open(path, 'r') as text_file: 70 logging.info('Content of %s\n%s', path, text_file.read()) 71 # Dump the output of 'top'. 72 utils.log_process_activity() 73 return message 74 75 def verify_lvds_downclock(self): 76 """On systems which support LVDS downclock, checks the kernel log for 77 a message that an LVDS downclock mode has been added.""" 78 logging.info('Running verify_lvds_downclock') 79 board = utils.get_board() 80 if not (board == 'alex' or board == 'lumpy' or board == 'stout'): 81 return '' 82 # Get the downclock message from the logs. 83 reader = cros_logging.LogReader() 84 reader.set_start_by_reboot(-1) 85 if not reader.can_find('Adding LVDS downclock mode'): 86 return self.handle_error('LVDS downclock quirk not applied. ') 87 return '' 88 89 def verify_short_blanking(self): 90 """On baytrail systems with a known panel, checks the kernel log for a 91 message that a short blanking mode has been added.""" 92 logging.info('Running verify_short_blanking') 93 if self._gpu_type != 'baytrail' or utils.has_no_monitor(): 94 return '' 95 96 # Open the EDID to find the panel model. 97 param_path = '/sys/class/drm/card0-eDP-1/edid' 98 if not os.path.exists(param_path): 99 logging.error('Error: %s not found.', param_path) 100 return self.handle_error( 101 'Short blanking not added (no EDID found). ') 102 103 with open(param_path, 'r') as edp_edid_file: 104 edp_edid_file.seek(8) 105 data = edp_edid_file.read(2) 106 manufacturer = int(struct.unpack('<H', data)[0]) 107 data = edp_edid_file.read(2) 108 product_code = int(struct.unpack('<H', data)[0]) 109 # This is not the panel we are looking for (AUO B116XTN02.2) 110 if manufacturer != 0xaf06 or product_code != 0x225c: 111 return '' 112 # Get the downclock message from the logs. 113 reader = cros_logging.LogReader() 114 reader.set_start_by_reboot(-1) 115 if not reader.can_find('Modified preferred into a short blanking mode'): 116 return self.handle_error('Short blanking not added. ') 117 return '' 118 119 def verify_graphics_rc6(self): 120 """ On systems which support RC6 (non atom), check that we are able to 121 get into rc6; idle before doing so, and retry every second for 20 122 seconds.""" 123 logging.info('Running verify_graphics_rc6') 124 # TODO(ihf): Implement on baytrail/braswell using residency counters. 125 # But note the format changed since SNB, so this will be complex. 126 if (utils.get_cpu_soc_family() == 'x86_64' and 127 self._gpu_type != 'pinetrail' and 128 self._gpu_type != 'baytrail' and self._gpu_type != 'braswell'): 129 tries = 0 130 found = False 131 param_path = self.get_valid_path(RC6_PATHS) 132 if not param_path: 133 return 'RC6_PATHS not found.' 134 while found == False and tries < 20: 135 time.sleep(1) 136 with open(param_path, 'r') as drpc_info_file: 137 for line in drpc_info_file: 138 match = re.search(r'Current RC state: (.*)', line) 139 if match and match.group(1) == 'RC6': 140 found = True 141 break 142 tries += 1 143 if not found: 144 return self.handle_error('Error: did not see the GPU in RC6.', 145 param_path) 146 return '' 147 148 def verify_graphics_i915_min_clock(self): 149 """ On i915 systems, check that we get into the lowest clock frequency; 150 idle before doing so, and retry every second for 20 seconds.""" 151 logging.info('Running verify_graphics_i915_min_clock') 152 if (utils.get_cpu_soc_family() == 'x86_64' and 153 self._gpu_type != 'pinetrail'): 154 tries = 0 155 found = False 156 param_path = self.get_valid_path(CLOCK_PATHS) 157 if not param_path: 158 return 'CLOCK_PATHS not found.' 159 while not found and tries < 80: 160 time.sleep(0.25) 161 162 with open(param_path, 'r') as delayinfo_file: 163 for line in delayinfo_file: 164 # This file has a different format depending on the 165 # board, so we parse both. Also, it would be tedious 166 # to add the minimum clock for each board, so instead 167 # we use 650MHz which is the max of the minimum clocks. 168 match = re.search(r'CAGF: (.*)MHz', line) 169 if match and int(match.group(1)) <= 650: 170 found = True 171 break 172 173 match = re.search(r'current GPU freq: (.*) MHz', line) 174 if match and int(match.group(1)) <= 650: 175 found = True 176 break 177 178 tries += 1 179 180 if not found: 181 return self.handle_error('Did not see the min i915 clock. ', 182 param_path) 183 184 return '' 185 186 def verify_graphics_dvfs(self): 187 """ On systems which support DVFS, check that we get into the lowest 188 clock frequency; idle before doing so, and retry every second for 20 189 seconds.""" 190 logging.info('Running verify_graphics_dvfs') 191 192 exynos_node = '/sys/devices/11800000.mali/' 193 rk3288_node = '/sys/devices/ffa30000.gpu/' 194 rk3399_node = '/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/' 195 mt8173_node = ('/sys/devices/soc/13000000.mfgsys-gpu/devfreq/' 196 '13000000.mfgsys-gpu/') 197 198 if self._cpu_type == 'exynos5': 199 if os.path.isdir(exynos_node): 200 node = exynos_node 201 use_devfreq = False 202 enable_node = 'dvfs' 203 enable_value = 'on' 204 else: 205 logging.error('Error: unknown exynos SoC.') 206 return self.handle_error('Unknown exynos SoC.') 207 elif self._cpu_type.startswith('rockchip'): 208 if os.path.isdir(rk3288_node): 209 node = rk3288_node 210 use_devfreq = False 211 enable_node = 'dvfs_enable' 212 enable_value = '1' 213 elif os.path.isdir(rk3399_node): 214 node = rk3399_node 215 use_devfreq = True 216 else: 217 logging.error('Error: unknown rockchip SoC.') 218 return self.handle_error('Unknown rockchip SoC.') 219 elif self._cpu_type == 'mediatek': 220 if os.path.isdir(mt8173_node): 221 node = mt8173_node 222 use_devfreq = True 223 else: 224 logging.error('Error: unknown mediatek SoC.') 225 return self.handle_error('Unknown mediatek SoC.') 226 else: 227 return '' 228 229 if use_devfreq: 230 governor_path = utils.locate_file('governor', node) 231 clock_path = utils.locate_file('cur_freq', node) 232 233 governor = utils.read_one_line(governor_path) 234 logging.info('DVFS governor = %s', governor) 235 if not governor == 'simple_ondemand': 236 logging.error('Error: DVFS governor is not simple_ondemand.') 237 return self.handle_error('Governor is wrong.') 238 else: 239 clock_path = utils.locate_file('clock', node) 240 enable_path = utils.locate_file(enable_node, node) 241 242 enable = utils.read_one_line(enable_path) 243 logging.info('DVFS enable = %s', enable) 244 if not enable == enable_value: 245 return self.handle_error('DVFS is not enabled. ') 246 247 freqs_path = utils.locate_file('available_frequencies', node) 248 249 # available_frequencies are always sorted in ascending order 250 # each line may contain one or multiple integers separated by spaces 251 min_freq = int(utils.read_one_line(freqs_path).split()[0]) 252 253 # daisy_* (exynos5250) boards set idle frequency to 266000000 254 # See: crbug.com/467401 and crosbug.com/p/19710 255 if self._board.startswith('daisy'): 256 min_freq = 266000000 257 258 logging.info('Expecting idle DVFS clock = %u', min_freq) 259 tries = 0 260 found = False 261 while not found and tries < 80: 262 time.sleep(0.25) 263 clock = int(utils.read_one_line(clock_path)) 264 if clock <= min_freq: 265 logging.info('Found idle DVFS clock = %u', clock) 266 found = True 267 break 268 269 tries += 1 270 if not found: 271 logging.error('Error: DVFS clock (%u) > min (%u)', clock, min_freq) 272 return self.handle_error('Did not see the min DVFS clock. ', 273 clock_path) 274 return '' 275 276 def verify_graphics_fbc(self): 277 """ On systems which support FBC, check that we can get into FBC; 278 idle before doing so, and retry every second for 20 seconds.""" 279 logging.info('Running verify_graphics_fbc') 280 281 # Link's FBC is disabled (crbug.com/338588). 282 # TODO(marcheu): remove this when/if we fix this bug. 283 board = utils.get_board() 284 if board == 'link': 285 return '' 286 287 # Machines which don't have a monitor can't get FBC. 288 if utils.has_no_monitor(): 289 return '' 290 291 if (self._gpu_type == 'haswell' or self._gpu_type == 'ivybridge' or 292 self._gpu_type == 'sandybridge'): 293 tries = 0 294 found = False 295 param_path = self.get_valid_path(FBC_PATHS) 296 if not param_path: 297 return 'FBC_PATHS not found.' 298 while not found and tries < 20: 299 time.sleep(1) 300 with open(param_path, 'r') as fbc_info_file: 301 for line in fbc_info_file: 302 if re.search('FBC enabled', line): 303 found = True 304 break 305 306 tries += 1 307 if not found: 308 return self.handle_error('Did not see FBC enabled. ', 309 param_path) 310 return '' 311 312 def verify_graphics_psr(self): 313 """ On systems which support PSR, check that we can get into PSR; 314 idle before doing so, and retry every second for 20 seconds.""" 315 logging.info('Running verify_graphics_psr') 316 317 board = utils.get_board() 318 if board != 'samus': 319 return '' 320 tries = 0 321 found = False 322 param_path = self.get_valid_path(PSR_PATHS) 323 if not param_path: 324 return 'PSR_PATHS not found.' 325 while not found and tries < 20: 326 time.sleep(1) 327 with open(param_path, 'r') as fbc_info_file: 328 for line in fbc_info_file: 329 match = re.search(r'Performance_Counter: (.*)', line) 330 if match and int(match.group(1)) > 0: 331 found = True 332 break 333 334 tries += 1 335 if not found: 336 return self.handle_error('Did not see PSR enabled. ', param_path) 337 return '' 338 339 def verify_graphics_gem_idle(self): 340 """ On systems which have i915, check that we can get all gem objects 341 to become idle (i.e. the i915_gem_active list need to go to 0); 342 idle before doing so, and retry every second for 20 seconds.""" 343 logging.info('Running verify_graphics_gem_idle') 344 if (utils.get_cpu_soc_family() == 'x86_64' and 345 self._gpu_type != 'pinetrail'): 346 tries = 0 347 found = False 348 gem_path = self.get_valid_path(GEM_PATHS) 349 if not gem_path: 350 return 'GEM_PATHS not found.' 351 while not found and tries < 240: 352 time.sleep(0.25) 353 with open(gem_path, 'r') as gem_file: 354 for line in gem_file: 355 if re.search('Total 0 objects', line): 356 found = True 357 break 358 359 tries += 1 360 if not found: 361 return self.handle_error('Did not reach 0 gem actives. ', 362 gem_path) 363 return '' 364