From 2633ce5ca5024d5565c9068c29ec39a8c3ed10e9 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Wed, 30 May 2012 16:44:40 -0700 Subject: [PATCH] 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 --- tests/ext4/Android.mk | 35 +++++++ tests/ext4/android_emmc_perf_tests.sh | 122 ++++++++++++++++++++++++ tests/ext4/corrupt_gdt_free_blocks.c | 100 ++++++++++++++++++++ tests/ext4/rand_emmc_perf.c | 129 ++++++++++++++++++++++++++ tests/ext4/set_ext4_err_bit.c | 62 +++++++++++++ 5 files changed, 448 insertions(+) create mode 100644 tests/ext4/Android.mk create mode 100755 tests/ext4/android_emmc_perf_tests.sh create mode 100644 tests/ext4/corrupt_gdt_free_blocks.c create mode 100644 tests/ext4/rand_emmc_perf.c create mode 100644 tests/ext4/set_ext4_err_bit.c diff --git a/tests/ext4/Android.mk b/tests/ext4/Android.mk new file mode 100644 index 0000000..36f2e17 --- /dev/null +++ b/tests/ext4/Android.mk @@ -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) + diff --git a/tests/ext4/android_emmc_perf_tests.sh b/tests/ext4/android_emmc_perf_tests.sh new file mode 100755 index 0000000..23190f8 --- /dev/null +++ b/tests/ext4/android_emmc_perf_tests.sh @@ -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" + diff --git a/tests/ext4/corrupt_gdt_free_blocks.c b/tests/ext4/corrupt_gdt_free_blocks.c new file mode 100644 index 0000000..7be737d --- /dev/null +++ b/tests/ext4/corrupt_gdt_free_blocks.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#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 \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; +} + diff --git a/tests/ext4/rand_emmc_perf.c b/tests/ext4/rand_emmc_perf.c new file mode 100644 index 0000000..d55adc9 --- /dev/null +++ b/tests/ext4/rand_emmc_perf.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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] \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); +} + diff --git a/tests/ext4/set_ext4_err_bit.c b/tests/ext4/set_ext4_err_bit.c new file mode 100644 index 0000000..88893d8 --- /dev/null +++ b/tests/ext4/set_ext4_err_bit.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +#define SB_OFFSET 1024 +#define SB_SIZE 1024 +#define EXT4_MAGIC_OFFSET 0x38 +#define EXT4_STATE_OFFSET 0x3A + +int main(int argc, char *argv[]) +{ + int fd; + char me[] = "set_ext4_err_bit"; + unsigned char sb[1024]; + + if (argc != 2) { + fprintf(stderr, "%s: Usage: %s \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, SB_SIZE) != SB_SIZE) { + fprintf(stderr, "%s: Cannot read superblock\n", me); + exit(1); + } + + if ((sb[EXT4_MAGIC_OFFSET] != 0x53) || (sb[EXT4_MAGIC_OFFSET+1] != 0xEF)) { + fprintf(stderr, "%s: invalid superblock magic\n", me); + exit(1); + } + + /* Set the errors detected bit */ + sb[EXT4_STATE_OFFSET] |= 0x2; + + if (lseek(fd, SB_OFFSET, SEEK_SET) == -1) { + fprintf(stderr, "%s: Cannot lseek to superblock to write\n", me); + exit(1); + } + + if (write(fd, sb, SB_SIZE) != SB_SIZE) { + fprintf(stderr, "%s: Cannot write superblock\n", me); + exit(1); + } + + close(fd); + + return 0; +} +