Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add some tools for testing ext4 performance and resiliency.
rand_emmc_perf is a simple test to test random read/write performance of emmc chips. android_emmc_perf_tests is a script that runs on the host that uses rand_emmc_perf to test the emmc performance on a device. corrupt_gdt_free_blocks is used to corrupt a filesystem so the kernel trips over it at runtime and panics, thus testing kernel's ability to mark the filesystem as needing to be fixed. set_ext4_err_bit sets the error bit in the superblock so e2fsck will trigger a full check next boot. corrupt_gdt_free_blocks and set_ext4_err_bit are only built for userdebug and eng builds. rand_emmc_perf is marked optional, and not included in any build by default. Change-Id: I808174025d891f358ac54008371cb590e3c19f2f
- Loading branch information
Ken Sumrall
committed
Jun 15, 2012
1 parent
b931402
commit 2633ce5
Showing
5 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Copyright 2012 The Android Open Source Project | ||
|
||
LOCAL_PATH:= $(call my-dir) | ||
include $(CLEAR_VARS) | ||
|
||
LOCAL_SRC_FILES:= corrupt_gdt_free_blocks.c | ||
|
||
LOCAL_MODULE:= corrupt_gdt_free_blocks | ||
LOCAL_MODULE_TAGS := debug | ||
LOCAL_C_INCLUDES += system/extras/ext4_utils | ||
|
||
include $(BUILD_EXECUTABLE) | ||
|
||
|
||
include $(CLEAR_VARS) | ||
|
||
LOCAL_SRC_FILES:= set_ext4_err_bit.c | ||
|
||
LOCAL_MODULE:= set_ext4_err_bit | ||
LOCAL_MODULE_TAGS := debug | ||
|
||
include $(BUILD_EXECUTABLE) | ||
|
||
|
||
include $(CLEAR_VARS) | ||
|
||
LOCAL_SRC_FILES:= rand_emmc_perf.c | ||
|
||
LOCAL_MODULE:= rand_emmc_perf | ||
LOCAL_MODULE_TAGS := optional | ||
LOCAL_FORCE_STATIC_EXECUTABLE := true | ||
LOCAL_STATIC_LIBRARIES := libc | ||
|
||
include $(BUILD_EXECUTABLE) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#!/bin/bash | ||
|
||
PERF="rand_emmc_perf" | ||
|
||
if [ ! -r "$PERF" ] | ||
then | ||
echo "Cannot read $PERF test binary" | ||
fi | ||
|
||
if [ ! -r "$PERF_OSYNC" ] | ||
then | ||
echo "Cannot read $PERF_OSYNC test binary" | ||
fi | ||
|
||
if ! adb shell true >/dev/null 2>&1 | ||
then | ||
echo "No device detected over adb" | ||
fi | ||
|
||
HARDWARE=`adb shell getprop ro.hardware | tr -d "\r"` | ||
|
||
case "$HARDWARE" in | ||
tuna | steelhead) | ||
CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq" | ||
CACHE="/dev/block/platform/omap/omap_hsmmc.0/by-name/cache" | ||
;; | ||
|
||
stingray | wingray) | ||
CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq" | ||
CACHE="/dev/block/platform/sdhci-tegra.3/by-name/cache" | ||
;; | ||
|
||
herring) | ||
echo "This test will wipe the userdata partition on $HARDWARE devices." | ||
read -p "Do you want to proceed? " ANSWER | ||
|
||
if [ "$ANSWER" != "yes" ] | ||
then | ||
echo "aborting test" | ||
exit 1 | ||
fi | ||
|
||
CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq" | ||
CACHE="/dev/block/platform/s3c-sdhci.0/by-name/userdata" | ||
;; | ||
|
||
grouper) | ||
CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq" | ||
CACHE="/dev/block/platform/sdhci-tegra.3/by-name/CAC" | ||
;; | ||
|
||
*) | ||
echo "Unknown hardware $HARDWARE. Exiting." | ||
exit 1 | ||
esac | ||
|
||
# prepare the device | ||
adb root | ||
adb wait-for-device | ||
adb push "$PERF" /dev | ||
adb push "$PERF_OSYNC" /dev | ||
adb shell stop | ||
adb shell stop sdcard | ||
adb shell stop ril-daemon | ||
adb shell stop media | ||
adb shell stop drm | ||
adb shell stop keystore | ||
adb shell stop tf_daemon | ||
adb shell stop bluetoothd | ||
adb shell stop hciattach | ||
adb shell umount /sdcard >/dev/null 2>&1 | ||
adb shell umount /data >/dev/null 2>&1 | ||
adb shell umount /cache >/dev/null 2>&1 | ||
# Add more services here that other devices need to stop. | ||
# So far, this list is sufficient for: | ||
# Prime | ||
|
||
# At this point, the device is quiescent, need to crank up the cpu speed, | ||
# then run tests | ||
adb shell "cat $CPUFREQ/cpuinfo_max_freq > $CPUFREQ/scaling_max_freq" | ||
adb shell "cat $CPUFREQ/cpuinfo_max_freq > $CPUFREQ/scaling_min_freq" | ||
|
||
# Start the tests | ||
|
||
# Sequential read test | ||
for I in 1 2 3 | ||
do | ||
echo "Sequential read test $I" | ||
adb shell dd if="$CACHE" of=/dev/null bs=1048576 count=200 | ||
done | ||
|
||
# Sequential write test | ||
for I in 1 2 3 | ||
do | ||
echo "Sequential write test $I" | ||
adb shell dd if=/dev/zero of="$CACHE" bs=1048576 count=200 | ||
done | ||
|
||
# Random read test | ||
for I in 1 2 3 | ||
do | ||
echo "Random read test $I" | ||
adb shell /dev/"$PERF" -r 100 "$CACHE" | ||
done | ||
|
||
# Random write test | ||
for I in 1 2 3 | ||
do | ||
echo "Random write test $I" | ||
adb shell /dev/"$PERF" -w 100 "$CACHE" | ||
done | ||
|
||
# Random write test with O_SYNC | ||
for I in 1 2 3 | ||
do | ||
echo "Random write with o_sync test $I" | ||
adb shell /dev/"$PERF" -w 100 -o "$CACHE" | ||
done | ||
|
||
# Make a new empty /cache filesystem | ||
adb shell make_ext4fs "$CACHE" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <fcntl.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
#include "ext4.h" | ||
#include "ext4_utils.h" | ||
|
||
#define SB_OFFSET 1024 | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
char me[] = "corrupt_gdt_free_blocks"; | ||
int fd; | ||
int block_size; | ||
int num_bgs; | ||
int i; | ||
struct ext4_super_block sb; | ||
struct ext2_group_desc gd; | ||
|
||
if (argc != 2) { | ||
fprintf(stderr, "%s: Usage: %s <ext4_block_device>\n", me, me); | ||
exit(1); | ||
} | ||
|
||
fd = open(argv[1], O_RDWR); | ||
|
||
if (fd < 0) { | ||
fprintf(stderr, "%s: Cannot open block device %s\n", me, argv[1]); | ||
exit(1); | ||
} | ||
|
||
if (lseek(fd, SB_OFFSET, SEEK_SET) == -1) { | ||
fprintf(stderr, "%s: Cannot lseek to superblock to read\n", me); | ||
exit(1); | ||
} | ||
|
||
if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { | ||
fprintf(stderr, "%s: Cannot read superblock\n", me); | ||
exit(1); | ||
} | ||
|
||
if (sb.s_magic != 0xEF53) { | ||
fprintf(stderr, "%s: invalid superblock magic\n", me); | ||
exit(1); | ||
} | ||
|
||
/* Make sure the block size is 2K or 4K */ | ||
if ((sb.s_log_block_size != 1) && (sb.s_log_block_size != 2)) { | ||
fprintf(stderr, "%s: block size not 2K or 4K\n", me); | ||
exit(1); | ||
} | ||
|
||
block_size = 1 << (10 + sb.s_log_block_size); | ||
num_bgs = DIV_ROUND_UP(sb.s_blocks_count_lo, sb.s_blocks_per_group); | ||
|
||
if (sb.s_desc_size != sizeof(struct ext2_group_desc)) { | ||
fprintf(stderr, "%s: Can't handle block group descriptor size of %d\n", | ||
me, sb.s_desc_size); | ||
exit(1); | ||
} | ||
|
||
/* read first block group descriptor, decrement free block count, and | ||
* write it back out | ||
*/ | ||
if (lseek(fd, block_size, SEEK_SET) == -1) { | ||
fprintf(stderr, "%s: Cannot lseek to block group descriptor table to read\n", me); | ||
exit(1); | ||
} | ||
|
||
/* Read in block group descriptors till we read one that has at least one free block */ | ||
|
||
for (i=0; i < num_bgs; i++) { | ||
if (read(fd, &gd, sizeof(gd)) != sizeof(gd)) { | ||
fprintf(stderr, "%s: Cannot read group descriptor %d\n", me, i); | ||
exit(1); | ||
} | ||
if (gd.bg_free_blocks_count) { | ||
break; | ||
} | ||
} | ||
|
||
gd.bg_free_blocks_count--; | ||
|
||
if (lseek(fd, -sizeof(gd), SEEK_CUR) == -1) { | ||
fprintf(stderr, "%s: Cannot lseek to block group descriptor table to write\n", me); | ||
exit(1); | ||
} | ||
|
||
if (write(fd, &gd, sizeof(gd)) != sizeof(gd)) { | ||
fprintf(stderr, "%s: Cannot write modified group descriptor\n", me); | ||
exit(1); | ||
} | ||
|
||
close(fd); | ||
|
||
return 0; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Copyright (C) 2010 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
/* A simple test of emmc random read and write performance. When testing write | ||
* performance, try it twice, once with O_SYNC compiled in, and once with it commented | ||
* out. Without O_SYNC, the close(2) blocks until all the dirty buffers are written | ||
* out, but the numbers tend to be higher. | ||
*/ | ||
|
||
#include <string.h> | ||
#include <stdio.h> | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <fcntl.h> | ||
#include <sys/time.h> | ||
#include <stdlib.h> | ||
|
||
#define TST_BLK_SIZE 4096 | ||
/* Number of seconds to run the test */ | ||
#define TEST_LEN 10 | ||
|
||
static void usage(void) { | ||
fprintf(stderr, "Usage: rand_emmc_perf [ -r | -w ] [-o] <size_in_mb> <block_dev>\n"); | ||
exit(1); | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
long max_blocks; | ||
int fd, fd2, write_mode = 0, iops = 0; | ||
struct timeval start, end, res; | ||
unsigned int seed; | ||
char buf[TST_BLK_SIZE] = { 0 }; | ||
int c; | ||
int o_sync = 0; | ||
|
||
while ((c = getopt(argc, argv, "+rwo")) != -1) { | ||
switch (c) { | ||
case '?': | ||
default: | ||
usage(); | ||
break; | ||
|
||
case 'r': | ||
/* Do nothing, read mode is the default */ | ||
break; | ||
|
||
case 'w': | ||
write_mode = 1; | ||
break; | ||
|
||
case 'o': | ||
o_sync = O_SYNC; | ||
break; | ||
} | ||
} | ||
|
||
if (o_sync && !write_mode) { | ||
/* Can only specify o_sync in write mode. Probably doesn't matter, | ||
* but clear o_sync if in read mode */ | ||
o_sync = 0; | ||
} | ||
|
||
if ((argc - optind) != 2) { | ||
usage(); | ||
} | ||
|
||
/* Size is given in megabytes, so compute the number of TST_BLK_SIZE blocks. */ | ||
max_blocks = atol(argv[2]) * ((1024*1024) / TST_BLK_SIZE); | ||
|
||
if ((fd = open(argv[3], O_RDWR | o_sync)) < 0) { | ||
fprintf(stderr, "Cannot open block device %s\n", argv[2]); | ||
exit(1); | ||
} | ||
|
||
fd2 = open("/dev/urandom\n", O_RDONLY); | ||
if (fd2 < 0) { | ||
fprintf(stderr, "Cannot open /dev/urandom\n"); | ||
} | ||
if (read(fd2, &seed, sizeof(seed)) != sizeof(seed)) { | ||
fprintf(stderr, "Cannot read /dev/urandom\n"); | ||
} | ||
close(fd2); | ||
srand(seed); | ||
|
||
res.tv_sec = 0; | ||
gettimeofday(&start, 0); | ||
while (res.tv_sec < TEST_LEN) { | ||
if (lseek64(fd, (rand() % max_blocks) * TST_BLK_SIZE, SEEK_SET) < 0) { | ||
fprintf(stderr, "lseek64 failed\n"); | ||
} | ||
if (write_mode) { | ||
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { | ||
fprintf(stderr, "Short write\n"); | ||
} | ||
} else { | ||
if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { | ||
fprintf(stderr, "Short read\n"); | ||
} | ||
} | ||
iops++; | ||
gettimeofday(&end, 0); | ||
timersub(&end, &start, &res); | ||
} | ||
close(fd); | ||
|
||
/* The close can take a while when in write_mode as buffers are flushed. | ||
* So get the time again. */ | ||
gettimeofday(&end, 0); | ||
timersub(&end, &start, &res); | ||
|
||
printf("%d iops/sec\n", iops / (int) res.tv_sec); | ||
|
||
exit(0); | ||
} | ||
|
Oops, something went wrong.