2024-09-10 13:03:02 -04:00

711 lines
17 KiB
C

/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* fstest - filesystem test code
*
* Writes a file (in small chunks) and then reads it back again
* (also in small chunks) and complains if what it reads back is
* not the same.
*
* The length of SLOGAN is intentionally a prime number and
* specifically *not* a power of two.
*/
#include <types.h>
#include <kern/errno.h>
#include <kern/fcntl.h>
#include <lib.h>
#include <uio.h>
#include <thread.h>
#include <synch.h>
#include <vfs.h>
#include <fs.h>
#include <vnode.h>
#include <test.h>
#define SLOGAN "HODIE MIHI - CRAS TIBI\n"
#define FILENAME "fstest.tmp"
#define NCHUNKS 720
#define NTHREADS 12
#define NLONG 32
#define NCREATE 24
static struct semaphore *threadsem = NULL;
static void init_threadsem(void) {
if (threadsem == NULL) {
threadsem = sem_create("fstestsem", 0);
if (threadsem == NULL) {
panic("fstest: sem_create failed\n");
}
}
}
/*
* Vary each line of the test file in a way that's predictable but
* unlikely to mask bugs in the filesystem.
*/
static void rotate(char *str, int amt) {
int i, ch;
amt = (amt + 2600) % 26;
KASSERT(amt >= 0);
for (i = 0; str[i]; i++) {
ch = str[i];
if (ch >= 'A' && ch <= 'Z') {
ch = ch - 'A';
ch += amt;
ch %= 26;
ch = ch + 'A';
KASSERT(ch >= 'A' && ch <= 'Z');
}
str[i] = ch;
}
}
////////////////////////////////////////////////////////////
static void fstest_makename(char *buf, size_t buflen, const char *fs,
const char *namesuffix) {
snprintf(buf, buflen, "%s:%s%s", fs, FILENAME, namesuffix);
KASSERT(strlen(buf) < buflen);
}
#define MAKENAME() fstest_makename(name, sizeof(name), fs, namesuffix)
static int fstest_remove(const char *fs, const char *namesuffix) {
char name[32];
char buf[32];
int err;
MAKENAME();
strcpy(buf, name);
err = vfs_remove(buf);
if (err) {
kprintf("Could not remove %s: %s\n", name, strerror(err));
return -1;
}
return 0;
}
static int fstest_write(const char *fs, const char *namesuffix, int stridesize,
int stridepos) {
struct vnode *vn;
int err;
int i;
size_t shouldbytes = 0;
size_t bytes = 0;
off_t pos = 0;
char name[32];
char buf[32];
struct iovec iov;
struct uio ku;
int flags;
KASSERT(sizeof(buf) > strlen(SLOGAN));
MAKENAME();
flags = O_WRONLY | O_CREAT;
if (stridesize == 1) {
flags |= O_TRUNC;
}
/* vfs_open destroys the string it's passed */
strcpy(buf, name);
err = vfs_open(buf, flags, 0664, &vn);
if (err) {
kprintf("Could not open %s for write: %s\n", name, strerror(err));
return -1;
}
for (i = 0; i < NCHUNKS; i++) {
if (i % stridesize != stridepos) {
pos += strlen(SLOGAN);
continue;
}
strcpy(buf, SLOGAN);
rotate(buf, i);
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), pos, UIO_WRITE);
err = VOP_WRITE(vn, &ku);
if (err) {
kprintf("%s: Write error: %s\n", name, strerror(err));
vfs_close(vn);
vfs_remove(name);
return -1;
}
if (ku.uio_resid > 0) {
kprintf("%s: Short write: %lu bytes left over\n", name,
(unsigned long)ku.uio_resid);
vfs_close(vn);
vfs_remove(name);
return -1;
}
bytes += (ku.uio_offset - pos);
shouldbytes += strlen(SLOGAN);
pos = ku.uio_offset;
}
vfs_close(vn);
if (bytes != shouldbytes) {
kprintf("%s: %lu bytes written, should have been %lu!\n", name,
(unsigned long)bytes, (unsigned long)(NCHUNKS * strlen(SLOGAN)));
vfs_remove(name);
return -1;
}
kprintf("%s: %lu bytes written\n", name, (unsigned long)bytes);
return 0;
}
static int fstest_read(const char *fs, const char *namesuffix) {
struct vnode *vn;
int err;
int i;
size_t bytes = 0;
char name[32];
char buf[32];
struct iovec iov;
struct uio ku;
MAKENAME();
/* vfs_open destroys the string it's passed */
strcpy(buf, name);
err = vfs_open(buf, O_RDONLY, 0664, &vn);
if (err) {
kprintf("Could not open test file for read: %s\n", strerror(err));
return -1;
}
for (i = 0; i < NCHUNKS; i++) {
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), bytes, UIO_READ);
err = VOP_READ(vn, &ku);
if (err) {
kprintf("%s: Read error: %s\n", name, strerror(err));
vfs_close(vn);
return -1;
}
if (ku.uio_resid > 0) {
kprintf("%s: Short read: %lu bytes left over\n", name,
(unsigned long)ku.uio_resid);
vfs_close(vn);
return -1;
}
buf[strlen(SLOGAN)] = 0;
rotate(buf, -i);
if (strcmp(buf, SLOGAN)) {
kprintf("%s: Test failed: line %d mismatched: %s\n", name, i + 1, buf);
vfs_close(vn);
return -1;
}
bytes = ku.uio_offset;
}
vfs_close(vn);
if (bytes != NCHUNKS * strlen(SLOGAN)) {
kprintf("%s: %lu bytes read, should have been %lu!\n", name,
(unsigned long)bytes, (unsigned long)(NCHUNKS * strlen(SLOGAN)));
return -1;
}
kprintf("%s: %lu bytes read\n", name, (unsigned long)bytes);
return 0;
}
////////////////////////////////////////////////////////////
static void dofstest(const char *filesys) {
kprintf("*** Starting filesystem test on %s:\n", filesys);
if (fstest_write(filesys, "", 1, 0)) {
kprintf("*** Test failed\n");
return;
}
if (fstest_read(filesys, "")) {
kprintf("*** Test failed\n");
return;
}
if (fstest_remove(filesys, "")) {
kprintf("*** Test failed\n");
return;
}
kprintf("*** Filesystem test done\n");
}
////////////////////////////////////////////////////////////
static void readstress_thread(void *fs, unsigned long num) {
const char *filesys = fs;
if (fstest_read(filesys, "")) {
kprintf("*** Thread %lu: failed\n", num);
}
V(threadsem);
}
static void doreadstress(const char *filesys) {
int i, err;
init_threadsem();
kprintf("*** Starting fs read stress test on %s:\n", filesys);
if (fstest_write(filesys, "", 1, 0)) {
kprintf("*** Test failed\n");
return;
}
for (i = 0; i < NTHREADS; i++) {
err =
thread_fork("readstress", NULL, readstress_thread, (char *)filesys, i);
if (err) {
panic("readstress: thread_fork failed: %s\n", strerror(err));
}
}
for (i = 0; i < NTHREADS; i++) {
P(threadsem);
}
if (fstest_remove(filesys, "")) {
kprintf("*** Test failed\n");
return;
}
kprintf("*** fs read stress test done\n");
}
////////////////////////////////////////////////////////////
static void writestress_thread(void *fs, unsigned long num) {
const char *filesys = fs;
char numstr[8];
snprintf(numstr, sizeof(numstr), "%lu", num);
if (fstest_write(filesys, numstr, 1, 0)) {
kprintf("*** Thread %lu: failed\n", num);
V(threadsem);
return;
}
if (fstest_read(filesys, numstr)) {
kprintf("*** Thread %lu: failed\n", num);
V(threadsem);
return;
}
if (fstest_remove(filesys, numstr)) {
kprintf("*** Thread %lu: failed\n", num);
}
kprintf("*** Thread %lu: done\n", num);
V(threadsem);
}
static void dowritestress(const char *filesys) {
int i, err;
init_threadsem();
kprintf("*** Starting fs write stress test on %s:\n", filesys);
for (i = 0; i < NTHREADS; i++) {
err = thread_fork("writestress", NULL, writestress_thread, (char *)filesys,
i);
if (err) {
panic("thread_fork failed %s\n", strerror(err));
}
}
for (i = 0; i < NTHREADS; i++) {
P(threadsem);
}
kprintf("*** fs write stress test done\n");
}
////////////////////////////////////////////////////////////
static void writestress2_thread(void *fs, unsigned long num) {
const char *filesys = fs;
if (fstest_write(filesys, "", NTHREADS, num)) {
kprintf("*** Thread %lu: failed\n", num);
V(threadsem);
return;
}
V(threadsem);
}
static void dowritestress2(const char *filesys) {
int i, err;
char name[32];
struct vnode *vn;
init_threadsem();
kprintf("*** Starting fs write stress test 2 on %s:\n", filesys);
/* Create and truncate test file */
fstest_makename(name, sizeof(name), filesys, "");
err = vfs_open(name, O_WRONLY | O_CREAT | O_TRUNC, 0664, &vn);
if (err) {
kprintf("Could not create test file: %s\n", strerror(err));
kprintf("*** Test failed\n");
return;
}
vfs_close(vn);
for (i = 0; i < NTHREADS; i++) {
err = thread_fork("writestress2", NULL, writestress2_thread,
(char *)filesys, i);
if (err) {
panic("writestress2: thread_fork failed: %s\n", strerror(err));
}
}
for (i = 0; i < NTHREADS; i++) {
P(threadsem);
}
if (fstest_read(filesys, "")) {
kprintf("*** Test failed\n");
return;
}
if (fstest_remove(filesys, "")) {
kprintf("*** Test failed\n");
}
kprintf("*** fs write stress test 2 done\n");
}
////////////////////////////////////////////////////////////
static void longstress_thread(void *fs, unsigned long num) {
const char *filesys = fs;
int i;
char numstr[16];
for (i = 0; i < NLONG; i++) {
snprintf(numstr, sizeof(numstr), "%lu-%d", num, i);
if (fstest_write(filesys, numstr, 1, 0)) {
kprintf("*** Thread %lu: file %d: failed\n", num, i);
V(threadsem);
return;
}
if (fstest_read(filesys, numstr)) {
kprintf("*** Thread %lu: file %d: failed\n", num, i);
V(threadsem);
return;
}
if (fstest_remove(filesys, numstr)) {
kprintf("*** Thread %lu: file %d: failed\n", num, i);
V(threadsem);
return;
}
}
V(threadsem);
}
static void dolongstress(const char *filesys) {
int i, err;
init_threadsem();
kprintf("*** Starting fs long stress test on %s:\n", filesys);
for (i = 0; i < NTHREADS; i++) {
err =
thread_fork("longstress", NULL, longstress_thread, (char *)filesys, i);
if (err) {
panic("longstress: thread_fork failed %s\n", strerror(err));
}
}
for (i = 0; i < NTHREADS; i++) {
P(threadsem);
}
kprintf("*** fs long stress test done\n");
}
////////////////////////////////////////////////////////////
static void createstress_thread(void *fs, unsigned long num) {
const char *filesys = fs;
int i, err;
char namesuffix[16];
char name[32];
char buf[32];
struct vnode *vn;
struct iovec iov;
struct uio ku;
size_t bytes;
unsigned numwritten = 0, numread = 0, numremoved = 0;
for (i = 0; i < NCREATE; i++) {
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
MAKENAME();
/* vfs_open destroys the string it's passed */
strcpy(buf, name);
err = vfs_open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0664, &vn);
if (err) {
kprintf("Could not open %s for write: %s\n", name, strerror(err));
continue;
}
strcpy(buf, SLOGAN);
rotate(buf, i);
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), 0, UIO_WRITE);
err = VOP_WRITE(vn, &ku);
vfs_close(vn);
if (err) {
kprintf("%s: Write error: %s\n", name, strerror(err));
continue;
}
if (ku.uio_resid > 0) {
kprintf("%s: Short write: %lu bytes left over\n", name,
(unsigned long)ku.uio_resid);
continue;
}
bytes = ku.uio_offset;
if (bytes != strlen(SLOGAN)) {
kprintf("%s: %lu bytes written, expected %lu!\n", name,
(unsigned long)bytes, (unsigned long)strlen(SLOGAN));
continue;
}
numwritten++;
}
kprintf("Thread %lu: %u files written\n", num, numwritten);
for (i = 0; i < NCREATE; i++) {
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
MAKENAME();
/* vfs_open destroys the string it's passed */
strcpy(buf, name);
err = vfs_open(buf, O_RDONLY, 0664, &vn);
if (err) {
kprintf("Could not open %s for read: %s\n", name, strerror(err));
continue;
}
uio_kinit(&iov, &ku, buf, strlen(SLOGAN), 0, UIO_READ);
err = VOP_READ(vn, &ku);
vfs_close(vn);
if (err) {
kprintf("%s: Read error: %s\n", name, strerror(err));
continue;
}
if (ku.uio_resid > 0) {
kprintf("%s: Short read: %lu bytes left over\n", name,
(unsigned long)ku.uio_resid);
continue;
}
buf[strlen(SLOGAN)] = 0;
rotate(buf, -i);
if (strcmp(buf, SLOGAN)) {
kprintf("%s: Test failed: file mismatched: %s\n", name, buf);
continue;
}
bytes = ku.uio_offset;
if (bytes != strlen(SLOGAN)) {
kprintf("%s: %lu bytes read, expected %lu!\n", name, (unsigned long)bytes,
(unsigned long)strlen(SLOGAN));
continue;
}
numread++;
}
kprintf("Thread %lu: %u files read\n", num, numread);
for (i = 0; i < NCREATE; i++) {
snprintf(namesuffix, sizeof(namesuffix), "%lu-%d", num, i);
if (fstest_remove(filesys, namesuffix)) {
continue;
}
numremoved++;
}
kprintf("Thread %lu: %u files removed\n", num, numremoved);
V(threadsem);
}
static void docreatestress(const char *filesys) {
int i, err;
init_threadsem();
kprintf("*** Starting fs create stress test on %s:\n", filesys);
for (i = 0; i < NTHREADS; i++) {
err = thread_fork("createstress", NULL, createstress_thread,
(char *)filesys, i);
if (err) {
panic("createstress: thread_fork failed %s\n", strerror(err));
}
}
for (i = 0; i < NTHREADS; i++) {
P(threadsem);
}
kprintf("*** fs create stress test done\n");
}
////////////////////////////////////////////////////////////
static int checkfilesystem(int nargs, char **args) {
char *device;
if (nargs != 2) {
kprintf("Usage: fs[123456] filesystem:\n");
return EINVAL;
}
device = args[1];
/* Allow (but do not require) colon after device name */
if (device[strlen(device) - 1] == ':') {
device[strlen(device) - 1] = 0;
}
return 0;
}
#define DEFTEST(testname) \
int testname(int nargs, char **args) { \
int result; \
result = checkfilesystem(nargs, args); \
if (result) { \
return result; \
} \
do \
##testname(args[1]); \
return 0; \
}
DEFTEST(fstest);
DEFTEST(readstress);
DEFTEST(writestress);
DEFTEST(writestress2);
DEFTEST(longstress);
DEFTEST(createstress);
////////////////////////////////////////////////////////////
int printfile(int nargs, char **args) {
struct vnode *rv, *wv;
struct iovec iov;
struct uio ku;
off_t rpos = 0, wpos = 0;
char buf[128];
char outfile[16];
int result;
int done = 0;
if (nargs != 2) {
kprintf("Usage: pf filename\n");
return EINVAL;
}
/* vfs_open destroys the string it's passed; make a copy */
strcpy(outfile, "con:");
result = vfs_open(args[1], O_RDONLY, 0664, &rv);
if (result) {
kprintf("printfile: %s\n", strerror(result));
return result;
}
result = vfs_open(outfile, O_WRONLY, 0664, &wv);
if (result) {
kprintf("printfile: output: %s\n", strerror(result));
vfs_close(rv);
return result;
}
while (!done) {
uio_kinit(&iov, &ku, buf, sizeof(buf), rpos, UIO_READ);
result = VOP_READ(rv, &ku);
if (result) {
kprintf("Read error: %s\n", strerror(result));
break;
}
rpos = ku.uio_offset;
if (ku.uio_resid > 0) {
done = 1;
}
uio_kinit(&iov, &ku, buf, sizeof(buf) - ku.uio_resid, wpos, UIO_WRITE);
result = VOP_WRITE(wv, &ku);
if (result) {
kprintf("Write error: %s\n", strerror(result));
break;
}
wpos = ku.uio_offset;
if (ku.uio_resid > 0) {
kprintf("Warning: short write\n");
}
}
vfs_close(wv);
vfs_close(rv);
return 0;
}