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 (lucasmr@br.ibm.com)
@author Rodrigo Sampaio Vaz (rsampaio@br.ibm.com)
"""
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.')