Home | History | Annotate | Download | only in power_HotCPUSuspend
      1 # Copyright (c) 2012 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 
      5 import hashlib, logging, multiprocessing, os, re, time
      6 from autotest_lib.client.bin import test, utils
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.cros import sys_power
      9 
     10 SUSPEND_BURN_SECONDS = 10
     11 RESUME_BURN_SECONDS = 5
     12 MIN_CPU_USAGE = .95
     13 
     14 PROC_STAT_CPU_FIELDS = ['user', 'nice', 'system', 'idle', 'iowait', 'irq',
     15                         'softirq', 'steal', 'guest', 'guest_nice']
     16 PROC_STAT_CPU_IDLE_FIELDS = ['idle', 'iowait']
     17 
     18 SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable'
     19 
     20 def cpu_stress():
     21     sha512_hash = open('/dev/urandom', 'r').read(64)
     22     while True:
     23         sha512_hash = hashlib.sha512(sha512_hash).digest()
     24 
     25 
     26 def get_system_times():
     27     proc_stat = utils.read_file('/proc/stat')
     28     for line in proc_stat.split('\n'):
     29         if line.startswith('cpu '):
     30             times = line[4:].strip().split(' ')
     31             times = [int(jiffies) for jiffies in times]
     32             return dict(zip(PROC_STAT_CPU_FIELDS, times))
     33 
     34 
     35 def get_avg_cpu_usage(pre_times, post_times):
     36     diff_times = {}
     37 
     38     for field in PROC_STAT_CPU_FIELDS:
     39         diff_times[field] = post_times[field] - pre_times[field]
     40 
     41     idle_time = sum(diff_times[field] for field in PROC_STAT_CPU_IDLE_FIELDS)
     42     total_time = sum(diff_times[field] for field in PROC_STAT_CPU_FIELDS)
     43 
     44     return float(total_time - idle_time) / total_time
     45 
     46 
     47 def sleep_and_measure_cpu(sleep_seconds):
     48     pre_times = get_system_times()
     49     time.sleep(sleep_seconds)
     50     post_times = get_system_times()
     51 
     52     avg_cpu_usage = get_avg_cpu_usage(pre_times, post_times)
     53     logging.info('average CPU utilization, last %ds: %s%%',
     54                  sleep_seconds, avg_cpu_usage * 100.)
     55     return avg_cpu_usage
     56 
     57 
     58 class power_HotCPUSuspend(test.test):
     59     """Suspend the system with 100% CPU usage."""
     60 
     61     version = 1
     62 
     63     def initialize(self):
     64         # Store the setting if the system has CPUQuiet feature
     65         if os.path.exists(SYSFS_CPUQUIET_ENABLE):
     66             self.is_cpuquiet_enabled = utils.read_file(SYSFS_CPUQUIET_ENABLE)
     67             utils.write_one_line(SYSFS_CPUQUIET_ENABLE, '0')
     68 
     69     def run_once(self):
     70         # create processs pool with enough workers to spin all CPUs
     71         cpus = multiprocessing.cpu_count()
     72         logging.info('found %d cpus', cpus)
     73         workers = max(16, cpus * 2)
     74         pool = multiprocessing.Pool(workers)
     75 
     76         try:
     77             # fill all CPUs with a spinning task
     78             logging.info('starting %d workers', workers)
     79             results = [pool.apply_async(cpu_stress) for _ in xrange(workers)]
     80 
     81             # wait for things to settle
     82             logging.info('spinning for %d seconds', SUSPEND_BURN_SECONDS)
     83             if sleep_and_measure_cpu(SUSPEND_BURN_SECONDS) < MIN_CPU_USAGE:
     84                 # There should be no idle time accounted while we're spinning.
     85                 raise error.TestError('unexpected CPU idle time while spinning')
     86 
     87             # go to suspend
     88             sys_power.kernel_suspend(10)
     89 
     90             # keep spinning after userland resumes
     91             logging.info('spinning for %d more seconds', RESUME_BURN_SECONDS)
     92             if sleep_and_measure_cpu(RESUME_BURN_SECONDS) < MIN_CPU_USAGE:
     93                 # There should be no idle time accounted while we're spinning.
     94                 raise error.TestError('unexpected CPU idle time after resume')
     95 
     96             # check workers: if computation completed, something is wrong
     97             for result in results:
     98                 if result.ready():
     99                     logging.error('worker finished: %s', result.get())
    100                     raise error.TestError('worker terminated!')
    101 
    102         finally:
    103             # kill off the workers
    104             logging.info('killing %d workers', workers)
    105             pool.terminate()
    106 
    107     def cleanup(self):
    108         # Restore the original setting if system has CPUQuiet feature
    109         if os.path.exists(SYSFS_CPUQUIET_ENABLE):
    110             utils.open_write_close(
    111                 SYSFS_CPUQUIET_ENABLE, self.is_cpuquiet_enabled)
    112