log write() data
usertest for big write()s push begin_trans/commit_trans down into syscalls
This commit is contained in:
parent
bd71a45046
commit
2e59046362
7 changed files with 143 additions and 43 deletions
29
file.c
29
file.c
|
@ -67,8 +67,11 @@ fileclose(struct file *f)
|
||||||
|
|
||||||
if(ff.type == FD_PIPE)
|
if(ff.type == FD_PIPE)
|
||||||
pipeclose(ff.pipe, ff.writable);
|
pipeclose(ff.pipe, ff.writable);
|
||||||
else if(ff.type == FD_INODE)
|
else if(ff.type == FD_INODE){
|
||||||
|
begin_trans();
|
||||||
iput(ff.ip);
|
iput(ff.ip);
|
||||||
|
commit_trans();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get metadata about file f.
|
// Get metadata about file f.
|
||||||
|
@ -116,10 +119,30 @@ filewrite(struct file *f, char *addr, int n)
|
||||||
return pipewrite(f->pipe, addr, n);
|
return pipewrite(f->pipe, addr, n);
|
||||||
if(f->type == FD_INODE){
|
if(f->type == FD_INODE){
|
||||||
ilock(f->ip);
|
ilock(f->ip);
|
||||||
if((r = writei(f->ip, addr, f->off, n)) > 0)
|
// write a few blocks at a time to avoid exceeding
|
||||||
|
// the maximum log transaction size, including
|
||||||
|
// i-node, indirect block, allocation blocks,
|
||||||
|
// and 2 blocks of slop for non-aligned writes.
|
||||||
|
// this really belongs lower down, since writei()
|
||||||
|
// might be writing a device like the console.
|
||||||
|
int max = ((LOGSIZE-1-1-2) / 2) * 512;
|
||||||
|
int i = 0;
|
||||||
|
while(i < n){
|
||||||
|
int n1 = n - i;
|
||||||
|
if(n1 > max)
|
||||||
|
n1 = max;
|
||||||
|
begin_trans();
|
||||||
|
r = writei(f->ip, addr + i, f->off, n1);
|
||||||
|
commit_trans();
|
||||||
|
if(r < 0)
|
||||||
|
break;
|
||||||
|
if(r != n1)
|
||||||
|
panic("short filewrite");
|
||||||
f->off += r;
|
f->off += r;
|
||||||
|
i += r;
|
||||||
|
}
|
||||||
iunlock(f->ip);
|
iunlock(f->ip);
|
||||||
return r;
|
return i == n ? n : -1;
|
||||||
}
|
}
|
||||||
panic("filewrite");
|
panic("filewrite");
|
||||||
}
|
}
|
||||||
|
|
4
fs.c
4
fs.c
|
@ -437,13 +437,13 @@ writei(struct inode *ip, char *src, uint off, uint n)
|
||||||
if(off > ip->size || off + n < off)
|
if(off > ip->size || off + n < off)
|
||||||
return -1;
|
return -1;
|
||||||
if(off + n > MAXFILE*BSIZE)
|
if(off + n > MAXFILE*BSIZE)
|
||||||
n = MAXFILE*BSIZE - off;
|
return -1;
|
||||||
|
|
||||||
for(tot=0; tot<n; tot+=m, off+=m, src+=m){
|
for(tot=0; tot<n; tot+=m, off+=m, src+=m){
|
||||||
bp = bread(ip->dev, bmap(ip, off/BSIZE));
|
bp = bread(ip->dev, bmap(ip, off/BSIZE));
|
||||||
m = min(n - tot, BSIZE - off%BSIZE);
|
m = min(n - tot, BSIZE - off%BSIZE);
|
||||||
memmove(bp->data + off%BSIZE, src, m);
|
memmove(bp->data + off%BSIZE, src, m);
|
||||||
bwrite(bp);
|
log_write(bp);
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
log.c
86
log.c
|
@ -8,18 +8,36 @@
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "buf.h"
|
#include "buf.h"
|
||||||
|
|
||||||
// Dirt simple "logging" supporting only one transaction. All file system calls
|
// Simple logging. Each system call that might write the file system
|
||||||
// that potentially write a block should be wrapped in begin_trans and commit_trans,
|
// should be surrounded with begin_trans() and commit_trans() calls.
|
||||||
// so that there is never more than one transaction. This serializes all file system
|
//
|
||||||
// operations that potentially write, but simplifies recovery (only the last
|
// The log holds at most one transaction at a time. Commit forces
|
||||||
// one transaction to recover) and concurrency (don't have to worry about reading a modified
|
// the log (with commit record) to disk, then installs the affected
|
||||||
// block from a transaction that hasn't committed yet).
|
// blocks to disk, then erases the log. begin_trans() ensures that
|
||||||
|
// only one system call can be in a transaction; others must wait.
|
||||||
|
//
|
||||||
|
// Allowing only one transaction at a time means that the file
|
||||||
|
// system code doesn't have to worry about the possibility of
|
||||||
|
// one transaction reading a block that another one has modified,
|
||||||
|
// for example an i-node block.
|
||||||
|
//
|
||||||
|
// Read-only system calls don't need to use transactions, though
|
||||||
|
// this means that they may observe uncommitted data. I-node
|
||||||
|
// and buffer locks prevent read-only calls from seeing inconsistent data.
|
||||||
|
//
|
||||||
|
// The log is a physical re-do log containing disk blocks.
|
||||||
|
// The on-disk log format:
|
||||||
|
// header block, containing sector #s for block A, B, C, ...
|
||||||
|
// block A
|
||||||
|
// block B
|
||||||
|
// block C
|
||||||
|
// ...
|
||||||
|
// Log appends are synchronous.
|
||||||
|
|
||||||
// The header of the log. If head == 0, there are no log entries. All entries till head
|
// Contents of the header block, used for both the on-disk header block
|
||||||
// are committed. sector[] records the home sector for each block in the log
|
// and to keep track in memory of logged sector #s before commit.
|
||||||
// (i.e., physical logging).
|
|
||||||
struct logheader {
|
struct logheader {
|
||||||
int head;
|
int n;
|
||||||
int sector[LOGSIZE];
|
int sector[LOGSIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,10 +73,10 @@ install_trans(void)
|
||||||
{
|
{
|
||||||
int tail;
|
int tail;
|
||||||
|
|
||||||
if (log.lh.head > 0)
|
//if (log.lh.n > 0)
|
||||||
cprintf("install_trans %d\n", log.lh.head);
|
// cprintf("install_trans %d\n", log.lh.n);
|
||||||
for (tail = 0; tail < log.lh.head; tail++) {
|
for (tail = 0; tail < log.lh.n; tail++) {
|
||||||
cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]);
|
// cprintf("put entry %d to disk block %d\n", tail, log.lh.sector[tail]);
|
||||||
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read i'th block from log
|
struct buf *lbuf = bread(log.dev, log.start+tail+1); // read i'th block from log
|
||||||
struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst block
|
struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst block
|
||||||
memmove(dbuf->data, lbuf->data, BSIZE);
|
memmove(dbuf->data, lbuf->data, BSIZE);
|
||||||
|
@ -75,27 +93,27 @@ read_head(void)
|
||||||
struct buf *buf = bread(log.dev, log.start);
|
struct buf *buf = bread(log.dev, log.start);
|
||||||
struct logheader *lh = (struct logheader *) (buf->data);
|
struct logheader *lh = (struct logheader *) (buf->data);
|
||||||
int i;
|
int i;
|
||||||
log.lh.head = lh->head;
|
log.lh.n = lh->n;
|
||||||
for (i = 0; i < log.lh.head; i++) {
|
for (i = 0; i < log.lh.n; i++) {
|
||||||
log.lh.sector[i] = lh->sector[i];
|
log.lh.sector[i] = lh->sector[i];
|
||||||
}
|
}
|
||||||
brelse(buf);
|
brelse(buf);
|
||||||
if (log.lh.head > 0)
|
//if (log.lh.n > 0)
|
||||||
cprintf("read_head: %d\n", log.lh.head);
|
// cprintf("read_head: %d\n", log.lh.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the in-memory log header to disk, committing log entries till head
|
// Write the in-memory log header to disk, committing log entries till head
|
||||||
static void
|
static void
|
||||||
write_head(void)
|
write_head(void)
|
||||||
{
|
{
|
||||||
if (log.lh.head > 0)
|
// if (log.lh.n > 0)
|
||||||
cprintf("write_head: %d\n", log.lh.head);
|
// cprintf("write_head: %d\n", log.lh.n);
|
||||||
|
|
||||||
struct buf *buf = bread(log.dev, log.start);
|
struct buf *buf = bread(log.dev, log.start);
|
||||||
struct logheader *hb = (struct logheader *) (buf->data);
|
struct logheader *hb = (struct logheader *) (buf->data);
|
||||||
int i;
|
int i;
|
||||||
hb->head = log.lh.head;
|
hb->n = log.lh.n;
|
||||||
for (i = 0; i < log.lh.head; i++) {
|
for (i = 0; i < log.lh.n; i++) {
|
||||||
hb->sector[i] = log.lh.sector[i];
|
hb->sector[i] = log.lh.sector[i];
|
||||||
}
|
}
|
||||||
bwrite(buf);
|
bwrite(buf);
|
||||||
|
@ -107,7 +125,7 @@ recover_from_log(void)
|
||||||
{
|
{
|
||||||
read_head();
|
read_head();
|
||||||
install_trans(); // Install all transactions till head
|
install_trans(); // Install all transactions till head
|
||||||
log.lh.head = 0;
|
log.lh.n = 0;
|
||||||
write_head(); // Reclaim log
|
write_head(); // Reclaim log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +145,7 @@ commit_trans(void)
|
||||||
{
|
{
|
||||||
write_head(); // This causes all blocks till log.head to be commited
|
write_head(); // This causes all blocks till log.head to be commited
|
||||||
install_trans(); // Install all the transactions till head
|
install_trans(); // Install all the transactions till head
|
||||||
log.lh.head = 0;
|
log.lh.n = 0;
|
||||||
write_head(); // Reclaim log
|
write_head(); // Reclaim log
|
||||||
|
|
||||||
acquire(&log.lock);
|
acquire(&log.lock);
|
||||||
|
@ -136,21 +154,27 @@ commit_trans(void)
|
||||||
release(&log.lock);
|
release(&log.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write buffer into the log at log.head and record the block number log.lh.entry, but
|
// Caller has modified b->data and is done with the buffer.
|
||||||
// don't write the log header (which would commit the write).
|
// Append the block to the log and record the block number,
|
||||||
|
// but don't write the log header (which would commit the write).
|
||||||
|
// log_write() replaces bwrite(); a typical use is:
|
||||||
|
// bp = bread(...)
|
||||||
|
// modify bp->data[]
|
||||||
|
// log_write(bp)
|
||||||
|
// brelse(bp)
|
||||||
void
|
void
|
||||||
log_write(struct buf *b)
|
log_write(struct buf *b)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (log.lh.head >= LOGSIZE)
|
if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
|
||||||
panic("too big a transaction");
|
panic("too big a transaction");
|
||||||
if (!log.intrans)
|
if (!log.intrans)
|
||||||
panic("write outside of trans");
|
panic("write outside of trans");
|
||||||
|
|
||||||
cprintf("log_write: %d %d\n", b->sector, log.lh.head);
|
// cprintf("log_write: %d %d\n", b->sector, log.lh.n);
|
||||||
|
|
||||||
for (i = 0; i < log.lh.head; i++) {
|
for (i = 0; i < log.lh.n; i++) {
|
||||||
if (log.lh.sector[i] == b->sector) // log absorbtion?
|
if (log.lh.sector[i] == b->sector) // log absorbtion?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +183,6 @@ log_write(struct buf *b)
|
||||||
memmove(lbuf->data, b->data, BSIZE);
|
memmove(lbuf->data, b->data, BSIZE);
|
||||||
bwrite(lbuf);
|
bwrite(lbuf);
|
||||||
brelse(lbuf);
|
brelse(lbuf);
|
||||||
if (i == log.lh.head)
|
if (i == log.lh.n)
|
||||||
log.lh.head++;
|
log.lh.n++;
|
||||||
}
|
}
|
||||||
|
|
2
param.h
2
param.h
|
@ -8,5 +8,5 @@
|
||||||
#define NDEV 10 // maximum major device number
|
#define NDEV 10 // maximum major device number
|
||||||
#define ROOTDEV 1 // device number of file system root disk
|
#define ROOTDEV 1 // device number of file system root disk
|
||||||
#define MAXARG 32 // max exec arguments
|
#define MAXARG 32 // max exec arguments
|
||||||
#define LOGSIZE 10 // size of log
|
#define LOGSIZE 10 // max data sectors in on-disk log
|
||||||
|
|
||||||
|
|
|
@ -141,9 +141,7 @@ syscall(void)
|
||||||
if(num >= 0 && num < SYS_open && syscalls[num]) {
|
if(num >= 0 && num < SYS_open && syscalls[num]) {
|
||||||
proc->tf->eax = syscalls[num]();
|
proc->tf->eax = syscalls[num]();
|
||||||
} else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
|
} else if (num >= SYS_open && num < NELEM(syscalls) && syscalls[num]) {
|
||||||
begin_trans();
|
|
||||||
proc->tf->eax = syscalls[num]();
|
proc->tf->eax = syscalls[num]();
|
||||||
commit_trans();
|
|
||||||
} else {
|
} else {
|
||||||
cprintf("%d %s: unknown sys call %d\n",
|
cprintf("%d %s: unknown sys call %d\n",
|
||||||
proc->pid, proc->name, num);
|
proc->pid, proc->name, num);
|
||||||
|
|
30
sysfile.c
30
sysfile.c
|
@ -121,6 +121,9 @@ sys_link(void)
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
begin_trans();
|
||||||
|
|
||||||
ip->nlink++;
|
ip->nlink++;
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
iunlock(ip);
|
iunlock(ip);
|
||||||
|
@ -134,6 +137,9 @@ sys_link(void)
|
||||||
}
|
}
|
||||||
iunlockput(dp);
|
iunlockput(dp);
|
||||||
iput(ip);
|
iput(ip);
|
||||||
|
|
||||||
|
commit_trans();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
|
@ -141,6 +147,7 @@ bad:
|
||||||
ip->nlink--;
|
ip->nlink--;
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
|
commit_trans();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +202,8 @@ sys_unlink(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
begin_trans();
|
||||||
|
|
||||||
memset(&de, 0, sizeof(de));
|
memset(&de, 0, sizeof(de));
|
||||||
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
|
||||||
panic("unlink: writei");
|
panic("unlink: writei");
|
||||||
|
@ -207,6 +216,9 @@ sys_unlink(void)
|
||||||
ip->nlink--;
|
ip->nlink--;
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
|
|
||||||
|
commit_trans();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,6 +263,7 @@ create(char *path, short type, short major, short minor)
|
||||||
panic("create: dirlink");
|
panic("create: dirlink");
|
||||||
|
|
||||||
iunlockput(dp);
|
iunlockput(dp);
|
||||||
|
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +278,10 @@ sys_open(void)
|
||||||
if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
|
if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if(omode & O_CREATE){
|
if(omode & O_CREATE){
|
||||||
if((ip = create(path, T_FILE, 0, 0)) == 0)
|
begin_trans();
|
||||||
|
ip = create(path, T_FILE, 0, 0);
|
||||||
|
commit_trans();
|
||||||
|
if(ip == 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
if((ip = namei(path)) == 0)
|
if((ip = namei(path)) == 0)
|
||||||
|
@ -299,9 +315,13 @@ sys_mkdir(void)
|
||||||
char *path;
|
char *path;
|
||||||
struct inode *ip;
|
struct inode *ip;
|
||||||
|
|
||||||
if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0)
|
begin_trans();
|
||||||
|
if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
|
||||||
|
commit_trans();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
|
commit_trans();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,12 +333,16 @@ sys_mknod(void)
|
||||||
int len;
|
int len;
|
||||||
int major, minor;
|
int major, minor;
|
||||||
|
|
||||||
|
begin_trans();
|
||||||
if((len=argstr(0, &path)) < 0 ||
|
if((len=argstr(0, &path)) < 0 ||
|
||||||
argint(1, &major) < 0 ||
|
argint(1, &major) < 0 ||
|
||||||
argint(2, &minor) < 0 ||
|
argint(2, &minor) < 0 ||
|
||||||
(ip = create(path, T_DEV, major, minor)) == 0)
|
(ip = create(path, T_DEV, major, minor)) == 0){
|
||||||
|
commit_trans();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
|
commit_trans();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
usertests.c
33
usertests.c
|
@ -7,7 +7,7 @@
|
||||||
#include "traps.h"
|
#include "traps.h"
|
||||||
#include "memlayout.h"
|
#include "memlayout.h"
|
||||||
|
|
||||||
char buf[2048];
|
char buf[8192];
|
||||||
char name[3];
|
char name[3];
|
||||||
char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 };
|
char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 };
|
||||||
int stdout = 1;
|
int stdout = 1;
|
||||||
|
@ -968,6 +968,36 @@ subdir(void)
|
||||||
printf(1, "subdir ok\n");
|
printf(1, "subdir ok\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test writes that are larger than the log.
|
||||||
|
void
|
||||||
|
bigwrite(void)
|
||||||
|
{
|
||||||
|
int fd, sz;
|
||||||
|
|
||||||
|
printf(1, "bigwrite test\n");
|
||||||
|
|
||||||
|
unlink("bigwrite");
|
||||||
|
for(sz = 499; sz < 12*512; sz += 471){
|
||||||
|
fd = open("bigwrite", O_CREATE | O_RDWR);
|
||||||
|
if(fd < 0){
|
||||||
|
printf(1, "cannot create bigwrite\n");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 2; i++){
|
||||||
|
int cc = write(fd, buf, sz);
|
||||||
|
if(cc != sz){
|
||||||
|
printf(1, "write(%d) ret %d\n", sz, cc);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
unlink("bigwrite");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(1, "bigwrite ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
bigfile(void)
|
bigfile(void)
|
||||||
{
|
{
|
||||||
|
@ -1467,6 +1497,7 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
close(open("usertests.ran", O_CREATE));
|
close(open("usertests.ran", O_CREATE));
|
||||||
|
|
||||||
|
bigwrite();
|
||||||
bigargtest();
|
bigargtest();
|
||||||
bsstest();
|
bsstest();
|
||||||
sbrktest();
|
sbrktest();
|
||||||
|
|
Loading…
Reference in a new issue