avoid deadlock by calling begin_trans() before locking any inodes

This commit is contained in:
Robert Morris 2011-08-15 12:44:20 -04:00
parent c95ce31c59
commit 5053dd6a6d
5 changed files with 66 additions and 15 deletions

6
file.c
View file

@ -118,7 +118,6 @@ filewrite(struct file *f, char *addr, int n)
if(f->type == FD_PIPE) if(f->type == FD_PIPE)
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);
// write a few blocks at a time to avoid exceeding // write a few blocks at a time to avoid exceeding
// the maximum log transaction size, including // the maximum log transaction size, including
// i-node, indirect block, allocation blocks, // i-node, indirect block, allocation blocks,
@ -131,9 +130,13 @@ filewrite(struct file *f, char *addr, int n)
int n1 = n - i; int n1 = n - i;
if(n1 > max) if(n1 > max)
n1 = max; n1 = max;
begin_trans(); begin_trans();
ilock(f->ip);
r = writei(f->ip, addr + i, f->off, n1); r = writei(f->ip, addr + i, f->off, n1);
iunlock(f->ip);
commit_trans(); commit_trans();
if(r < 0) if(r < 0)
break; break;
if(r != n1) if(r != n1)
@ -141,7 +144,6 @@ filewrite(struct file *f, char *addr, int n)
f->off += r; f->off += r;
i += r; i += r;
} }
iunlock(f->ip);
return i == n ? n : -1; return i == n ? n : -1;
} }
panic("filewrite"); panic("filewrite");

7
fs.c
View file

@ -43,13 +43,13 @@ bzero(int dev, int bno)
bp = bread(dev, bno); bp = bread(dev, bno);
memset(bp->data, 0, BSIZE); memset(bp->data, 0, BSIZE);
bwrite(bp); log_write(bp);
brelse(bp); brelse(bp);
} }
// Blocks. // Blocks.
// Allocate a disk block. // Allocate a zeroed disk block.
static uint static uint
balloc(uint dev) balloc(uint dev)
{ {
@ -67,6 +67,7 @@ balloc(uint dev)
bp->data[bi/8] |= m; // Mark block in use on disk. bp->data[bi/8] |= m; // Mark block in use on disk.
log_write(bp); log_write(bp);
brelse(bp); brelse(bp);
bzero(dev, b + bi);
return b + bi; return b + bi;
} }
} }
@ -83,8 +84,6 @@ bfree(int dev, uint b)
struct superblock sb; struct superblock sb;
int bi, m; int bi, m;
bzero(dev, b);
readsb(dev, &sb); readsb(dev, &sb);
bp = bread(dev, BBLOCK(b, sb.ninodes)); bp = bread(dev, BBLOCK(b, sb.ninodes));
bi = b % BPB; bi = b % BPB;

4
log.c
View file

@ -124,9 +124,9 @@ static void
recover_from_log(void) recover_from_log(void)
{ {
read_head(); read_head();
install_trans(); // Install all transactions till head install_trans(); // if committed, copy from log to disk
log.lh.n = 0; log.lh.n = 0;
write_head(); // Reclaim log write_head(); // clear the log
} }
void void

View file

@ -116,14 +116,16 @@ sys_link(void)
return -1; return -1;
if((ip = namei(old)) == 0) if((ip = namei(old)) == 0)
return -1; return -1;
begin_trans();
ilock(ip); ilock(ip);
if(ip->type == T_DIR){ if(ip->type == T_DIR){
iunlockput(ip); iunlockput(ip);
commit_trans();
return -1; return -1;
} }
begin_trans();
ip->nlink++; ip->nlink++;
iupdate(ip); iupdate(ip);
iunlock(ip); iunlock(ip);
@ -180,16 +182,21 @@ sys_unlink(void)
return -1; return -1;
if((dp = nameiparent(path, name)) == 0) if((dp = nameiparent(path, name)) == 0)
return -1; return -1;
begin_trans();
ilock(dp); ilock(dp);
// Cannot unlink "." or "..". // Cannot unlink "." or "..".
if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){ if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){
iunlockput(dp); iunlockput(dp);
commit_trans();
return -1; return -1;
} }
if((ip = dirlookup(dp, name, &off)) == 0){ if((ip = dirlookup(dp, name, &off)) == 0){
iunlockput(dp); iunlockput(dp);
commit_trans();
return -1; return -1;
} }
ilock(ip); ilock(ip);
@ -199,11 +206,10 @@ sys_unlink(void)
if(ip->type == T_DIR && !isdirempty(ip)){ if(ip->type == T_DIR && !isdirempty(ip)){
iunlockput(ip); iunlockput(ip);
iunlockput(dp); iunlockput(dp);
commit_trans();
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");

View file

@ -364,6 +364,8 @@ sharedfd(void)
int fd, pid, i, n, nc, np; int fd, pid, i, n, nc, np;
char buf[10]; char buf[10];
printf(1, "sharedfd test\n");
unlink("sharedfd"); unlink("sharedfd");
fd = open("sharedfd", O_CREATE|O_RDWR); fd = open("sharedfd", O_CREATE|O_RDWR);
if(fd < 0){ if(fd < 0){
@ -655,7 +657,7 @@ linktest(void)
printf(1, "linktest ok\n"); printf(1, "linktest ok\n");
} }
// test concurrent create and unlink of the same file // test concurrent create/link/unlink of the same file
void void
concreate(void) concreate(void)
{ {
@ -728,10 +730,15 @@ concreate(void)
} }
if(((i % 3) == 0 && pid == 0) || if(((i % 3) == 0 && pid == 0) ||
((i % 3) == 1 && pid != 0)){ ((i % 3) == 1 && pid != 0)){
fd = open(file, 0); close(open(file, 0));
close(fd); close(open(file, 0));
close(open(file, 0));
close(open(file, 0));
} else { } else {
unlink(file); unlink(file);
unlink(file);
unlink(file);
unlink(file);
} }
if(pid == 0) if(pid == 0)
exit(); exit();
@ -742,6 +749,42 @@ concreate(void)
printf(1, "concreate ok\n"); printf(1, "concreate ok\n");
} }
// another concurrent link/unlink/create test,
// to look for deadlocks.
void
linkunlink()
{
int pid, i;
printf(1, "linkunlink test\n");
unlink("x");
pid = fork();
if(pid < 0){
printf(1, "fork failed\n");
exit();
}
unsigned int x = (pid ? 1 : 97);
for(i = 0; i < 100; i++){
x = x * 1103515245 + 12345;
if((x % 3) == 0){
close(open("x", O_RDWR | O_CREATE));
} else if((x % 3) == 1){
link("cat", "x");
} else {
unlink("x");
}
}
if(pid)
wait();
else
exit();
printf(1, "linkunlink ok\n");
}
// directory that uses indirect blocks // directory that uses indirect blocks
void void
bigdir(void) bigdir(void)
@ -1518,6 +1561,7 @@ main(int argc, char *argv[])
bigfile(); bigfile();
subdir(); subdir();
concreate(); concreate();
linkunlink();
linktest(); linktest();
unlinkread(); unlinkread();
createdelete(); createdelete();