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
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 <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;
+}
+
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 <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);
+}
+
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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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 <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, 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;
+}
+