| import os, time, re, subprocess, shutil, logging |
| from autotest_lib.client.bin import utils, test |
| from autotest_lib.client.common_lib import error |
| |
| |
| class dma_memtest(test.test): |
| """ |
| A test for the memory subsystem against heavy IO and DMA operations, |
| implemented based on the work of Doug Leford |
| (http://people.redhat.com/dledford/memtest.shtml) |
| |
| @author Lucas Meneghel Rodrigues ([email protected]) |
| @author Rodrigo Sampaio Vaz ([email protected]) |
| """ |
| version = 1 |
| def initialize(self): |
| self.cachedir = os.path.join(self.bindir, 'cache') |
| self.nfail = 0 |
| |
| |
| def setup(self, tarball_base='linux-2.6.18.tar.bz2', parallel=True): |
| """ |
| Downloads a copy of the linux kernel, calculate an estimated size of |
| the uncompressed tarball, use this value to calculate the number of |
| copies of the linux kernel that will be uncompressed. |
| |
| @param tarball_base: Name of the kernel tarball location that will |
| be looked up on the kernel.org mirrors. |
| @param parallel: If we are going to uncompress the copies of the |
| kernel in parallel or not |
| """ |
| if not os.path.isdir(self.cachedir): |
| os.makedirs(self.cachedir) |
| self.parallel = parallel |
| |
| kernel_repo = 'http://www.kernel.org/pub/linux/kernel/v2.6' |
| tarball_url = os.path.join(kernel_repo, tarball_base) |
| tarball_md5 = '296a6d150d260144639c3664d127d174' |
| logging.info('Downloading linux kernel tarball') |
| self.tarball = utils.unmap_url_cache(self.cachedir, tarball_url, |
| tarball_md5) |
| size_tarball = os.path.getsize(self.tarball) / 1024 / 1024 |
| # Estimation of the tarball size after uncompression |
| compress_ratio = 5 |
| est_size = size_tarball * compress_ratio |
| self.sim_cps = self.get_sim_cps(est_size) |
| logging.info('Source file: %s' % tarball_base) |
| logging.info('Megabytes per copy: %s' % size_tarball) |
| logging.info('Compress ratio: %s' % compress_ratio) |
| logging.info('Estimated size after uncompression: %s' % est_size) |
| logging.info('Number of copies: %s' % self.sim_cps) |
| logging.info('Parallel: %s' % parallel) |
| |
| |
| def get_sim_cps(self, est_size): |
| ''' |
| Calculate the amount of simultaneous copies that can be uncompressed |
| so that it will make the system swap. |
| |
| @param est_size: Estimated size of uncompressed linux tarball |
| ''' |
| mem_str = utils.system_output('grep MemTotal /proc/meminfo') |
| mem = int(re.search(r'\d+', mem_str).group(0)) |
| mem = int(mem / 1024) |
| |
| # The general idea here is that we'll make an amount of copies of the |
| # kernel tree equal to 1.5 times the physical RAM, to make sure the |
| # system swaps, therefore reading and writing stuff to the disk. The |
| # DMA reads and writes together with the memory operations that will |
| # make it more likely to reveal failures in the memory subsystem. |
| sim_cps = (1.5 * mem) / est_size |
| |
| if (mem % est_size) >= (est_size / 2): |
| sim_cps += 1 |
| |
| if (mem / 32) < 1: |
| sim_cps += 1 |
| |
| return int(sim_cps) |
| |
| |
| def run_once(self): |
| """ |
| Represents a single iteration of the process. Uncompresses a previously |
| calculated number of copies of the linux kernel, sequentially or in |
| parallel, and then compares the tree with a base tree, that was |
| uncompressed on the very beginning. |
| """ |
| |
| parallel_procs = [] |
| |
| os.chdir(self.tmpdir) |
| # This is the reference copy of the linux tarball |
| # that will be used for subsequent comparisons |
| logging.info('Unpacking base copy') |
| base_dir = os.path.join(self.tmpdir, 'linux.orig') |
| utils.extract_tarball_to_dir(self.tarball, base_dir) |
| logging.info('Unpacking test copies') |
| for j in range(self.sim_cps): |
| tmp_dir = 'linux.%s' % j |
| if self.parallel: |
| os.mkdir(tmp_dir) |
| # Start parallel process |
| tar_cmd = ['tar', 'jxf', self.tarball, '-C', tmp_dir] |
| logging.debug("Unpacking tarball to %s", tmp_dir) |
| parallel_procs.append(subprocess.Popen(tar_cmd, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE)) |
| else: |
| logging.debug("Unpacking tarball to %s", tmp_dir) |
| utils.extract_tarball_to_dir(self.tarball, tmp_dir) |
| # Wait for the subprocess before comparison |
| if self.parallel: |
| logging.debug("Wait background processes before proceed") |
| for proc in parallel_procs: |
| proc.wait() |
| |
| parallel_procs = [] |
| |
| logging.info('Comparing test copies with base copy') |
| for j in range(self.sim_cps): |
| tmp_dir = 'linux.%s/%s' % (j, |
| os.path.basename(self.tarball).strip('.tar.bz2')) |
| if self.parallel: |
| diff_cmd = ['diff', '-U3', '-rN', 'linux.orig', tmp_dir] |
| logging.debug("Comparing linux.orig with %s", tmp_dir) |
| p = subprocess.Popen(diff_cmd, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| parallel_procs.append(p) |
| else: |
| try: |
| logging.debug('Comparing linux.orig with %s', tmp_dir) |
| utils.system('diff -U3 -rN linux.orig linux.%s' % j) |
| except error.CmdError, e: |
| self.nfail += 1 |
| logging.error('Error comparing trees: %s', e) |
| |
| for proc in parallel_procs: |
| out_buf = proc.stdout.read() |
| out_buf += proc.stderr.read() |
| proc.wait() |
| if out_buf != "": |
| self.nfail += 1 |
| logging.error('Error comparing trees: %s', out_buf) |
| |
| # Clean up for the next iteration |
| parallel_procs = [] |
| |
| logging.info('Cleaning up') |
| for j in range(self.sim_cps): |
| tmp_dir = 'linux.%s' % j |
| shutil.rmtree(tmp_dir) |
| shutil.rmtree(base_dir) |
| |
| |
| def cleanup(self): |
| if self.nfail != 0: |
| raise error.TestError('DMA memory test failed.') |
| else: |
| logging.info('DMA memory test passed.') |