Add dd drive I/O Benchmark test
* All tests: runs if SMART=CS/OVERRIDE & BADBLOCKS=CS * All drive tests: runs if SMART=CS/OVERRIDE & BADBLOCKS=CS * I/O tests are readonly, blocksize=4M, and limited to 16 Gb * Fix issue #23
This commit is contained in:
parent
a7079d4eae
commit
e55dbeeb23
2 changed files with 124 additions and 8 deletions
|
|
@ -39,6 +39,11 @@ TESTS = {
|
||||||
'Results': {},
|
'Results': {},
|
||||||
'Status': {},
|
'Status': {},
|
||||||
},
|
},
|
||||||
|
'iobenchmark': {
|
||||||
|
'Enabled': False,
|
||||||
|
'Results': {},
|
||||||
|
'Status': {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_smart_details(dev):
|
def get_smart_details(dev):
|
||||||
|
|
@ -66,15 +71,17 @@ def menu_diags(*args):
|
||||||
"""Main HW-Diagnostic menu."""
|
"""Main HW-Diagnostic menu."""
|
||||||
diag_modes = [
|
diag_modes = [
|
||||||
{'Name': 'All tests',
|
{'Name': 'All tests',
|
||||||
'Tests': ['Prime95', 'NVMe/SMART', 'badblocks']},
|
'Tests': ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']},
|
||||||
{'Name': 'Prime95',
|
{'Name': 'Prime95',
|
||||||
'Tests': ['Prime95']},
|
'Tests': ['Prime95']},
|
||||||
{'Name': 'NVMe/SMART & badblocks',
|
{'Name': 'All drive tests',
|
||||||
'Tests': ['NVMe/SMART', 'badblocks']},
|
'Tests': ['NVMe/SMART', 'badblocks', 'iobenchmark']},
|
||||||
{'Name': 'NVMe/SMART',
|
{'Name': 'NVMe/SMART',
|
||||||
'Tests': ['NVMe/SMART']},
|
'Tests': ['NVMe/SMART']},
|
||||||
{'Name': 'badblocks',
|
{'Name': 'badblocks',
|
||||||
'Tests': ['badblocks']},
|
'Tests': ['badblocks']},
|
||||||
|
{'Name': 'I/O Benchmark',
|
||||||
|
'Tests': ['iobenchmark']},
|
||||||
{'Name': 'Quick drive test',
|
{'Name': 'Quick drive test',
|
||||||
'Tests': ['Quick', 'NVMe/SMART']},
|
'Tests': ['Quick', 'NVMe/SMART']},
|
||||||
]
|
]
|
||||||
|
|
@ -197,6 +204,75 @@ def run_badblocks():
|
||||||
run_program('tmux kill-pane -a'.split(), check=False)
|
run_program('tmux kill-pane -a'.split(), check=False)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def run_iobenchmark():
|
||||||
|
"""Run a read-only test for all detected disks."""
|
||||||
|
aborted = False
|
||||||
|
clear_screen()
|
||||||
|
print_log('\nStart I/O Benchmark test(s)\n')
|
||||||
|
progress_file = '{}/iobenchmark_progress.out'.format(global_vars['LogDir'])
|
||||||
|
update_progress()
|
||||||
|
|
||||||
|
# Set Window layout and start test
|
||||||
|
run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format(
|
||||||
|
TESTS['Progress Out']).split())
|
||||||
|
|
||||||
|
# Show disk details
|
||||||
|
for name, dev in sorted(TESTS['iobenchmark']['Devices'].items()):
|
||||||
|
show_disk_details(dev)
|
||||||
|
print_standard(' ')
|
||||||
|
update_progress()
|
||||||
|
|
||||||
|
# Run
|
||||||
|
print_standard('Running benchmark test(s):')
|
||||||
|
for name, dev in sorted(TESTS['iobenchmark']['Devices'].items()):
|
||||||
|
cur_status = TESTS['iobenchmark']['Status'][name]
|
||||||
|
nvme_smart_status = TESTS['NVMe/SMART']['Status'].get(name, None)
|
||||||
|
bb_status = TESTS['badblocks']['Status'].get(name, None)
|
||||||
|
if cur_status == 'Denied':
|
||||||
|
# Skip denied disks
|
||||||
|
continue
|
||||||
|
if nvme_smart_status == 'NS':
|
||||||
|
TESTS['iobenchmark']['Status'][name] = 'Skipped'
|
||||||
|
elif bb_status in ['NS', 'Skipped']:
|
||||||
|
TESTS['iobenchmark']['Status'][name] = 'Skipped'
|
||||||
|
else:
|
||||||
|
# (SMART tests not run or CS/OVERRIDE)
|
||||||
|
# AND (BADBLOCKS tests not run or CS)
|
||||||
|
TESTS['iobenchmark']['Status'][name] = 'Working'
|
||||||
|
update_progress()
|
||||||
|
print_standard(' /dev/{:11} '.format(name+'...'), end='', flush=True)
|
||||||
|
run_program('tmux split-window -dl 5 {} {} {}'.format(
|
||||||
|
'hw-diags-iobenchmark',
|
||||||
|
'/dev/{}'.format(name),
|
||||||
|
progress_file).split())
|
||||||
|
wait_for_process('dd')
|
||||||
|
print_standard('Done', timestamp=False)
|
||||||
|
|
||||||
|
# Check results
|
||||||
|
with open(progress_file, 'r') as f:
|
||||||
|
text = f.read()
|
||||||
|
io_stats = text.replace('\r', '\n').split('\n')
|
||||||
|
try:
|
||||||
|
io_stats = [re.sub(r'.*\s+(\d+) MB/s\s*$', r'\1', stat)
|
||||||
|
for stat in io_stats if 'MB/s' in stat]
|
||||||
|
io_stats = [int(x) for x in io_stats]
|
||||||
|
TESTS['iobenchmark']['Results'][name] = 'Read speed: {:0.0f} MB/s (Min: {}, Max: {})'.format(
|
||||||
|
sum(io_stats) / len(io_stats),
|
||||||
|
min(io_stats),
|
||||||
|
max(io_stats))
|
||||||
|
TESTS['iobenchmark']['Status'][name] = 'CS'
|
||||||
|
except:
|
||||||
|
TESTS['iobenchmark']['Status'][name] = 'NS'
|
||||||
|
|
||||||
|
# Move temp file
|
||||||
|
shutil.move(progress_file, '{}/iobenchmark-{}.log'.format(
|
||||||
|
global_vars['LogDir'], name))
|
||||||
|
update_progress()
|
||||||
|
|
||||||
|
# Done
|
||||||
|
run_program('tmux kill-pane -a'.split(), check=False)
|
||||||
|
pass
|
||||||
|
|
||||||
def run_mprime():
|
def run_mprime():
|
||||||
"""Run Prime95 for MPRIME_LIMIT minutes while showing the temps."""
|
"""Run Prime95 for MPRIME_LIMIT minutes while showing the temps."""
|
||||||
aborted = False
|
aborted = False
|
||||||
|
|
@ -389,12 +465,12 @@ def run_tests(tests):
|
||||||
print_log('Starting Hardware Diagnostics')
|
print_log('Starting Hardware Diagnostics')
|
||||||
print_log('\nRunning tests: {}'.format(', '.join(tests)))
|
print_log('\nRunning tests: {}'.format(', '.join(tests)))
|
||||||
# Enable selected tests
|
# Enable selected tests
|
||||||
for t in ['Prime95', 'NVMe/SMART', 'badblocks']:
|
for t in ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']:
|
||||||
TESTS[t]['Enabled'] = t in tests
|
TESTS[t]['Enabled'] = t in tests
|
||||||
TESTS['NVMe/SMART']['Quick'] = 'Quick' in tests
|
TESTS['NVMe/SMART']['Quick'] = 'Quick' in tests
|
||||||
|
|
||||||
# Initialize
|
# Initialize
|
||||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']:
|
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']:
|
||||||
scan_disks()
|
scan_disks()
|
||||||
update_progress()
|
update_progress()
|
||||||
|
|
||||||
|
|
@ -410,6 +486,8 @@ def run_tests(tests):
|
||||||
run_nvme_smart()
|
run_nvme_smart()
|
||||||
if TESTS['badblocks']['Enabled']:
|
if TESTS['badblocks']['Enabled']:
|
||||||
run_badblocks()
|
run_badblocks()
|
||||||
|
if TESTS['iobenchmark']['Enabled']:
|
||||||
|
run_iobenchmark()
|
||||||
|
|
||||||
# Show results
|
# Show results
|
||||||
show_results()
|
show_results()
|
||||||
|
|
@ -437,6 +515,7 @@ def scan_disks():
|
||||||
devs[d['name']] = {'lsblk': d}
|
devs[d['name']] = {'lsblk': d}
|
||||||
TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending'
|
TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending'
|
||||||
TESTS['badblocks']['Status'][d['name']] = 'Pending'
|
TESTS['badblocks']['Status'][d['name']] = 'Pending'
|
||||||
|
TESTS['iobenchmark']['Status'][d['name']] = 'Pending'
|
||||||
else:
|
else:
|
||||||
# Skip WizardKit devices
|
# Skip WizardKit devices
|
||||||
wk_label = '{}_LINUX'.format(KIT_NAME_SHORT)
|
wk_label = '{}_LINUX'.format(KIT_NAME_SHORT)
|
||||||
|
|
@ -444,6 +523,7 @@ def scan_disks():
|
||||||
devs[d['name']] = {'lsblk': d}
|
devs[d['name']] = {'lsblk': d}
|
||||||
TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending'
|
TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending'
|
||||||
TESTS['badblocks']['Status'][d['name']] = 'Pending'
|
TESTS['badblocks']['Status'][d['name']] = 'Pending'
|
||||||
|
TESTS['iobenchmark']['Status'][d['name']] = 'Pending'
|
||||||
|
|
||||||
for dev, data in devs.items():
|
for dev, data in devs.items():
|
||||||
# Get SMART attributes
|
# Get SMART attributes
|
||||||
|
|
@ -483,7 +563,7 @@ def scan_disks():
|
||||||
data['SMART Support'] = False
|
data['SMART Support'] = False
|
||||||
|
|
||||||
# Ask for manual overrides if necessary
|
# Ask for manual overrides if necessary
|
||||||
if not data['Quick Health OK'] and TESTS['badblocks']['Enabled']:
|
if not data['Quick Health OK'] and (TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']):
|
||||||
show_disk_details(data)
|
show_disk_details(data)
|
||||||
print_warning("WARNING: Health can't be confirmed for: {}".format(
|
print_warning("WARNING: Health can't be confirmed for: {}".format(
|
||||||
'/dev/{}'.format(dev)))
|
'/dev/{}'.format(dev)))
|
||||||
|
|
@ -494,10 +574,12 @@ def scan_disks():
|
||||||
else:
|
else:
|
||||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
||||||
TESTS['badblocks']['Status'][dev_name] = 'Denied'
|
TESTS['badblocks']['Status'][dev_name] = 'Denied'
|
||||||
|
TESTS['iobenchmark']['Status'][dev_name] = 'Denied'
|
||||||
print_standard(' ') # In case there's more than one "OVERRIDE" disk
|
print_standard(' ') # In case there's more than one "OVERRIDE" disk
|
||||||
|
|
||||||
TESTS['NVMe/SMART']['Devices'] = devs
|
TESTS['NVMe/SMART']['Devices'] = devs
|
||||||
TESTS['badblocks']['Devices'] = devs
|
TESTS['badblocks']['Devices'] = devs
|
||||||
|
TESTS['iobenchmark']['Devices'] = devs
|
||||||
|
|
||||||
def show_disk_details(dev):
|
def show_disk_details(dev):
|
||||||
"""Display disk details."""
|
"""Display disk details."""
|
||||||
|
|
@ -616,8 +698,8 @@ def show_results():
|
||||||
print(' {}'.format(line.strip()))
|
print(' {}'.format(line.strip()))
|
||||||
print_standard(' ')
|
print_standard(' ')
|
||||||
|
|
||||||
# NVMe/SMART / badblocks
|
# NVMe/SMART / badblocks / iobenchmark
|
||||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']:
|
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']:
|
||||||
print_success('Disks:')
|
print_success('Disks:')
|
||||||
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
||||||
show_disk_details(dev)
|
show_disk_details(dev)
|
||||||
|
|
@ -635,6 +717,12 @@ def show_results():
|
||||||
print_standard(' {}'.format(line))
|
print_standard(' {}'.format(line))
|
||||||
else:
|
else:
|
||||||
print_error(' {}'.format(line))
|
print_error(' {}'.format(line))
|
||||||
|
io_status = TESTS['iobenchmark']['Status'].get(name, None)
|
||||||
|
if (TESTS['iobenchmark']['Enabled']
|
||||||
|
and io_status not in ['Denied', 'OVERRIDE', 'Skipped']):
|
||||||
|
print_info('Benchmark:')
|
||||||
|
result = TESTS['iobenchmark']['Results'].get(name, '')
|
||||||
|
print_standard(' {}'.format(result))
|
||||||
print_standard(' ')
|
print_standard(' ')
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
|
|
@ -676,6 +764,16 @@ def update_progress():
|
||||||
s_color = get_status_color(status),
|
s_color = get_status_color(status),
|
||||||
status = status,
|
status = status,
|
||||||
**COLORS))
|
**COLORS))
|
||||||
|
if TESTS['iobenchmark']['Enabled']:
|
||||||
|
output.append(' ')
|
||||||
|
output.append('{BLUE}I/O Benchmark{CLEAR}'.format(**COLORS))
|
||||||
|
for dev, status in sorted(TESTS['iobenchmark']['Status'].items()):
|
||||||
|
output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format(
|
||||||
|
dev = dev,
|
||||||
|
pad = 15-len(dev),
|
||||||
|
s_color = get_status_color(status),
|
||||||
|
status = status,
|
||||||
|
**COLORS))
|
||||||
|
|
||||||
# Add line-endings
|
# Add line-endings
|
||||||
output = ['{}\n'.format(line) for line in output]
|
output = ['{}\n'.format(line) for line in output]
|
||||||
|
|
|
||||||
18
.bin/Scripts/hw-diags-iobenchmark
Normal file
18
.bin/Scripts/hw-diags-iobenchmark
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
## Wizard Kit: HW Diagnostics - Benchmarks
|
||||||
|
|
||||||
|
function usage {
|
||||||
|
echo "Usage: ${0} device log-file"
|
||||||
|
echo " e.g. ${0} /dev/sda /tmp/tmp.XXXXXXX/benchmarks.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if [ ! -b "${1}" ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run Benchmarks
|
||||||
|
echo 3 | sudo tee -a /proc/sys/vm/drop_caches >/dev/null 2>&1
|
||||||
|
sudo dd bs=4M count=4096 if="${1}" of=/dev/null status=progress 2>&1 | tee -a "${2}"
|
||||||
Loading…
Reference in a new issue