From 5053dd6a6d2b481bfcddbd91bacc885b9f0e0ff5 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 15 Aug 2011 12:44:20 -0400 Subject: [PATCH] avoid deadlock by calling begin_trans() before locking any inodes --- file.c | 6 ++++-- fs.c | 7 +++---- log.c | 4 ++-- sysfile.c | 14 ++++++++++---- usertests.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/file.c b/file.c index 7101a50..f8a14ad 100644 --- a/file.c +++ b/file.c @@ -118,7 +118,6 @@ filewrite(struct file *f, char *addr, int n) if(f->type == FD_PIPE) return pipewrite(f->pipe, addr, n); if(f->type == FD_INODE){ - ilock(f->ip); // write a few blocks at a time to avoid exceeding // the maximum log transaction size, including // i-node, indirect block, allocation blocks, @@ -131,9 +130,13 @@ filewrite(struct file *f, char *addr, int n) int n1 = n - i; if(n1 > max) n1 = max; + begin_trans(); + ilock(f->ip); r = writei(f->ip, addr + i, f->off, n1); + iunlock(f->ip); commit_trans(); + if(r < 0) break; if(r != n1) @@ -141,7 +144,6 @@ filewrite(struct file *f, char *addr, int n) f->off += r; i += r; } - iunlock(f->ip); return i == n ? n : -1; } panic("filewrite"); diff --git a/fs.c b/fs.c index a76788b..fe9ce48 100644 --- a/fs.c +++ b/fs.c @@ -43,13 +43,13 @@ bzero(int dev, int bno) bp = bread(dev, bno); memset(bp->data, 0, BSIZE); - bwrite(bp); + log_write(bp); brelse(bp); } // Blocks. -// Allocate a disk block. +// Allocate a zeroed disk block. static uint balloc(uint dev) { @@ -67,6 +67,7 @@ balloc(uint dev) bp->data[bi/8] |= m; // Mark block in use on disk. log_write(bp); brelse(bp); + bzero(dev, b + bi); return b + bi; } } @@ -83,8 +84,6 @@ bfree(int dev, uint b) struct superblock sb; int bi, m; - bzero(dev, b); - readsb(dev, &sb); bp = bread(dev, BBLOCK(b, sb.ninodes)); bi = b % BPB; diff --git a/log.c b/log.c index db36ba9..e9a16c5 100644 --- a/log.c +++ b/log.c @@ -124,9 +124,9 @@ static void recover_from_log(void) { read_head(); - install_trans(); // Install all transactions till head + install_trans(); // if committed, copy from log to disk log.lh.n = 0; - write_head(); // Reclaim log + write_head(); // clear the log } void diff --git a/sysfile.c b/sysfile.c index ca54013..c9d3594 100644 --- a/sysfile.c +++ b/sysfile.c @@ -116,14 +116,16 @@ sys_link(void) return -1; if((ip = namei(old)) == 0) return -1; + + begin_trans(); + ilock(ip); if(ip->type == T_DIR){ iunlockput(ip); + commit_trans(); return -1; } - begin_trans(); - ip->nlink++; iupdate(ip); iunlock(ip); @@ -180,16 +182,21 @@ sys_unlink(void) return -1; if((dp = nameiparent(path, name)) == 0) return -1; + + begin_trans(); + ilock(dp); // Cannot unlink "." or "..". if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){ iunlockput(dp); + commit_trans(); return -1; } if((ip = dirlookup(dp, name, &off)) == 0){ iunlockput(dp); + commit_trans(); return -1; } ilock(ip); @@ -199,11 +206,10 @@ sys_unlink(void) if(ip->type == T_DIR && !isdirempty(ip)){ iunlockput(ip); iunlockput(dp); + commit_trans(); return -1; } - begin_trans(); - memset(&de, 0, sizeof(de)); if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de)) panic("unlink: writei"); diff --git a/usertests.c b/usertests.c index ba648a7..8db8385 100644 --- a/usertests.c +++ b/usertests.c @@ -364,6 +364,8 @@ sharedfd(void) int fd, pid, i, n, nc, np; char buf[10]; + printf(1, "sharedfd test\n"); + unlink("sharedfd"); fd = open("sharedfd", O_CREATE|O_RDWR); if(fd < 0){ @@ -655,7 +657,7 @@ linktest(void) printf(1, "linktest ok\n"); } -// test concurrent create and unlink of the same file +// test concurrent create/link/unlink of the same file void concreate(void) { @@ -728,10 +730,15 @@ concreate(void) } if(((i % 3) == 0 && pid == 0) || ((i % 3) == 1 && pid != 0)){ - fd = open(file, 0); - close(fd); + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); } else { unlink(file); + unlink(file); + unlink(file); + unlink(file); } if(pid == 0) exit(); @@ -742,6 +749,42 @@ concreate(void) 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 void bigdir(void) @@ -1518,6 +1561,7 @@ main(int argc, char *argv[]) bigfile(); subdir(); concreate(); + linkunlink(); linktest(); unlinkread(); createdelete();