| # Copyright 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server import test |
| |
| |
| # Timeout for commands run on the host. |
| _COMMAND_TIMEOUT = 60 |
| |
| # Lock file created by flashrom to tell powerd not to suspend or shut down the |
| # system. |
| _LOCK_FILE = '/run/lock/flashrom_powerd.lock' |
| |
| # Time in seconds to perform a flashrom write and to wait for the system to |
| # suspend and resume. |
| _SUSPEND_FLASHROM_SEC = 15 |
| _SUSPEND_DOWN_SEC = 60 |
| _SUSPEND_UP_SEC = 60 |
| |
| # Time in seconds to perform a flashrom write and to wait for the system to |
| # power down and reboot. |
| _REBOOT_FLASHROM_SEC = 15 |
| _REBOOT_DOWN_SEC = 60 |
| _REBOOT_UP_SEC = 60 |
| |
| |
| class power_DeferForFlashrom(test.test): |
| """Test that powerd defers suspend and shutdown for flashrom.""" |
| version = 1 |
| |
| def initialize(self, host): |
| """ |
| Initial settings before running test. |
| |
| @param host: Host/DUT object to use for test. |
| """ |
| self.host = host |
| |
| |
| def create_temp_file(self, base_name, source_path, size): |
| """ |
| Create a temporary file on the host and returns its path. |
| |
| @param base_name: String containing the base name for the temp file. |
| @param source_path: String containing the path to the device from |
| which file contents should be read. |
| @param size: Number of bytes to write to the file. |
| """ |
| logging.info('Creating %d-byte temp file from %s', size, source_path) |
| temp_file = self.host.run( |
| 'mktemp --tmpdir %s.XXXXXXXXXX' % base_name, |
| timeout=_COMMAND_TIMEOUT).stdout.strip() |
| self.host.run('dd if=%s of=%s bs=%d count=1 2>&1' % |
| (source_path, temp_file, size)) |
| logging.info('Created %s', temp_file) |
| return temp_file |
| |
| |
| def run_in_background(self, cmd): |
| """ |
| Asynchronously run a command on the host. |
| |
| @param cmd: Command to run (as a string). |
| """ |
| bg_cmd = '(%s) </dev/null >/dev/null 2>&1 &' % (cmd) |
| logging.info("Running %s", bg_cmd) |
| self.host.run(bg_cmd, timeout=_COMMAND_TIMEOUT) |
| |
| |
| def start_fake_flashrom_write(self, duration_sec): |
| """ |
| Start a fake flashrom write. |
| |
| @param duration_sec: Duration for the write in seconds. |
| """ |
| # flashrom simulates a 4096-byte block size, so the file size needs to |
| # be a multiple of that. |
| BLOCK_SIZE = 4096 |
| |
| # flashrom will write one bit per cycle. Convert the block size to bits |
| # (yielding the frequency for a one-second write) and then scale it as |
| # needed. |
| frequency_hz = int(BLOCK_SIZE * 8 / float(duration_sec)) |
| |
| # To avoid flashrom needing to read (slowly) from the dummy device, pass |
| # a custom diff file filled with zeroes. |
| zero_file = self.create_temp_file( |
| 'power_DeferForFlashrom.zero', '/dev/zero', BLOCK_SIZE) |
| rand_file = self.create_temp_file( |
| 'power_DeferForFlashrom.rand', '/dev/urandom', BLOCK_SIZE) |
| |
| # Start flashrom in the background and wait for it to create its lock |
| # file. |
| self.run_in_background( |
| ('flashrom -w %s --diff %s --noverify ' |
| '-p dummy:freq=%d,emulate=VARIABLE_SIZE,size=%d,' |
| 'erase_to_zero=yes') % |
| (rand_file, zero_file, frequency_hz, BLOCK_SIZE)) |
| |
| logging.info("Waiting for flashrom to create %s...", _LOCK_FILE) |
| self.host.run( |
| 'while [ ! -e %s ]; do sleep 0.1; done' % (_LOCK_FILE), |
| timeout=_COMMAND_TIMEOUT) |
| |
| |
| def send_suspend_request(self, wake_sec): |
| """ |
| Asynchronously ask powerd to suspend the system immediately. |
| |
| @param wake_sec: Integer delay in seconds to use for setting a wake |
| alarm. Note that the alarm starts when the request is sent to |
| powerd, not when the system actually suspends. |
| """ |
| self.run_in_background( |
| 'powerd_dbus_suspend --delay=0 --wakeup_timeout=%d' % (wake_sec)) |
| |
| |
| def send_reboot_request(self): |
| """Ask powerd to reboot the system immediately.""" |
| logging.info('Calling powerd\'s RequestRestart method') |
| self.host.run( |
| ('dbus-send --type=method_call --system ' |
| '--dest=org.chromium.PowerManager /org/chromium/PowerManager ' |
| 'org.chromium.PowerManager.RequestRestart'), |
| timeout=_COMMAND_TIMEOUT) |
| |
| |
| def wait_for_system_to_cycle(self, down_sec, up_sec): |
| """ |
| Wait for the system to stop and then start responding to pings. |
| |
| @param down_sec: Maximum delay for the system to go down. |
| @param up_sec: Maximum delay for the system to come back up. |
| |
| @return: Floating-point time when system went down. |
| """ |
| logging.info("Waiting for host to go down...") |
| if not self.host.ping_wait_down(timeout=down_sec): |
| raise error.TestError( |
| 'System hasn\'t gone down after %d seconds' % (down_sec)) |
| down_timestamp = time.time() |
| logging.info("System went down at %.2f", down_timestamp) |
| |
| logging.info("Waiting for host to come back up...") |
| if not self.host.ping_wait_up(timeout=up_sec) or \ |
| not self.host.wait_up(timeout=up_sec): |
| raise error.TestError('System didn\'t come back up') |
| |
| return down_timestamp |
| |
| |
| def run_once(self): |
| # Start flashrom and then request that the system be suspended. The |
| # suspend should be deferred until flashrom finishes writing but should |
| # happen eventually. |
| flashrom_time = time.time() |
| self.start_fake_flashrom_write(_SUSPEND_FLASHROM_SEC) |
| self.send_suspend_request(_SUSPEND_DOWN_SEC) |
| delay_sec = self.wait_for_system_to_cycle( |
| _SUSPEND_DOWN_SEC, _SUSPEND_UP_SEC) - flashrom_time |
| |
| # Check that powerd waited for flashrom to finish. |
| if delay_sec < _SUSPEND_FLASHROM_SEC: |
| raise error.TestError( |
| ('Suspend was blocked for %.2f sec; expected it to be blocked ' |
| 'for at least %d sec') % (delay_sec, _SUSPEND_FLASHROM_SEC)) |
| |
| # Now do the same thing, but with a reboot request. |
| flashrom_time = time.time() |
| self.start_fake_flashrom_write(_REBOOT_FLASHROM_SEC) |
| self.send_reboot_request() |
| delay_sec = self.wait_for_system_to_cycle( |
| _REBOOT_DOWN_SEC, _REBOOT_UP_SEC) - flashrom_time |
| if delay_sec < _REBOOT_FLASHROM_SEC: |
| raise error.TestError( |
| ('Reboot was blocked for %.2f sec; expected it to be blocked ' |
| 'for at least %d sec') % (delay_sec, _REBOOT_FLASHROM_SEC)) |