• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
6import time
7
8from autotest_lib.client.common_lib import error
9from 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/power_override/flashrom.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
32class 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