os161/kern/test/fstest.c
2025-03-01 00:01:05 -05:00

785 lines
16 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;
}