Home | History | Annotate | Download | only in power_DeferForFlashrom
      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 
      5 import logging
      6 import time
      7 
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.server import test
     10 
     11 
     12 # Timeout for commands run on the host.
     13 _COMMAND_TIMEOUT = 60
     14 
     15 # Lock file created by flashrom to tell powerd not to suspend or shut down the
     16 # system.
     17 _LOCK_FILE = '/run/lock/flashrom_powerd.lock'
     18 
     19 # Time in seconds to perform a flashrom write and to wait for the system to
     20 # suspend and resume.
     21 _SUSPEND_FLASHROM_SEC = 15
     22 _SUSPEND_DOWN_SEC = 60
     23 _SUSPEND_UP_SEC = 60
     24 
     25 # Time in seconds to perform a flashrom write and to wait for the system to
     26 # power down and reboot.
     27 _REBOOT_FLASHROM_SEC = 15
     28 _REBOOT_DOWN_SEC = 60
     29 _REBOOT_UP_SEC = 60
     30 
     31 
     32 class power_DeferForFlashrom(test.test):
     33     """Test that powerd defers suspend and shutdown for flashrom."""
     34     version = 1
     35 
     36     def initialize(self, host):
     37         """
     38         Initial settings before running test.
     39 
     40         @param host: Host/DUT object to use for test.
     41         """
     42         self.host = host
     43 
     44 
     45     def create_temp_file(self, base_name, source_path, size):
     46         """
     47         Create a temporary file on the host and returns its path.
     48 
     49         @param base_name: String containing the base name for the temp file.
     50         @param source_path: String containing the path to the device from
     51                 which file contents should be read.
     52         @param size: Number of bytes to write to the file.
     53         """
     54         logging.info('Creating %d-byte temp file from %s', size, source_path)
     55         temp_file = self.host.run(
     56             'mktemp --tmpdir %s.XXXXXXXXXX' % base_name,
     57             timeout=_COMMAND_TIMEOUT).stdout.strip()
     58         self.host.run('dd if=%s of=%s bs=%d count=1 2>&1' %
     59             (source_path, temp_file, size))
     60         logging.info('Created %s', temp_file)
     61         return temp_file
     62 
     63 
     64     def run_in_background(self, cmd):
     65         """
     66         Asynchronously run a command on the host.
     67 
     68         @param cmd: Command to run (as a string).
     69         """
     70         bg_cmd = '(%s) </dev/null >/dev/null 2>&1 &' % (cmd)
     71         logging.info("Running %s", bg_cmd)
     72         self.host.run(bg_cmd, timeout=_COMMAND_TIMEOUT)
     73 
     74 
     75     def start_fake_flashrom_write(self, duration_sec):
     76         """
     77         Start a fake flashrom write.
     78 
     79         @param duration_sec: Duration for the write in seconds.
     80         """
     81         # flashrom simulates a 4096-byte block size, so the file size needs to
     82         # be a multiple of that.
     83         BLOCK_SIZE = 4096
     84 
     85         # flashrom will write one bit per cycle. Convert the block size to bits
     86         # (yielding the frequency for a one-second write) and then scale it as
     87         # needed.
     88         frequency_hz = int(BLOCK_SIZE * 8 / float(duration_sec))
     89 
     90         # To avoid flashrom needing to read (slowly) from the dummy device, pass
     91         # a custom diff file filled with zeroes.
     92         zero_file = self.create_temp_file(
     93             'power_DeferForFlashrom.zero', '/dev/zero', BLOCK_SIZE)
     94         rand_file = self.create_temp_file(
     95             'power_DeferForFlashrom.rand', '/dev/urandom', BLOCK_SIZE)
     96 
     97         # Start flashrom in the background and wait for it to create its lock
     98         # file.
     99         self.run_in_background(
    100             ('flashrom -w %s --diff %s --noverify '
    101              '-p dummy:freq=%d,emulate=VARIABLE_SIZE,size=%d,'
    102              'erase_to_zero=yes') %
    103             (rand_file, zero_file, frequency_hz, BLOCK_SIZE))
    104 
    105         logging.info("Waiting for flashrom to create %s...", _LOCK_FILE)
    106         self.host.run(
    107             'while [ ! -e %s ]; do sleep 0.1; done' % (_LOCK_FILE),
    108             timeout=_COMMAND_TIMEOUT)
    109 
    110 
    111     def send_suspend_request(self, wake_sec):
    112         """
    113         Asynchronously ask powerd to suspend the system immediately.
    114 
    115         @param wake_sec: Integer delay in seconds to use for setting a wake
    116                 alarm. Note that the alarm starts when the request is sent to
    117                 powerd, not when the system actually suspends.
    118         """
    119         self.run_in_background(
    120             'powerd_dbus_suspend --delay=0 --wakeup_timeout=%d' % (wake_sec))
    121 
    122 
    123     def send_reboot_request(self):
    124         """Ask powerd to reboot the system immediately."""
    125         logging.info('Calling powerd\'s RequestRestart method')
    126         self.host.run(
    127             ('dbus-send --type=method_call --system '
    128              '--dest=org.chromium.PowerManager /org/chromium/PowerManager '
    129              'org.chromium.PowerManager.RequestRestart'),
    130             timeout=_COMMAND_TIMEOUT)
    131 
    132 
    133     def wait_for_system_to_cycle(self, down_sec, up_sec):
    134         """
    135         Wait for the system to stop and then start responding to pings.
    136 
    137         @param down_sec: Maximum delay for the system to go down.
    138         @param up_sec: Maximum delay for the system to come back up.
    139 
    140         @return: Floating-point time when system went down.
    141         """
    142         logging.info("Waiting for host to go down...")
    143         if not self.host.ping_wait_down(timeout=down_sec):
    144             raise error.TestError(
    145                 'System hasn\'t gone down after %d seconds' % (down_sec))
    146         down_timestamp = time.time()
    147         logging.info("System went down at %.2f", down_timestamp)
    148 
    149         logging.info("Waiting for host to come back up...")
    150         if not self.host.ping_wait_up(timeout=up_sec) or \
    151             not self.host.wait_up(timeout=up_sec):
    152             raise error.TestError('System didn\'t come back up')
    153 
    154         return down_timestamp
    155 
    156 
    157     def run_once(self):
    158         # Start flashrom and then request that the system be suspended. The
    159         # suspend should be deferred until flashrom finishes writing but should
    160         # happen eventually.
    161         flashrom_time = time.time()
    162         self.start_fake_flashrom_write(_SUSPEND_FLASHROM_SEC)
    163         self.send_suspend_request(_SUSPEND_DOWN_SEC)
    164         delay_sec = self.wait_for_system_to_cycle(
    165             _SUSPEND_DOWN_SEC, _SUSPEND_UP_SEC) - flashrom_time
    166 
    167         # Check that powerd waited for flashrom to finish.
    168         if delay_sec < _SUSPEND_FLASHROM_SEC:
    169             raise error.TestError(
    170                 ('Suspend was blocked for %.2f sec; expected it to be blocked '
    171                  'for at least %d sec') % (delay_sec, _SUSPEND_FLASHROM_SEC))
    172 
    173         # Now do the same thing, but with a reboot request.
    174         flashrom_time = time.time()
    175         self.start_fake_flashrom_write(_REBOOT_FLASHROM_SEC)
    176         self.send_reboot_request()
    177         delay_sec = self.wait_for_system_to_cycle(
    178             _REBOOT_DOWN_SEC, _REBOOT_UP_SEC) - flashrom_time
    179         if delay_sec < _REBOOT_FLASHROM_SEC:
    180             raise error.TestError(
    181                 ('Reboot was blocked for %.2f sec; expected it to be blocked '
    182                  'for at least %d sec') % (delay_sec, _REBOOT_FLASHROM_SEC))
    183