Initial Spring 2016 commit.
This commit is contained in:
217
kern/test/arraytest.c
Normal file
217
kern/test/arraytest.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <array.h>
|
||||
#include <test.h>
|
||||
|
||||
#define TESTSIZE 73
|
||||
#define BIGTESTSIZE 3000 /* more than one page of pointers */
|
||||
#define NTH(i) ((void *)(0xb007 + 3*(i)))
|
||||
|
||||
static
|
||||
void
|
||||
testa(struct array *a)
|
||||
{
|
||||
int testarray[TESTSIZE];
|
||||
int i, j, n, r, *p;
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
testarray[i]=i;
|
||||
}
|
||||
|
||||
n = array_num(a);
|
||||
KASSERT(n==0);
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
r = array_add(a, &testarray[i], NULL);
|
||||
KASSERT(r==0);
|
||||
n = array_num(a);
|
||||
KASSERT(n==i+1);
|
||||
}
|
||||
n = array_num(a);
|
||||
KASSERT(n==TESTSIZE);
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
p = array_get(a, i);
|
||||
KASSERT(*p == i);
|
||||
}
|
||||
n = array_num(a);
|
||||
KASSERT(n==TESTSIZE);
|
||||
|
||||
for (j=0; j<TESTSIZE*4; j++) {
|
||||
i = random()%TESTSIZE;
|
||||
p = array_get(a, i);
|
||||
KASSERT(*p == i);
|
||||
}
|
||||
n = array_num(a);
|
||||
KASSERT(n==TESTSIZE);
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
array_set(a, i, &testarray[TESTSIZE-i-1]);
|
||||
}
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
p = array_get(a, i);
|
||||
KASSERT(*p == TESTSIZE-i-1);
|
||||
}
|
||||
|
||||
r = array_setsize(a, TESTSIZE/2);
|
||||
KASSERT(r==0);
|
||||
|
||||
for (i=0; i<TESTSIZE/2; i++) {
|
||||
p = array_get(a, i);
|
||||
KASSERT(*p == TESTSIZE-i-1);
|
||||
}
|
||||
|
||||
array_remove(a, 1);
|
||||
|
||||
for (i=1; i<TESTSIZE/2 - 1; i++) {
|
||||
p = array_get(a, i);
|
||||
KASSERT(*p == TESTSIZE-i-2);
|
||||
}
|
||||
p = array_get(a, 0);
|
||||
KASSERT(*p == TESTSIZE-1);
|
||||
|
||||
array_setsize(a, 2);
|
||||
p = array_get(a, 0);
|
||||
KASSERT(*p == TESTSIZE-1);
|
||||
p = array_get(a, 1);
|
||||
KASSERT(*p == TESTSIZE-3);
|
||||
|
||||
array_set(a, 1, NULL);
|
||||
array_setsize(a, 2);
|
||||
p = array_get(a, 0);
|
||||
KASSERT(*p == TESTSIZE-1);
|
||||
p = array_get(a, 1);
|
||||
KASSERT(p==NULL);
|
||||
|
||||
array_setsize(a, TESTSIZE*10);
|
||||
p = array_get(a, 0);
|
||||
KASSERT(*p == TESTSIZE-1);
|
||||
p = array_get(a, 1);
|
||||
KASSERT(p==NULL);
|
||||
}
|
||||
|
||||
int
|
||||
arraytest(int nargs, char **args)
|
||||
{
|
||||
struct array *a;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Beginning array test...\n");
|
||||
a = array_create();
|
||||
KASSERT(a != NULL);
|
||||
|
||||
testa(a);
|
||||
|
||||
array_setsize(a, 0);
|
||||
|
||||
testa(a);
|
||||
|
||||
array_setsize(a, 0);
|
||||
array_destroy(a);
|
||||
|
||||
kprintf("Array test complete\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
arraytest2(int nargs, char **args)
|
||||
{
|
||||
struct array *a;
|
||||
void *p;
|
||||
unsigned i, x;
|
||||
int result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Beginning large array test...\n");
|
||||
a = array_create();
|
||||
KASSERT(a != NULL);
|
||||
|
||||
/* 1. Fill it one at a time. */
|
||||
p = (void *)0xc0ffee;
|
||||
for (i=0; i<BIGTESTSIZE; i++) {
|
||||
result = array_add(a, p, &x);
|
||||
KASSERT(result == 0);
|
||||
KASSERT(x == i);
|
||||
}
|
||||
KASSERT(array_num(a) == BIGTESTSIZE);
|
||||
|
||||
/* 2. Check the contents */
|
||||
for (i=0; i<BIGTESTSIZE; i++) {
|
||||
KASSERT(array_get(a, i) == p);
|
||||
}
|
||||
|
||||
/* 3. Clear it */
|
||||
result = array_setsize(a, 0);
|
||||
KASSERT(result == 0);
|
||||
|
||||
/* 4. Set the size and initialize with array_set */
|
||||
result = array_setsize(a, BIGTESTSIZE);
|
||||
KASSERT(result == 0);
|
||||
for (i=0; i<BIGTESTSIZE; i++) {
|
||||
array_set(a, i, NTH(i));
|
||||
}
|
||||
|
||||
/* 5. Check the contents again */
|
||||
for (i=0; i<BIGTESTSIZE; i++) {
|
||||
KASSERT(array_get(a, i) == NTH(i));
|
||||
}
|
||||
|
||||
/* 6. Zot an entry and check the contents */
|
||||
array_remove(a, 1);
|
||||
KASSERT(array_get(a, 0) == NTH(0));
|
||||
KASSERT(array_num(a) == BIGTESTSIZE-1);
|
||||
for (i=1; i<BIGTESTSIZE-1; i++) {
|
||||
KASSERT(array_get(a, i) == NTH(i+1));
|
||||
}
|
||||
|
||||
/* 7. Double the size and check the preexisting contents */
|
||||
result = array_setsize(a, BIGTESTSIZE*2);
|
||||
KASSERT(result == 0);
|
||||
KASSERT(array_get(a, 0) == NTH(0));
|
||||
for (i=1; i<BIGTESTSIZE-1; i++) {
|
||||
KASSERT(array_get(a, i) == NTH(i+1));
|
||||
}
|
||||
|
||||
/* done */
|
||||
result = array_setsize(a, 0);
|
||||
KASSERT(result == 0);
|
||||
array_destroy(a);
|
||||
|
||||
kprintf("Done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
106
kern/test/bitmaptest.c
Normal file
106
kern/test/bitmaptest.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <bitmap.h>
|
||||
#include <test.h>
|
||||
|
||||
#define TESTSIZE 533
|
||||
|
||||
int
|
||||
bitmaptest(int nargs, char **args)
|
||||
{
|
||||
struct bitmap *b;
|
||||
char data[TESTSIZE];
|
||||
uint32_t x;
|
||||
int i;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Starting bitmap test...\n");
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
data[i] = random()%2;
|
||||
}
|
||||
|
||||
b = bitmap_create(TESTSIZE);
|
||||
KASSERT(b != NULL);
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
KASSERT(bitmap_isset(b, i)==0);
|
||||
}
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
if (data[i]) {
|
||||
bitmap_mark(b, i);
|
||||
}
|
||||
}
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
if (data[i]) {
|
||||
KASSERT(bitmap_isset(b, i));
|
||||
}
|
||||
else {
|
||||
KASSERT(bitmap_isset(b, i)==0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
if (data[i]) {
|
||||
bitmap_unmark(b, i);
|
||||
}
|
||||
else {
|
||||
bitmap_mark(b, i);
|
||||
}
|
||||
}
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
if (data[i]) {
|
||||
KASSERT(bitmap_isset(b, i)==0);
|
||||
}
|
||||
else {
|
||||
KASSERT(bitmap_isset(b, i));
|
||||
}
|
||||
}
|
||||
|
||||
while (bitmap_alloc(b, &x)==0) {
|
||||
KASSERT(x < TESTSIZE);
|
||||
KASSERT(bitmap_isset(b, x));
|
||||
KASSERT(data[x]==1);
|
||||
data[x] = 0;
|
||||
}
|
||||
|
||||
for (i=0; i<TESTSIZE; i++) {
|
||||
KASSERT(bitmap_isset(b, i));
|
||||
KASSERT(data[i]==0);
|
||||
}
|
||||
|
||||
kprintf("Bitmap test complete\n");
|
||||
return 0;
|
||||
}
|
784
kern/test/fstest.c
Normal file
784
kern/test/fstest.c
Normal file
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
* 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[12345] 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;
|
||||
}
|
380
kern/test/kmalloctest.c
Normal file
380
kern/test/kmalloctest.c
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Test code for kmalloc.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <thread.h>
|
||||
#include <synch.h>
|
||||
#include <vm.h> /* for PAGE_SIZE */
|
||||
#include <test.h>
|
||||
|
||||
#include "opt-dumbvm.h"
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// km1/km2
|
||||
|
||||
/*
|
||||
* Test kmalloc; allocate ITEMSIZE bytes NTRIES times, freeing
|
||||
* somewhat later.
|
||||
*
|
||||
* The total of ITEMSIZE * NTRIES is intended to exceed the size of
|
||||
* available memory.
|
||||
*
|
||||
* kmallocstress does the same thing, but from NTHREADS different
|
||||
* threads at once.
|
||||
*/
|
||||
|
||||
#define NTRIES 1200
|
||||
#define ITEMSIZE 997
|
||||
#define NTHREADS 8
|
||||
|
||||
static
|
||||
void
|
||||
kmallocthread(void *sm, unsigned long num)
|
||||
{
|
||||
struct semaphore *sem = sm;
|
||||
void *ptr;
|
||||
void *oldptr=NULL;
|
||||
void *oldptr2=NULL;
|
||||
int i;
|
||||
|
||||
for (i=0; i<NTRIES; i++) {
|
||||
ptr = kmalloc(ITEMSIZE);
|
||||
if (ptr==NULL) {
|
||||
if (sem) {
|
||||
kprintf("thread %lu: kmalloc returned NULL\n",
|
||||
num);
|
||||
goto done;
|
||||
}
|
||||
kprintf("kmalloc returned null; test failed.\n");
|
||||
goto done;
|
||||
}
|
||||
if (oldptr2) {
|
||||
kfree(oldptr2);
|
||||
}
|
||||
oldptr2 = oldptr;
|
||||
oldptr = ptr;
|
||||
}
|
||||
done:
|
||||
if (oldptr2) {
|
||||
kfree(oldptr2);
|
||||
}
|
||||
if (oldptr) {
|
||||
kfree(oldptr);
|
||||
}
|
||||
if (sem) {
|
||||
V(sem);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
kmalloctest(int nargs, char **args)
|
||||
{
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Starting kmalloc test...\n");
|
||||
kmallocthread(NULL, 0);
|
||||
kprintf("kmalloc test done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
kmallocstress(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
int i, result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
sem = sem_create("kmallocstress", 0);
|
||||
if (sem == NULL) {
|
||||
panic("kmallocstress: sem_create failed\n");
|
||||
}
|
||||
|
||||
kprintf("Starting kmalloc stress test...\n");
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
result = thread_fork("kmallocstress", NULL,
|
||||
kmallocthread, sem, i);
|
||||
if (result) {
|
||||
panic("kmallocstress: thread_fork failed: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
P(sem);
|
||||
}
|
||||
|
||||
sem_destroy(sem);
|
||||
kprintf("kmalloc stress test done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// km3
|
||||
|
||||
/*
|
||||
* Larger kmalloc test. Or at least, potentially larger. The size is
|
||||
* an argument.
|
||||
*
|
||||
* The argument specifies the number of objects to allocate; the size
|
||||
* of each allocation rotates through sizes[]. (FUTURE: should there
|
||||
* be a mode that allocates random sizes?) In order to hold the
|
||||
* pointers returned by kmalloc we first allocate a two-level radix
|
||||
* tree whose lower tier is made up of blocks of size PAGE_SIZE/4.
|
||||
* (This is so they all go to the subpage allocator rather than being
|
||||
* whole-page allocations.)
|
||||
*
|
||||
* Since PAGE_SIZE is commonly 4096, each of these blocks holds 1024
|
||||
* pointers (on a 32-bit machine) or 512 (on a 64-bit machine) and so
|
||||
* we can store considerably more pointers than we have memory for
|
||||
* before the upper tier becomes a whole page or otherwise gets
|
||||
* uncomfortably large.
|
||||
*
|
||||
* Having set this up, the test just allocates and then frees all the
|
||||
* pointers in order, setting and checking the contents.
|
||||
*/
|
||||
int
|
||||
kmalloctest3(int nargs, char **args)
|
||||
{
|
||||
#define NUM_KM3_SIZES 5
|
||||
static const unsigned sizes[NUM_KM3_SIZES] = { 32, 41, 109, 86, 9 };
|
||||
unsigned numptrs;
|
||||
size_t ptrspace;
|
||||
size_t blocksize;
|
||||
unsigned numptrblocks;
|
||||
void ***ptrblocks;
|
||||
unsigned curblock, curpos, cursizeindex, cursize;
|
||||
size_t totalsize;
|
||||
unsigned i, j;
|
||||
unsigned char *ptr;
|
||||
|
||||
if (nargs != 2) {
|
||||
kprintf("kmalloctest3: usage: km3 numobjects\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Figure out how many pointers we'll get and the space they need. */
|
||||
numptrs = atoi(args[1]);
|
||||
ptrspace = numptrs * sizeof(void *);
|
||||
|
||||
/* Figure out how many blocks in the lower tier. */
|
||||
blocksize = PAGE_SIZE / 4;
|
||||
numptrblocks = DIVROUNDUP(ptrspace, blocksize);
|
||||
|
||||
kprintf("kmalloctest3: %u objects, %u pointer blocks\n",
|
||||
numptrs, numptrblocks);
|
||||
|
||||
/* Allocate the upper tier. */
|
||||
ptrblocks = kmalloc(numptrblocks * sizeof(ptrblocks[0]));
|
||||
if (ptrblocks == NULL) {
|
||||
panic("kmalloctest3: failed on pointer block array\n");
|
||||
}
|
||||
/* Allocate the lower tier. */
|
||||
for (i=0; i<numptrblocks; i++) {
|
||||
ptrblocks[i] = kmalloc(blocksize);
|
||||
if (ptrblocks[i] == NULL) {
|
||||
panic("kmalloctest3: failed on pointer block %u\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the objects. */
|
||||
curblock = 0;
|
||||
curpos = 0;
|
||||
cursizeindex = 0;
|
||||
totalsize = 0;
|
||||
for (i=0; i<numptrs; i++) {
|
||||
cursize = sizes[cursizeindex];
|
||||
ptr = kmalloc(cursize);
|
||||
if (ptr == NULL) {
|
||||
kprintf("kmalloctest3: failed on object %u size %u\n",
|
||||
i, cursize);
|
||||
kprintf("kmalloctest3: pos %u in pointer block %u\n",
|
||||
curpos, curblock);
|
||||
kprintf("kmalloctest3: total so far %zu\n", totalsize);
|
||||
panic("kmalloctest3: failed.\n");
|
||||
}
|
||||
/* Fill the object with its number. */
|
||||
for (j=0; j<cursize; j++) {
|
||||
ptr[j] = (unsigned char) i;
|
||||
}
|
||||
/* Move to the next slot in the tree. */
|
||||
ptrblocks[curblock][curpos] = ptr;
|
||||
curpos++;
|
||||
if (curpos >= blocksize / sizeof(void *)) {
|
||||
curblock++;
|
||||
curpos = 0;
|
||||
}
|
||||
/* Update the running total, and rotate the size. */
|
||||
totalsize += cursize;
|
||||
cursizeindex = (cursizeindex + 1) % NUM_KM3_SIZES;
|
||||
}
|
||||
|
||||
kprintf("kmalloctest3: %zu bytes allocated\n", totalsize);
|
||||
|
||||
/* Free the objects. */
|
||||
curblock = 0;
|
||||
curpos = 0;
|
||||
cursizeindex = 0;
|
||||
for (i=0; i<numptrs; i++) {
|
||||
cursize = sizes[cursizeindex];
|
||||
ptr = ptrblocks[curblock][curpos];
|
||||
KASSERT(ptr != NULL);
|
||||
for (j=0; j<cursize; j++) {
|
||||
if (ptr[j] == (unsigned char) i) {
|
||||
continue;
|
||||
}
|
||||
kprintf("kmalloctest3: failed on object %u size %u\n",
|
||||
i, cursize);
|
||||
kprintf("kmalloctest3: pos %u in pointer block %u\n",
|
||||
curpos, curblock);
|
||||
kprintf("kmalloctest3: at object offset %u\n", j);
|
||||
kprintf("kmalloctest3: expected 0x%x, found 0x%x\n",
|
||||
ptr[j], (unsigned char) i);
|
||||
panic("kmalloctest3: failed.\n");
|
||||
}
|
||||
kfree(ptr);
|
||||
curpos++;
|
||||
if (curpos >= blocksize / sizeof(void *)) {
|
||||
curblock++;
|
||||
curpos = 0;
|
||||
}
|
||||
KASSERT(totalsize > 0);
|
||||
totalsize -= cursize;
|
||||
cursizeindex = (cursizeindex + 1) % NUM_KM3_SIZES;
|
||||
}
|
||||
KASSERT(totalsize == 0);
|
||||
|
||||
/* Free the lower tier. */
|
||||
for (i=0; i<numptrblocks; i++) {
|
||||
KASSERT(ptrblocks[i] != NULL);
|
||||
kfree(ptrblocks[i]);
|
||||
}
|
||||
/* Free the upper tier. */
|
||||
kfree(ptrblocks);
|
||||
|
||||
kprintf("kmalloctest3: passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// km4
|
||||
|
||||
static
|
||||
void
|
||||
kmalloctest4thread(void *sm, unsigned long num)
|
||||
{
|
||||
#define NUM_KM4_SIZES 5
|
||||
static const unsigned sizes[NUM_KM4_SIZES] = { 1, 3, 5, 2, 4 };
|
||||
|
||||
struct semaphore *sem = sm;
|
||||
void *ptrs[NUM_KM4_SIZES];
|
||||
unsigned p, q;
|
||||
unsigned i;
|
||||
|
||||
for (i=0; i<NUM_KM4_SIZES; i++) {
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
p = 0;
|
||||
q = NUM_KM4_SIZES / 2;
|
||||
|
||||
for (i=0; i<NTRIES; i++) {
|
||||
if (ptrs[q] != NULL) {
|
||||
kfree(ptrs[q]);
|
||||
ptrs[q] = NULL;
|
||||
}
|
||||
ptrs[p] = kmalloc(sizes[p] * PAGE_SIZE);
|
||||
if (ptrs[p] == NULL) {
|
||||
panic("kmalloctest4: thread %lu: "
|
||||
"allocating %u pages failed\n",
|
||||
num, sizes[p]);
|
||||
}
|
||||
p = (p + 1) % NUM_KM4_SIZES;
|
||||
q = (q + 1) % NUM_KM4_SIZES;
|
||||
}
|
||||
|
||||
for (i=0; i<NUM_KM4_SIZES; i++) {
|
||||
if (ptrs[i] != NULL) {
|
||||
kfree(ptrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
V(sem);
|
||||
}
|
||||
|
||||
int
|
||||
kmalloctest4(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
unsigned nthreads;
|
||||
unsigned i;
|
||||
int result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Starting multipage kmalloc test...\n");
|
||||
#if OPT_DUMBVM
|
||||
kprintf("(This test will not work with dumbvm)\n");
|
||||
#endif
|
||||
|
||||
sem = sem_create("kmalloctest4", 0);
|
||||
if (sem == NULL) {
|
||||
panic("kmalloctest4: sem_create failed\n");
|
||||
}
|
||||
|
||||
/* use 6 instead of 8 threads */
|
||||
nthreads = (3*NTHREADS)/4;
|
||||
|
||||
for (i=0; i<nthreads; i++) {
|
||||
result = thread_fork("kmalloctest4", NULL,
|
||||
kmalloctest4thread, sem, i);
|
||||
if (result) {
|
||||
panic("kmallocstress: thread_fork failed: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<nthreads; i++) {
|
||||
P(sem);
|
||||
}
|
||||
|
||||
sem_destroy(sem);
|
||||
kprintf("Multipage kmalloc test done\n");
|
||||
return 0;
|
||||
}
|
44
kern/test/nettest.c
Normal file
44
kern/test/nettest.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Network test code.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <test.h>
|
||||
|
||||
int
|
||||
nettest(int nargs, char **args)
|
||||
{
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
kprintf("No network support available\n");
|
||||
return 1;
|
||||
}
|
820
kern/test/semunit.c
Normal file
820
kern/test/semunit.c
Normal file
@@ -0,0 +1,820 @@
|
||||
/*
|
||||
* Copyright (c) 2015
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <spinlock.h>
|
||||
#include <synch.h>
|
||||
#include <thread.h>
|
||||
#include <current.h>
|
||||
#include <clock.h>
|
||||
#include <test.h>
|
||||
|
||||
/*
|
||||
* Unit tests for semaphores.
|
||||
*
|
||||
* We test 21 correctness criteria, each stated in a comment at the
|
||||
* top of each test.
|
||||
*
|
||||
* Note that these tests go inside the semaphore abstraction to
|
||||
* validate the internal state.
|
||||
*
|
||||
* All tests (apart from those that crash) attempt to clean up after
|
||||
* running, to avoid leaking memory and leaving extra threads lying
|
||||
* around. Tests with a cleanup phase call ok() before going to it in
|
||||
* case the cleanup crashes -- this should not happen of course but if
|
||||
* it does it should be distinguished from the main part of the test
|
||||
* itself dying.
|
||||
*/
|
||||
|
||||
#define NAMESTRING "some-silly-name"
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// support code
|
||||
|
||||
static unsigned waiters_running = 0;
|
||||
static struct spinlock waiters_lock = SPINLOCK_INITIALIZER;
|
||||
|
||||
static
|
||||
void
|
||||
ok(void)
|
||||
{
|
||||
kprintf("Test passed; now cleaning up.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for sem_create when we aren't explicitly tweaking it.
|
||||
*/
|
||||
static
|
||||
struct semaphore *
|
||||
makesem(unsigned count)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
sem = sem_create(NAMESTRING, count);
|
||||
if (sem == NULL) {
|
||||
panic("semunit: whoops: sem_create failed\n");
|
||||
}
|
||||
return sem;
|
||||
}
|
||||
|
||||
/*
|
||||
* A thread that just waits on a semaphore.
|
||||
*/
|
||||
static
|
||||
void
|
||||
waiter(void *vsem, unsigned long junk)
|
||||
{
|
||||
struct semaphore *sem = vsem;
|
||||
(void)junk;
|
||||
|
||||
P(sem);
|
||||
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running > 0);
|
||||
waiters_running--;
|
||||
spinlock_release(&waiters_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a waiter.
|
||||
*/
|
||||
static
|
||||
void
|
||||
makewaiter(struct semaphore *sem)
|
||||
{
|
||||
int result;
|
||||
|
||||
spinlock_acquire(&waiters_lock);
|
||||
waiters_running++;
|
||||
spinlock_release(&waiters_lock);
|
||||
|
||||
result = thread_fork("semunit waiter", NULL, waiter, sem, 0);
|
||||
if (result) {
|
||||
panic("semunit: thread_fork failed\n");
|
||||
}
|
||||
kprintf("Sleeping for waiter to run\n");
|
||||
clocksleep(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Spinlocks don't natively provide this, because it only makes sense
|
||||
* under controlled conditions.
|
||||
*
|
||||
* Note that we should really read the holder atomically; but because
|
||||
* we're using this under controlled conditions, it doesn't actually
|
||||
* matter -- nobody is supposed to be able to touch the holder while
|
||||
* we're checking it, or the check wouldn't be reliable; and, provided
|
||||
* clocksleep works, nobody can.
|
||||
*/
|
||||
static
|
||||
bool
|
||||
spinlock_not_held(struct spinlock *splk)
|
||||
{
|
||||
return splk->splk_holder == NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// tests
|
||||
|
||||
/*
|
||||
* 1. After a successful sem_create:
|
||||
* - sem_name compares equal to the passed-in name
|
||||
* - sem_name is not the same pointer as the passed-in name
|
||||
* - sem_wchan is not null
|
||||
* - sem_lock is not held and has no owner
|
||||
* - sem_count is the passed-in count
|
||||
*/
|
||||
int
|
||||
semu1(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
const char *name = NAMESTRING;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = sem_create(name, 56);
|
||||
if (sem == NULL) {
|
||||
panic("semu1: whoops: sem_create failed\n");
|
||||
}
|
||||
KASSERT(!strcmp(sem->sem_name, name));
|
||||
KASSERT(sem->sem_name != name);
|
||||
KASSERT(sem->sem_wchan != NULL);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 56);
|
||||
|
||||
ok();
|
||||
/* clean up */
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. Passing a null name to sem_create asserts or crashes.
|
||||
*/
|
||||
int
|
||||
semu2(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should crash with a kernel null dereference\n");
|
||||
sem = sem_create(NULL, 44);
|
||||
(void)sem;
|
||||
panic("semu2: sem_create accepted a null name\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Passing a null semaphore to sem_destroy asserts or crashes.
|
||||
*/
|
||||
int
|
||||
semu3(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that sem != NULL\n");
|
||||
sem_destroy(NULL);
|
||||
panic("semu3: sem_destroy accepted a null semaphore\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 4. sem_count is an unsigned type.
|
||||
*/
|
||||
int
|
||||
semu4(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
/* Create a semaphore with count 0. */
|
||||
sem = makesem(0);
|
||||
/* Decrement the count. */
|
||||
sem->sem_count--;
|
||||
/* This value should be positive. */
|
||||
KASSERT(sem->sem_count > 0);
|
||||
|
||||
/* Clean up. */
|
||||
ok();
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 5. A semaphore can be successfully initialized with a count of at
|
||||
* least 0xf0000000.
|
||||
*/
|
||||
int
|
||||
semu5(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = sem_create(NAMESTRING, 0xf0000000U);
|
||||
if (sem == NULL) {
|
||||
/* This might not be an innocuous malloc shortage. */
|
||||
panic("semu5: sem_create failed\n");
|
||||
}
|
||||
KASSERT(sem->sem_count == 0xf0000000U);
|
||||
|
||||
/* Clean up. */
|
||||
ok();
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 6. Passing a semaphore with a waiting thread to sem_destroy asserts
|
||||
* (in the wchan code).
|
||||
*/
|
||||
int
|
||||
semu6(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = makesem(0);
|
||||
makewaiter(sem);
|
||||
kprintf("This should assert that the wchan's threadlist is empty\n");
|
||||
sem_destroy(sem);
|
||||
panic("semu6: wchan_destroy with waiters succeeded\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 7. Calling V on a semaphore does not block the caller, regardless
|
||||
* of the semaphore count.
|
||||
*/
|
||||
int
|
||||
semu7(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct spinlock lk;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = makesem(0);
|
||||
|
||||
/*
|
||||
* Check for blocking by taking a spinlock; if we block while
|
||||
* holding a spinlock, wchan_sleep will assert.
|
||||
*/
|
||||
spinlock_init(&lk);
|
||||
spinlock_acquire(&lk);
|
||||
|
||||
/* try with count 0, count 1, and count 2, just for completeness */
|
||||
V(sem);
|
||||
V(sem);
|
||||
V(sem);
|
||||
|
||||
/* Clean up. */
|
||||
ok();
|
||||
spinlock_release(&lk);
|
||||
spinlock_cleanup(&lk);
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 8/9. After calling V on a semaphore with no threads waiting:
|
||||
* - sem_name is unchanged
|
||||
* - sem_wchan is unchanged
|
||||
* - sem_lock is (still) unheld and has no owner
|
||||
* - sem_count is increased by one
|
||||
*
|
||||
* This is true even if we are in an interrupt handler.
|
||||
*/
|
||||
static
|
||||
void
|
||||
do_semu89(bool interrupthandler)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct wchan *wchan;
|
||||
const char *name;
|
||||
|
||||
sem = makesem(0);
|
||||
|
||||
/* check preconditions */
|
||||
name = sem->sem_name;
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
|
||||
/*
|
||||
* The right way to this is to set up an actual interrupt,
|
||||
* e.g. an interprocessor interrupt, and hook onto it to run
|
||||
* the V() in the actual interrupt handler. However, that
|
||||
* requires a good bit of infrastructure that we don't
|
||||
* have. Instead we'll fake it by explicitly setting
|
||||
* curthread->t_in_interrupt.
|
||||
*/
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == false);
|
||||
curthread->t_in_interrupt = true;
|
||||
}
|
||||
|
||||
V(sem);
|
||||
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == true);
|
||||
curthread->t_in_interrupt = false;
|
||||
}
|
||||
|
||||
/* check postconditions */
|
||||
KASSERT(name == sem->sem_name);
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(wchan == sem->sem_wchan);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 1);
|
||||
|
||||
/* clean up */
|
||||
ok();
|
||||
sem_destroy(sem);
|
||||
}
|
||||
|
||||
int
|
||||
semu8(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
do_semu89(false /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
semu9(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
do_semu89(true /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 10/11. After calling V on a semaphore with one thread waiting, and giving
|
||||
* it time to run:
|
||||
* - sem_name is unchanged
|
||||
* - sem_wchan is unchanged
|
||||
* - sem_lock is (still) unheld and has no owner
|
||||
* - sem_count is still 0
|
||||
* - the other thread does in fact run
|
||||
*
|
||||
* This is true even if we are in an interrupt handler.
|
||||
*/
|
||||
static
|
||||
int
|
||||
do_semu1011(bool interrupthandler)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct wchan *wchan;
|
||||
const char *name;
|
||||
|
||||
sem = makesem(0);
|
||||
makewaiter(sem);
|
||||
|
||||
/* check preconditions */
|
||||
name = sem->sem_name;
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running == 1);
|
||||
spinlock_release(&waiters_lock);
|
||||
|
||||
/* see above */
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == false);
|
||||
curthread->t_in_interrupt = true;
|
||||
}
|
||||
|
||||
V(sem);
|
||||
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == true);
|
||||
curthread->t_in_interrupt = false;
|
||||
}
|
||||
|
||||
/* give the waiter time to exit */
|
||||
clocksleep(1);
|
||||
|
||||
/* check postconditions */
|
||||
KASSERT(name == sem->sem_name);
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(wchan == sem->sem_wchan);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 0);
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running == 0);
|
||||
spinlock_release(&waiters_lock);
|
||||
|
||||
/* clean up */
|
||||
ok();
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
semu10(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
do_semu1011(false /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
semu11(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
do_semu1011(true /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 12/13. After calling V on a semaphore with two threads waiting, and
|
||||
* giving it time to run:
|
||||
* - sem_name is unchanged
|
||||
* - sem_wchan is unchanged
|
||||
* - sem_lock is (still) unheld and has no owner
|
||||
* - sem_count is still 0
|
||||
* - one of the other threads does in fact run
|
||||
* - the other one does not
|
||||
*/
|
||||
static
|
||||
void
|
||||
semu1213(bool interrupthandler)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct wchan *wchan;
|
||||
const char *name;
|
||||
|
||||
sem = makesem(0);
|
||||
makewaiter(sem);
|
||||
makewaiter(sem);
|
||||
|
||||
/* check preconditions */
|
||||
name = sem->sem_name;
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running == 2);
|
||||
spinlock_release(&waiters_lock);
|
||||
|
||||
/* see above */
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == false);
|
||||
curthread->t_in_interrupt = true;
|
||||
}
|
||||
|
||||
V(sem);
|
||||
|
||||
if (interrupthandler) {
|
||||
KASSERT(curthread->t_in_interrupt == true);
|
||||
curthread->t_in_interrupt = false;
|
||||
}
|
||||
|
||||
/* give the waiter time to exit */
|
||||
clocksleep(1);
|
||||
|
||||
/* check postconditions */
|
||||
KASSERT(name == sem->sem_name);
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(wchan == sem->sem_wchan);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 0);
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running == 1);
|
||||
spinlock_release(&waiters_lock);
|
||||
|
||||
/* clean up */
|
||||
ok();
|
||||
V(sem);
|
||||
clocksleep(1);
|
||||
spinlock_acquire(&waiters_lock);
|
||||
KASSERT(waiters_running == 0);
|
||||
spinlock_release(&waiters_lock);
|
||||
sem_destroy(sem);
|
||||
}
|
||||
|
||||
int
|
||||
semu12(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
semu1213(false /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
semu13(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
semu1213(true /*interrupthandler*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 14. Calling V on a semaphore whose count is the maximum allowed value
|
||||
* asserts.
|
||||
*/
|
||||
int
|
||||
semu14(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that sem_count is > 0.\n");
|
||||
sem = makesem(0);
|
||||
|
||||
/*
|
||||
* The maximum value is (unsigned)-1. Get this by decrementing
|
||||
* from 0.
|
||||
*/
|
||||
sem->sem_count--;
|
||||
V(sem);
|
||||
KASSERT(sem->sem_count == 0);
|
||||
panic("semu14: V tolerated count wraparound\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 15. Calling V on a null semaphore asserts.
|
||||
*/
|
||||
int
|
||||
semu15(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that the semaphore isn't null.\n");
|
||||
V(NULL);
|
||||
panic("semu15: V tolerated null semaphore\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 16. Calling P on a semaphore with count > 0 does not block the caller.
|
||||
*/
|
||||
int
|
||||
semu16(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct spinlock lk;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = makesem(1);
|
||||
|
||||
/* As above, check for improper blocking by taking a spinlock. */
|
||||
spinlock_init(&lk);
|
||||
spinlock_acquire(&lk);
|
||||
|
||||
P(sem);
|
||||
|
||||
ok();
|
||||
spinlock_release(&lk);
|
||||
spinlock_cleanup(&lk);
|
||||
sem_destroy(sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 17. Calling P on a semaphore with count == 0 does block the caller.
|
||||
*/
|
||||
|
||||
static struct thread *semu17_thread;
|
||||
|
||||
static
|
||||
void
|
||||
semu17_sub(void *semv, unsigned long junk)
|
||||
{
|
||||
struct semaphore *sem = semv;
|
||||
|
||||
(void)junk;
|
||||
|
||||
semu17_thread = curthread;
|
||||
|
||||
/* precondition */
|
||||
KASSERT(sem->sem_count == 0);
|
||||
|
||||
P(sem);
|
||||
}
|
||||
|
||||
int
|
||||
semu17(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
int result;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
semu17_thread = NULL;
|
||||
|
||||
sem = makesem(0);
|
||||
result = thread_fork("semu17_sub", NULL, semu17_sub, sem, 0);
|
||||
if (result) {
|
||||
panic("semu17: whoops: thread_fork failed\n");
|
||||
}
|
||||
kprintf("Waiting for subthread...\n");
|
||||
clocksleep(1);
|
||||
|
||||
/* The subthread should be blocked. */
|
||||
KASSERT(semu17_thread != NULL);
|
||||
KASSERT(semu17_thread->t_state == S_SLEEP);
|
||||
|
||||
/* Clean up. */
|
||||
ok();
|
||||
V(sem);
|
||||
clocksleep(1);
|
||||
sem_destroy(sem);
|
||||
semu17_thread = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 18. After calling P on a semaphore with count > 0:
|
||||
* - sem_name is unchanged
|
||||
* - sem_wchan is unchanged
|
||||
* - sem_lock is unheld and has no owner
|
||||
* - sem_count is one less
|
||||
*/
|
||||
int
|
||||
semu18(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct wchan *wchan;
|
||||
const char *name;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = makesem(1);
|
||||
|
||||
/* preconditions */
|
||||
name = sem->sem_name;
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 1);
|
||||
|
||||
P(sem);
|
||||
|
||||
/* postconditions */
|
||||
KASSERT(name == sem->sem_name);
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(wchan == sem->sem_wchan);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 19. After calling P on a semaphore with count == 0 and another
|
||||
* thread uses V exactly once to cause a wakeup:
|
||||
* - sem_name is unchanged
|
||||
* - sem_wchan is unchanged
|
||||
* - sem_lock is unheld and has no owner
|
||||
* - sem_count is still 0
|
||||
*/
|
||||
|
||||
static
|
||||
void
|
||||
semu19_sub(void *semv, unsigned long junk)
|
||||
{
|
||||
struct semaphore *sem = semv;
|
||||
|
||||
(void)junk;
|
||||
|
||||
kprintf("semu19: waiting for parent to sleep\n");
|
||||
clocksleep(1);
|
||||
/*
|
||||
* We could assert here that the parent *is* sleeping; but for
|
||||
* that we'd need its thread pointer and it's not worth the
|
||||
* trouble.
|
||||
*/
|
||||
V(sem);
|
||||
}
|
||||
|
||||
int
|
||||
semu19(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
struct wchan *wchan;
|
||||
const char *name;
|
||||
int result;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
sem = makesem(0);
|
||||
result = thread_fork("semu19_sub", NULL, semu19_sub, sem, 0);
|
||||
if (result) {
|
||||
panic("semu19: whoops: thread_fork failed\n");
|
||||
}
|
||||
|
||||
/* preconditions */
|
||||
name = sem->sem_name;
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
wchan = sem->sem_wchan;
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 0);
|
||||
|
||||
P(sem);
|
||||
|
||||
/* postconditions */
|
||||
KASSERT(name == sem->sem_name);
|
||||
KASSERT(!strcmp(name, NAMESTRING));
|
||||
KASSERT(wchan == sem->sem_wchan);
|
||||
KASSERT(spinlock_not_held(&sem->sem_lock));
|
||||
KASSERT(sem->sem_count == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 20/21. Calling P in an interrupt handler asserts, regardless of the
|
||||
* count.
|
||||
*/
|
||||
int
|
||||
semu20(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that we aren't in an interrupt\n");
|
||||
|
||||
sem = makesem(0);
|
||||
/* as above */
|
||||
curthread->t_in_interrupt = true;
|
||||
P(sem);
|
||||
panic("semu20: P tolerated being in an interrupt handler\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
semu21(int nargs, char **args)
|
||||
{
|
||||
struct semaphore *sem;
|
||||
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that we aren't in an interrupt\n");
|
||||
|
||||
sem = makesem(1);
|
||||
/* as above */
|
||||
curthread->t_in_interrupt = true;
|
||||
P(sem);
|
||||
panic("semu21: P tolerated being in an interrupt handler\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 22. Calling P on a null semaphore asserts.
|
||||
*/
|
||||
int
|
||||
semu22(int nargs, char **args)
|
||||
{
|
||||
(void)nargs; (void)args;
|
||||
|
||||
kprintf("This should assert that the semaphore isn't null.\n");
|
||||
P(NULL);
|
||||
panic("semu22: P tolerated null semaphore\n");
|
||||
return 0;
|
||||
}
|
401
kern/test/synchtest.c
Normal file
401
kern/test/synchtest.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Synchronization test code.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <clock.h>
|
||||
#include <thread.h>
|
||||
#include <synch.h>
|
||||
#include <test.h>
|
||||
|
||||
#define NSEMLOOPS 63
|
||||
#define NLOCKLOOPS 120
|
||||
#define NCVLOOPS 5
|
||||
#define NTHREADS 32
|
||||
|
||||
static volatile unsigned long testval1;
|
||||
static volatile unsigned long testval2;
|
||||
static volatile unsigned long testval3;
|
||||
static struct semaphore *testsem;
|
||||
static struct lock *testlock;
|
||||
static struct cv *testcv;
|
||||
static struct semaphore *donesem;
|
||||
|
||||
static
|
||||
void
|
||||
inititems(void)
|
||||
{
|
||||
if (testsem==NULL) {
|
||||
testsem = sem_create("testsem", 2);
|
||||
if (testsem == NULL) {
|
||||
panic("synchtest: sem_create failed\n");
|
||||
}
|
||||
}
|
||||
if (testlock==NULL) {
|
||||
testlock = lock_create("testlock");
|
||||
if (testlock == NULL) {
|
||||
panic("synchtest: lock_create failed\n");
|
||||
}
|
||||
}
|
||||
if (testcv==NULL) {
|
||||
testcv = cv_create("testlock");
|
||||
if (testcv == NULL) {
|
||||
panic("synchtest: cv_create failed\n");
|
||||
}
|
||||
}
|
||||
if (donesem==NULL) {
|
||||
donesem = sem_create("donesem", 0);
|
||||
if (donesem == NULL) {
|
||||
panic("synchtest: sem_create failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
semtestthread(void *junk, unsigned long num)
|
||||
{
|
||||
int i;
|
||||
(void)junk;
|
||||
|
||||
/*
|
||||
* Only one of these should print at a time.
|
||||
*/
|
||||
P(testsem);
|
||||
kprintf("Thread %2lu: ", num);
|
||||
for (i=0; i<NSEMLOOPS; i++) {
|
||||
kprintf("%c", (int)num+64);
|
||||
}
|
||||
kprintf("\n");
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
int
|
||||
semtest(int nargs, char **args)
|
||||
{
|
||||
int i, result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
inititems();
|
||||
kprintf("Starting semaphore test...\n");
|
||||
kprintf("If this hangs, it's broken: ");
|
||||
P(testsem);
|
||||
P(testsem);
|
||||
kprintf("ok\n");
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
result = thread_fork("semtest", NULL, semtestthread, NULL, i);
|
||||
if (result) {
|
||||
panic("semtest: thread_fork failed: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
V(testsem);
|
||||
P(donesem);
|
||||
}
|
||||
|
||||
/* so we can run it again */
|
||||
V(testsem);
|
||||
V(testsem);
|
||||
|
||||
kprintf("Semaphore test done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fail(unsigned long num, const char *msg)
|
||||
{
|
||||
kprintf("thread %lu: Mismatch on %s\n", num, msg);
|
||||
kprintf("Test failed\n");
|
||||
|
||||
lock_release(testlock);
|
||||
|
||||
V(donesem);
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
locktestthread(void *junk, unsigned long num)
|
||||
{
|
||||
int i;
|
||||
(void)junk;
|
||||
|
||||
for (i=0; i<NLOCKLOOPS; i++) {
|
||||
lock_acquire(testlock);
|
||||
testval1 = num;
|
||||
testval2 = num*num;
|
||||
testval3 = num%3;
|
||||
|
||||
if (testval2 != testval1*testval1) {
|
||||
fail(num, "testval2/testval1");
|
||||
}
|
||||
|
||||
if (testval2%3 != (testval3*testval3)%3) {
|
||||
fail(num, "testval2/testval3");
|
||||
}
|
||||
|
||||
if (testval3 != testval1%3) {
|
||||
fail(num, "testval3/testval1");
|
||||
}
|
||||
|
||||
if (testval1 != num) {
|
||||
fail(num, "testval1/num");
|
||||
}
|
||||
|
||||
if (testval2 != num*num) {
|
||||
fail(num, "testval2/num");
|
||||
}
|
||||
|
||||
if (testval3 != num%3) {
|
||||
fail(num, "testval3/num");
|
||||
}
|
||||
|
||||
lock_release(testlock);
|
||||
}
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
locktest(int nargs, char **args)
|
||||
{
|
||||
int i, result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
inititems();
|
||||
kprintf("Starting lock test...\n");
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
result = thread_fork("synchtest", NULL, locktestthread,
|
||||
NULL, i);
|
||||
if (result) {
|
||||
panic("locktest: thread_fork failed: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
P(donesem);
|
||||
}
|
||||
|
||||
kprintf("Lock test done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
cvtestthread(void *junk, unsigned long num)
|
||||
{
|
||||
int i;
|
||||
volatile int j;
|
||||
struct timespec ts1, ts2;
|
||||
|
||||
(void)junk;
|
||||
|
||||
for (i=0; i<NCVLOOPS; i++) {
|
||||
lock_acquire(testlock);
|
||||
while (testval1 != num) {
|
||||
gettime(&ts1);
|
||||
cv_wait(testcv, testlock);
|
||||
gettime(&ts2);
|
||||
|
||||
/* ts2 -= ts1 */
|
||||
timespec_sub(&ts2, &ts1, &ts2);
|
||||
|
||||
/* Require at least 2000 cpu cycles (we're 25mhz) */
|
||||
if (ts2.tv_sec == 0 && ts2.tv_nsec < 40*2000) {
|
||||
kprintf("cv_wait took only %u ns\n",
|
||||
ts2.tv_nsec);
|
||||
kprintf("That's too fast... you must be "
|
||||
"busy-looping\n");
|
||||
V(donesem);
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
}
|
||||
kprintf("Thread %lu\n", num);
|
||||
testval1 = (testval1 + NTHREADS - 1)%NTHREADS;
|
||||
|
||||
/*
|
||||
* loop a little while to make sure we can measure the
|
||||
* time waiting on the cv.
|
||||
*/
|
||||
for (j=0; j<3000; j++);
|
||||
|
||||
cv_broadcast(testcv, testlock);
|
||||
lock_release(testlock);
|
||||
}
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
int
|
||||
cvtest(int nargs, char **args)
|
||||
{
|
||||
|
||||
int i, result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
inititems();
|
||||
kprintf("Starting CV test...\n");
|
||||
kprintf("Threads should print out in reverse order.\n");
|
||||
|
||||
testval1 = NTHREADS-1;
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
result = thread_fork("synchtest", NULL, cvtestthread, NULL, i);
|
||||
if (result) {
|
||||
panic("cvtest: thread_fork failed: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
P(donesem);
|
||||
}
|
||||
|
||||
kprintf("CV test done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Try to find out if going to sleep is really atomic.
|
||||
*
|
||||
* What we'll do is rotate through NCVS lock/CV pairs, with one thread
|
||||
* sleeping and the other waking it up. If we miss a wakeup, the sleep
|
||||
* thread won't go around enough times.
|
||||
*/
|
||||
|
||||
#define NCVS 250
|
||||
#define NLOOPS 40
|
||||
static struct cv *testcvs[NCVS];
|
||||
static struct lock *testlocks[NCVS];
|
||||
static struct semaphore *gatesem;
|
||||
static struct semaphore *exitsem;
|
||||
|
||||
static
|
||||
void
|
||||
sleepthread(void *junk1, unsigned long junk2)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
(void)junk1;
|
||||
(void)junk2;
|
||||
|
||||
for (j=0; j<NLOOPS; j++) {
|
||||
for (i=0; i<NCVS; i++) {
|
||||
lock_acquire(testlocks[i]);
|
||||
V(gatesem);
|
||||
cv_wait(testcvs[i], testlocks[i]);
|
||||
lock_release(testlocks[i]);
|
||||
}
|
||||
kprintf("sleepthread: %u\n", j);
|
||||
}
|
||||
V(exitsem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
wakethread(void *junk1, unsigned long junk2)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
(void)junk1;
|
||||
(void)junk2;
|
||||
|
||||
for (j=0; j<NLOOPS; j++) {
|
||||
for (i=0; i<NCVS; i++) {
|
||||
P(gatesem);
|
||||
lock_acquire(testlocks[i]);
|
||||
cv_signal(testcvs[i], testlocks[i]);
|
||||
lock_release(testlocks[i]);
|
||||
}
|
||||
kprintf("wakethread: %u\n", j);
|
||||
}
|
||||
V(exitsem);
|
||||
}
|
||||
|
||||
int
|
||||
cvtest2(int nargs, char **args)
|
||||
{
|
||||
unsigned i;
|
||||
int result;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
for (i=0; i<NCVS; i++) {
|
||||
testlocks[i] = lock_create("cvtest2 lock");
|
||||
testcvs[i] = cv_create("cvtest2 cv");
|
||||
}
|
||||
gatesem = sem_create("gatesem", 0);
|
||||
exitsem = sem_create("exitsem", 0);
|
||||
|
||||
kprintf("cvtest2...\n");
|
||||
|
||||
result = thread_fork("cvtest2", NULL, sleepthread, NULL, 0);
|
||||
if (result) {
|
||||
panic("cvtest2: thread_fork failed\n");
|
||||
}
|
||||
result = thread_fork("cvtest2", NULL, wakethread, NULL, 0);
|
||||
if (result) {
|
||||
panic("cvtest2: thread_fork failed\n");
|
||||
}
|
||||
|
||||
P(exitsem);
|
||||
P(exitsem);
|
||||
|
||||
sem_destroy(exitsem);
|
||||
sem_destroy(gatesem);
|
||||
exitsem = gatesem = NULL;
|
||||
for (i=0; i<NCVS; i++) {
|
||||
lock_destroy(testlocks[i]);
|
||||
cv_destroy(testcvs[i]);
|
||||
testlocks[i] = NULL;
|
||||
testcvs[i] = NULL;
|
||||
}
|
||||
|
||||
kprintf("cvtest2 done\n");
|
||||
return 0;
|
||||
}
|
343
kern/test/threadlisttest.c
Normal file
343
kern/test/threadlisttest.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (c) 2013
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <thread.h>
|
||||
#include <threadlist.h>
|
||||
#include <test.h>
|
||||
|
||||
#define NUMNAMES 7
|
||||
static const char *const names[NUMNAMES] = {
|
||||
"Aillard",
|
||||
"Aldaran",
|
||||
"Alton",
|
||||
"Ardais",
|
||||
"Elhalyn",
|
||||
"Hastur",
|
||||
"Ridenow",
|
||||
};
|
||||
|
||||
static struct thread *fakethreads[NUMNAMES];
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// fakethread
|
||||
|
||||
#define FAKE_MAGIC ((void *)0xbaabaa)
|
||||
|
||||
/*
|
||||
* Create a dummy struct thread that we can put on lists for testing.
|
||||
*/
|
||||
static
|
||||
struct thread *
|
||||
fakethread_create(const char *name)
|
||||
{
|
||||
struct thread *t;
|
||||
|
||||
t = kmalloc(sizeof(*t));
|
||||
if (t == NULL) {
|
||||
panic("threadlisttest: Out of memory\n");
|
||||
}
|
||||
/* ignore most of the fields, zero everything for tidiness */
|
||||
bzero(t, sizeof(*t));
|
||||
t->t_name = kstrdup(name);
|
||||
if (t->t_name == NULL) {
|
||||
panic("threadlisttest: Out of memory\n");
|
||||
}
|
||||
t->t_stack = FAKE_MAGIC;
|
||||
threadlistnode_init(&t->t_listnode, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a fake thread.
|
||||
*/
|
||||
static
|
||||
void
|
||||
fakethread_destroy(struct thread *t)
|
||||
{
|
||||
KASSERT(t->t_stack == FAKE_MAGIC);
|
||||
threadlistnode_cleanup(&t->t_listnode);
|
||||
kfree(t->t_name);
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// support stuff
|
||||
|
||||
static
|
||||
void
|
||||
check_order(struct threadlist *tl, bool rev)
|
||||
{
|
||||
const char string0[] = "...";
|
||||
const char stringN[] = "~~~";
|
||||
|
||||
struct thread *t;
|
||||
const char *first = rev ? stringN : string0;
|
||||
const char *last = rev ? string0 : stringN;
|
||||
const char *prev;
|
||||
int cmp;
|
||||
|
||||
prev = first;
|
||||
THREADLIST_FORALL(t, *tl) {
|
||||
cmp = strcmp(prev, t->t_name);
|
||||
KASSERT(rev ? (cmp > 0) : (cmp < 0));
|
||||
prev = t->t_name;
|
||||
}
|
||||
cmp = strcmp(prev, last);
|
||||
KASSERT(rev ? (cmp > 0) : (cmp < 0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// tests
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_a(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
|
||||
threadlist_init(&tl);
|
||||
KASSERT(threadlist_isempty(&tl));
|
||||
threadlist_cleanup(&tl);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_b(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
struct thread *t;
|
||||
|
||||
threadlist_init(&tl);
|
||||
|
||||
threadlist_addhead(&tl, fakethreads[0]);
|
||||
check_order(&tl, false);
|
||||
check_order(&tl, true);
|
||||
KASSERT(tl.tl_count == 1);
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
|
||||
threadlist_addtail(&tl, fakethreads[0]);
|
||||
check_order(&tl, false);
|
||||
check_order(&tl, true);
|
||||
KASSERT(tl.tl_count == 1);
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
|
||||
threadlist_cleanup(&tl);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_c(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
struct thread *t;
|
||||
|
||||
threadlist_init(&tl);
|
||||
|
||||
threadlist_addhead(&tl, fakethreads[0]);
|
||||
threadlist_addhead(&tl, fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 2);
|
||||
|
||||
check_order(&tl, true);
|
||||
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(t == fakethreads[1]);
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
|
||||
threadlist_addtail(&tl, fakethreads[0]);
|
||||
threadlist_addtail(&tl, fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 2);
|
||||
|
||||
check_order(&tl, false);
|
||||
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(t == fakethreads[1]);
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
|
||||
threadlist_cleanup(&tl);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_d(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
struct thread *t;
|
||||
|
||||
threadlist_init(&tl);
|
||||
|
||||
threadlist_addhead(&tl, fakethreads[0]);
|
||||
threadlist_addtail(&tl, fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 2);
|
||||
|
||||
check_order(&tl, false);
|
||||
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(t == fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
|
||||
threadlist_addhead(&tl, fakethreads[0]);
|
||||
threadlist_addtail(&tl, fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 2);
|
||||
|
||||
check_order(&tl, false);
|
||||
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(t == fakethreads[1]);
|
||||
t = threadlist_remtail(&tl);
|
||||
KASSERT(t == fakethreads[0]);
|
||||
KASSERT(tl.tl_count == 0);
|
||||
|
||||
threadlist_cleanup(&tl);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_e(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
struct thread *t;
|
||||
unsigned i;
|
||||
|
||||
threadlist_init(&tl);
|
||||
|
||||
threadlist_addhead(&tl, fakethreads[1]);
|
||||
threadlist_addtail(&tl, fakethreads[3]);
|
||||
KASSERT(tl.tl_count == 2);
|
||||
check_order(&tl, false);
|
||||
|
||||
threadlist_insertafter(&tl, fakethreads[3], fakethreads[4]);
|
||||
KASSERT(tl.tl_count == 3);
|
||||
check_order(&tl, false);
|
||||
|
||||
threadlist_insertbefore(&tl, fakethreads[0], fakethreads[1]);
|
||||
KASSERT(tl.tl_count == 4);
|
||||
check_order(&tl, false);
|
||||
|
||||
threadlist_insertafter(&tl, fakethreads[1], fakethreads[2]);
|
||||
KASSERT(tl.tl_count == 5);
|
||||
check_order(&tl, false);
|
||||
|
||||
KASSERT(fakethreads[4]->t_listnode.tln_prev->tln_self ==
|
||||
fakethreads[3]);
|
||||
KASSERT(fakethreads[3]->t_listnode.tln_prev->tln_self ==
|
||||
fakethreads[2]);
|
||||
KASSERT(fakethreads[2]->t_listnode.tln_prev->tln_self ==
|
||||
fakethreads[1]);
|
||||
KASSERT(fakethreads[1]->t_listnode.tln_prev->tln_self ==
|
||||
fakethreads[0]);
|
||||
|
||||
for (i=0; i<5; i++) {
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(t == fakethreads[i]);
|
||||
}
|
||||
KASSERT(tl.tl_count == 0);
|
||||
|
||||
threadlist_cleanup(&tl);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
threadlisttest_f(void)
|
||||
{
|
||||
struct threadlist tl;
|
||||
struct thread *t;
|
||||
unsigned i;
|
||||
|
||||
threadlist_init(&tl);
|
||||
|
||||
for (i=0; i<NUMNAMES; i++) {
|
||||
threadlist_addtail(&tl, fakethreads[i]);
|
||||
}
|
||||
KASSERT(tl.tl_count == NUMNAMES);
|
||||
|
||||
i=0;
|
||||
THREADLIST_FORALL(t, tl) {
|
||||
KASSERT(t == fakethreads[i]);
|
||||
i++;
|
||||
}
|
||||
KASSERT(i == NUMNAMES);
|
||||
|
||||
i=0;
|
||||
THREADLIST_FORALL_REV(t, tl) {
|
||||
KASSERT(t == fakethreads[NUMNAMES - i - 1]);
|
||||
i++;
|
||||
}
|
||||
KASSERT(i == NUMNAMES);
|
||||
|
||||
for (i=0; i<NUMNAMES; i++) {
|
||||
t = threadlist_remhead(&tl);
|
||||
KASSERT(t == fakethreads[i]);
|
||||
}
|
||||
KASSERT(tl.tl_count == 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// external interface
|
||||
|
||||
int
|
||||
threadlisttest(int nargs, char **args)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
kprintf("Testing threadlists...\n");
|
||||
|
||||
for (i=0; i<NUMNAMES; i++) {
|
||||
fakethreads[i] = fakethread_create(names[i]);
|
||||
}
|
||||
|
||||
threadlisttest_a();
|
||||
threadlisttest_b();
|
||||
threadlisttest_c();
|
||||
threadlisttest_d();
|
||||
threadlisttest_e();
|
||||
threadlisttest_f();
|
||||
|
||||
for (i=0; i<NUMNAMES; i++) {
|
||||
fakethread_destroy(fakethreads[i]);
|
||||
fakethreads[i] = NULL;
|
||||
}
|
||||
|
||||
kprintf("Done.\n");
|
||||
return 0;
|
||||
}
|
146
kern/test/threadtest.c
Normal file
146
kern/test/threadtest.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Thread test code.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <thread.h>
|
||||
#include <synch.h>
|
||||
#include <test.h>
|
||||
|
||||
#define NTHREADS 8
|
||||
|
||||
static struct semaphore *tsem = NULL;
|
||||
|
||||
static
|
||||
void
|
||||
init_sem(void)
|
||||
{
|
||||
if (tsem==NULL) {
|
||||
tsem = sem_create("tsem", 0);
|
||||
if (tsem == NULL) {
|
||||
panic("threadtest: sem_create failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
loudthread(void *junk, unsigned long num)
|
||||
{
|
||||
int ch = '0' + num;
|
||||
int i;
|
||||
|
||||
(void)junk;
|
||||
|
||||
for (i=0; i<120; i++) {
|
||||
putch(ch);
|
||||
}
|
||||
V(tsem);
|
||||
}
|
||||
|
||||
/*
|
||||
* The idea with this is that you should see
|
||||
*
|
||||
* 01234567 <pause> 01234567
|
||||
*
|
||||
* (possibly with the numbers in different orders)
|
||||
*
|
||||
* The delay loop is supposed to be long enough that it should be clear
|
||||
* if either timeslicing or the scheduler is not working right.
|
||||
*/
|
||||
static
|
||||
void
|
||||
quietthread(void *junk, unsigned long num)
|
||||
{
|
||||
int ch = '0' + num;
|
||||
volatile int i;
|
||||
|
||||
(void)junk;
|
||||
|
||||
putch(ch);
|
||||
for (i=0; i<200000; i++);
|
||||
putch(ch);
|
||||
|
||||
V(tsem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
runthreads(int doloud)
|
||||
{
|
||||
char name[16];
|
||||
int i, result;
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
snprintf(name, sizeof(name), "threadtest%d", i);
|
||||
result = thread_fork(name, NULL,
|
||||
doloud ? loudthread : quietthread,
|
||||
NULL, i);
|
||||
if (result) {
|
||||
panic("threadtest: thread_fork failed %s)\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<NTHREADS; i++) {
|
||||
P(tsem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
threadtest(int nargs, char **args)
|
||||
{
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
init_sem();
|
||||
kprintf("Starting thread test...\n");
|
||||
runthreads(1);
|
||||
kprintf("\nThread test done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
threadtest2(int nargs, char **args)
|
||||
{
|
||||
(void)nargs;
|
||||
(void)args;
|
||||
|
||||
init_sem();
|
||||
kprintf("Starting thread test 2...\n");
|
||||
runthreads(0);
|
||||
kprintf("\nThread test 2 done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
276
kern/test/tt3.c
Normal file
276
kern/test/tt3.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* More thread test code.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <wchan.h>
|
||||
#include <thread.h>
|
||||
#include <synch.h>
|
||||
#include <test.h>
|
||||
|
||||
/* dimension of matrices (cannot be too large or will overflow stack) */
|
||||
|
||||
#define DIM 70
|
||||
|
||||
/* number of iterations for sleepalot threads */
|
||||
#define SLEEPALOT_PRINTS 20 /* number of printouts */
|
||||
#define SLEEPALOT_ITERS 4 /* iterations per printout */
|
||||
/* polling frequency of waker thread */
|
||||
#define WAKER_WAKES 100
|
||||
/* number of iterations per compute thread */
|
||||
#define COMPUTE_ITERS 10
|
||||
|
||||
/* N distinct wait channels */
|
||||
#define NWAITCHANS 12
|
||||
static struct spinlock spinlocks[NWAITCHANS];
|
||||
static struct wchan *waitchans[NWAITCHANS];
|
||||
|
||||
static volatile int wakerdone;
|
||||
static struct semaphore *wakersem;
|
||||
static struct semaphore *donesem;
|
||||
|
||||
static
|
||||
void
|
||||
setup(void)
|
||||
{
|
||||
char tmp[16];
|
||||
int i;
|
||||
|
||||
if (wakersem == NULL) {
|
||||
wakersem = sem_create("wakersem", 1);
|
||||
donesem = sem_create("donesem", 0);
|
||||
for (i=0; i<NWAITCHANS; i++) {
|
||||
spinlock_init(&spinlocks[i]);
|
||||
snprintf(tmp, sizeof(tmp), "wc%d", i);
|
||||
waitchans[i] = wchan_create(kstrdup(tmp));
|
||||
}
|
||||
}
|
||||
wakerdone = 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
sleepalot_thread(void *junk, unsigned long num)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
(void)junk;
|
||||
|
||||
for (i=0; i<SLEEPALOT_PRINTS; i++) {
|
||||
for (j=0; j<SLEEPALOT_ITERS; j++) {
|
||||
unsigned n;
|
||||
struct spinlock *lk;
|
||||
struct wchan *wc;
|
||||
|
||||
n = random() % NWAITCHANS;
|
||||
lk = &spinlocks[n];
|
||||
wc = waitchans[n];
|
||||
spinlock_acquire(lk);
|
||||
wchan_sleep(wc, lk);
|
||||
spinlock_release(lk);
|
||||
}
|
||||
kprintf("[%lu]", num);
|
||||
}
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
waker_thread(void *junk1, unsigned long junk2)
|
||||
{
|
||||
int i, done;
|
||||
|
||||
(void)junk1;
|
||||
(void)junk2;
|
||||
|
||||
while (1) {
|
||||
P(wakersem);
|
||||
done = wakerdone;
|
||||
V(wakersem);
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (i=0; i<WAKER_WAKES; i++) {
|
||||
unsigned n;
|
||||
struct spinlock *lk;
|
||||
struct wchan *wc;
|
||||
|
||||
n = random() % NWAITCHANS;
|
||||
lk = &spinlocks[n];
|
||||
wc = waitchans[n];
|
||||
spinlock_acquire(lk);
|
||||
wchan_wakeall(wc, lk);
|
||||
spinlock_release(lk);
|
||||
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
make_sleepalots(int howmany)
|
||||
{
|
||||
char name[16];
|
||||
int i, result;
|
||||
|
||||
for (i=0; i<howmany; i++) {
|
||||
snprintf(name, sizeof(name), "sleepalot%d", i);
|
||||
result = thread_fork(name, NULL, sleepalot_thread, NULL, i);
|
||||
if (result) {
|
||||
panic("thread_fork failed: %s\n", strerror(result));
|
||||
}
|
||||
}
|
||||
result = thread_fork("waker", NULL, waker_thread, NULL, 0);
|
||||
if (result) {
|
||||
panic("thread_fork failed: %s\n", strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
compute_thread(void *junk1, unsigned long num)
|
||||
{
|
||||
struct matrix {
|
||||
char m[DIM][DIM];
|
||||
};
|
||||
struct matrix *m1, *m2, *m3;
|
||||
unsigned char tot;
|
||||
int i, j, k, m;
|
||||
uint32_t rand;
|
||||
|
||||
(void)junk1;
|
||||
|
||||
m1 = kmalloc(sizeof(struct matrix));
|
||||
KASSERT(m1 != NULL);
|
||||
m2 = kmalloc(sizeof(struct matrix));
|
||||
KASSERT(m2 != NULL);
|
||||
m3 = kmalloc(sizeof(struct matrix));
|
||||
KASSERT(m3 != NULL);
|
||||
|
||||
for (m=0; m<COMPUTE_ITERS; m++) {
|
||||
|
||||
for (i=0; i<DIM; i++) {
|
||||
for (j=0; j<DIM; j++) {
|
||||
rand = random();
|
||||
m1->m[i][j] = rand >> 16;
|
||||
m2->m[i][j] = rand & 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<DIM; i++) {
|
||||
for (j=0; j<DIM; j++) {
|
||||
tot = 0;
|
||||
for (k=0; k<DIM; k++) {
|
||||
tot += m1->m[i][k] * m2->m[k][j];
|
||||
}
|
||||
m3->m[i][j] = tot;
|
||||
}
|
||||
}
|
||||
|
||||
tot = 0;
|
||||
for (i=0; i<DIM; i++) {
|
||||
tot += m3->m[i][i];
|
||||
}
|
||||
|
||||
kprintf("{%lu: %u}", num, (unsigned) tot);
|
||||
thread_yield();
|
||||
}
|
||||
|
||||
kfree(m1);
|
||||
kfree(m2);
|
||||
kfree(m3);
|
||||
|
||||
V(donesem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
make_computes(int howmany)
|
||||
{
|
||||
char name[16];
|
||||
int i, result;
|
||||
|
||||
for (i=0; i<howmany; i++) {
|
||||
snprintf(name, sizeof(name), "compute%d", i);
|
||||
result = thread_fork(name, NULL, compute_thread, NULL, i);
|
||||
if (result) {
|
||||
panic("thread_fork failed: %s\n", strerror(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
finish(int howmanytotal)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<howmanytotal; i++) {
|
||||
P(donesem);
|
||||
}
|
||||
P(wakersem);
|
||||
wakerdone = 1;
|
||||
V(wakersem);
|
||||
P(donesem);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
runtest3(int nsleeps, int ncomputes)
|
||||
{
|
||||
setup();
|
||||
kprintf("Starting thread test 3 (%d [sleepalots], %d {computes}, "
|
||||
"1 waker)\n",
|
||||
nsleeps, ncomputes);
|
||||
make_sleepalots(nsleeps);
|
||||
make_computes(ncomputes);
|
||||
finish(nsleeps+ncomputes);
|
||||
kprintf("\nThread test 3 done\n");
|
||||
}
|
||||
|
||||
int
|
||||
threadtest3(int nargs, char **args)
|
||||
{
|
||||
if (nargs==1) {
|
||||
runtest3(5, 2);
|
||||
}
|
||||
else if (nargs==3) {
|
||||
runtest3(atoi(args[1]), atoi(args[2]));
|
||||
}
|
||||
else {
|
||||
kprintf("Usage: tt3 [sleepthreads computethreads]\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user