blob: 99d5f85f3ec547917e666c6eb09b81a0fd9a00c3 [file] [log] [blame]
/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* Like hammer, this test stresses the file system but instead of
* using writes to one big file, crdel uses lots of little files.
* Uses a circular buffer of files that are created, written and
* then deleted. The creation and deletion run synchrounous but
* keep the circular buffer at least 90% full.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <eprintf.h>
#include <esys.h>
#include <puny.h>
#include <style.h>
#include <timer.h>
#include <twister.h>
#include <util.h>
enum { MAX_NAME = 12,
MAX_FILE_SIZE = 1 << 14,
NUM_FILES = 1 << 16 };
struct file {
char name[MAX_NAME];
};
bool Done = FALSE;
struct file *File;
u64 Num_files = NUM_FILES;
int Next;
int Last;
unint Commited;
unint Deleted;
static bool is_empty(void)
{
return Next == Last;
}
static bool is_full(void)
{
int next = Next + 1;
if (next == Num_files) {
return Last == 0;
} else {
return next == Last;
}
}
static void commit(void)
{
if (++Next == Num_files)
Next = 0;
++Commited;
}
static void delete(void)
{
if (++Last == Num_files)
Last = 0;
++Deleted;
}
static void doze(void)
{
struct timespec sleep = { 0, ONE_THOUSAND /* ns */};
nanosleep(&sleep, 0);
}
static void fill(u8 *buf, int n)
{
int i;
for (i = 0; i < n; i++) {
buf[i] = twister_random();
}
}
void *creator(void *unused)
{
u8 *buf = emalloc(Option.file_size);
int fd;
fill(buf, Option.file_size);
while (!Done) {
while (is_full())
doze();
gen_name(File[Next].name, MAX_NAME);
fd = ecreat(File[Next].name);
/* Can't advance Next until after file is created */
commit();
ewrite(fd, buf, Option.file_size);
eclose(fd);
}
return NULL;
}
void *unlinker(void *unused)
{
while (!Done) {
while (is_empty())
doze();
if (((Commited - Deleted) * 100 / Num_files) > 90) {
eunlink(File[Last].name);
delete();
} else {
doze();
}
}
while (!is_empty()) {
eunlink(File[Last].name);
delete();
}
return NULL;
}
void *syncer(void *unused)
{
struct timespec sleep = { 20 /* s */, 0 /* ns */};
u64 start;
u64 finish;
u64 i;
for (i = 0; !Done; i++) {
start = nsecs();
esync();
finish = nsecs();
printf("sync: %4llu. %3.2g\n", i,
(double)(finish - start)/ONE_BILLION);
nanosleep(&sleep, NULL);
}
return NULL;
}
void *timer(void *arg)
{
struct timespec sleep = { Option.sleep_secs, 0 /* ns */ };
unint old_num_created;
unint old_num_unlinked;
unint delta_created;
unint delta_unlinked;
u64 i;
printf("secs created/sec deleted/sec\n");
for (i = 0; !Done; i++) {
old_num_unlinked = Deleted;
old_num_created = Commited;
nanosleep(&sleep, NULL);
delta_unlinked = Deleted - old_num_unlinked;
delta_created = Commited - old_num_created;
printf("%4llu. %10ld %10ld\n", i, delta_created, delta_unlinked);
}
return NULL;
}
void cleanup(void)
{
static bool cleaning_up = FALSE;
char cmd[1024];
int rc;
if (!Option.cleanup || cleaning_up)
return;
cleaning_up = TRUE;
echdir("..");
rc = snprintf(cmd, sizeof(cmd), "rm -fr %s", Option.dir);
if (rc > sizeof(cmd) - 2) { /* shouldn't be that big */
eprintf("counldn't cleanup %s", cmd);
}
esystem(cmd);
}
void setup(void)
{
emkdir(Option.dir);
echdir(Option.dir);
File = ezalloc(sizeof(*File) * Num_files);
set_cleanup(cleanup);
}
void usage(void)
{
pr_usage("-d<directory> -n<number of files> -z<size in bytes>\n"
" busy continuously creates and deletes files\n"
" It displays the create/delete in seconds\n"
"\td - directory to create paths\n"
"\tn - max files to create\n"
"\ts - seconds to sleep between reports\n"
"\tz - size in MiBs of file to hammer system");
}
bool myopt(int c)
{
switch (c) {
case 'n':
Num_files = strtoll(optarg, NULL, 0);
break;
default:
return FALSE;
}
return TRUE;
}
void epthread_create(const char *name, pthread_t *thread,
const pthread_attr_t *attr, void *(*start_routine) (void *),
void *arg)
{
int rc;
rc = pthread_create(thread, attr, start_routine, arg);
if (rc)
fatal("%s thread:");
}
int main(int argc, char *argv[])
{
pthread_t timer_thread;
pthread_t creator_thread;
pthread_t unlinker_thread;
pthread_t syncer_thread;
Option.file_size = MAX_FILE_SIZE;
Option.sleep_secs = 1;
punyopt(argc, argv, myopt, "n:");
setup();
epthread_create("timer", &timer_thread, NULL, timer, NULL);
epthread_create("creator", &creator_thread, NULL, creator, NULL);
epthread_create("unlinker", &unlinker_thread, NULL, unlinker, NULL);
epthread_create("syncer", &syncer_thread, NULL, syncer, NULL);
pthread_join(syncer_thread, NULL);
pthread_join(creator_thread, NULL);
pthread_join(unlinker_thread, NULL);
pthread_join(timer_thread, NULL);
cleanup();
return 0;
}