diff -u --recursive dist-2.1.24/drivers/block/md.c linux/drivers/block/md.c --- dist-2.1.24/drivers/block/md.c Tue Jan 21 01:19:51 1997 +++ linux/drivers/block/md.c Tue Jan 21 00:03:51 1997 @@ -216,9 +216,12 @@ } /* Remove locks. */ - for (i=0; if_pos = 0; f->f_reada = 0; f->f_op = inode->i_op->default_file_ops; + inode->i_count++; if (f->f_op->open) { int error = f->f_op->open(inode,f); if (error) { f->f_count--; put_unused_fd(fd); + iput(inode); return error; } } current->files->fd[fd] = f; - inode->i_count++; } return fd; } diff -u --recursive dist-2.1.24/fs/inode.c linux/fs/inode.c --- dist-2.1.24/fs/inode.c Tue Jan 21 01:19:51 1997 +++ linux/fs/inode.c Tue Jan 21 00:04:05 1997 @@ -13,7 +13,7 @@ #include #define NR_IHASH 512 - +static unsigned long empty_dirty_writes = 0; /* * Be VERY careful when you access the inode hash table. There * are some rather scary race conditions you need to take care of: @@ -145,13 +145,13 @@ __wait_on_inode(inode); } -static inline void lock_inode(struct inode * inode) +void lock_inode(struct inode * inode) { wait_on_inode(inode); inode->i_lock = 1; } -static inline void unlock_inode(struct inode * inode) +void unlock_inode(struct inode * inode) { inode->i_lock = 0; wake_up(&inode->i_wait); @@ -159,7 +159,8 @@ /* * Note that we don't want to disturb any wait-queues when we discard - * an inode. + * an inode. Note also that the inode must be locked when clear_inode + * is called. * * Argghh. Got bitten by a gcc problem with inlining: no way to tell * the compiler that the inline asm function 'memset' changes 'inode'. @@ -168,13 +169,16 @@ * * The solution is the weird use of 'volatile'. Ho humm. Have to report * it to the gcc lists, and hope we can do this more cleanly some day.. + * + * Doh!!! This has been another one of those places where races are + * bound to occur. Just remember that the inode must remain locked + * whenever we can sleep... Please!!! --blah */ void clear_inode(struct inode * inode) { struct wait_queue * wait; - truncate_inode_pages(inode, 0); - wait_on_inode(inode); + truncate_inode_pages(inode, 0); /* may sleep */ if (IS_WRITABLE(inode)) { if (inode->i_sb && inode->i_sb->dq_op) inode->i_sb->dq_op->drop(inode); @@ -202,7 +206,9 @@ continue; if (inode->i_count || inode->i_dirt || inode->i_lock) return 0; + lock_inode(inode); clear_inode(inode); + unlock_inode(inode); } return 1; } @@ -312,14 +318,14 @@ */ void inode_setattr(struct inode *inode, struct iattr *attr) { + unsigned char i_dirt = 1; + if (attr->ia_valid & ATTR_UID) inode->i_uid = attr->ia_uid; if (attr->ia_valid & ATTR_GID) inode->i_gid = attr->ia_gid; if (attr->ia_valid & ATTR_SIZE) inode->i_size = attr->ia_size; - if (attr->ia_valid & ATTR_ATIME) - inode->i_atime = attr->ia_atime; if (attr->ia_valid & ATTR_MTIME) inode->i_mtime = attr->ia_mtime; if (attr->ia_valid & ATTR_CTIME) @@ -329,7 +335,12 @@ if (!fsuser() && !in_group_p(inode->i_gid)) inode->i_mode &= ~S_ISGID; } - inode->i_dirt = 1; + if (attr->ia_valid & ATTR_ATIME) { + if (attr->ia_valid == ATTR_ATIME) + i_dirt = inode->i_dirt ? 1 : 2; + inode->i_atime = attr->ia_atime; + } + inode->i_dirt = i_dirt; } /* @@ -395,20 +406,25 @@ kdevname(dev)); continue; } + lock_inode(inode); clear_inode(inode); + unlock_inode(inode); } } +static unsigned long last_empty_dirty_writes = 0; + void sync_inodes(kdev_t dev) { int i; struct inode * inode; - +if (last_empty_dirty_writes != empty_dirty_writes) +printk(KERN_DEBUG "VFS: sync_inodes(%02x) called - empty_dirty_writes: %lu\n", (int)dev, last_empty_dirty_writes=empty_dirty_writes); inode = first_inode; for(i = 0; i < nr_inodes*2; i++, inode = inode->i_next) { if (dev && inode->i_dev != dev) continue; - wait_on_inode(inode); + /* wait_on_inode(inode); --blah: shouldn't be nescessary */ if (inode->i_dirt) write_inode(inode); } @@ -441,17 +457,19 @@ } if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) { - inode->i_sb->s_op->put_inode(inode); + inode->i_lock = 1; + inode->i_sb->s_op->put_inode(inode); /* sleeps here were once bad */ + unlock_inode(inode); if (!inode->i_nlink) return; } - - if (inode->i_dirt) { +#if 0 /* not writing dirty inodes here, but letting get_empty_inode or sync_inodes do it fits more with the unix way of doing things */ + if (inode->i_dirt && (inode->i_dirt != 2)) { write_inode(inode); /* we can sleep - so do again */ wait_on_inode(inode); goto repeat; } - +#endif if (IS_WRITABLE(inode)) { if (inode->i_sb && inode->i_sb->dq_op) { /* Here we can sleep also. Let's do it again @@ -517,12 +535,14 @@ goto repeat; } if (best->i_dirt) { +empty_dirty_writes++; write_inode(best); goto repeat; } if (best->i_count) goto repeat; found_good: + lock_inode(best); clear_inode(best); best->i_count = 1; best->i_nlink = 1; @@ -530,6 +550,7 @@ best->i_sem.count = 1; best->i_ino = ++ino; best->i_dev = 0; + unlock_inode(best); nr_free_inodes--; if (nr_free_inodes < 0) { printk ("VFS: get_empty_inode: bad free inode count.\n"); diff -u --recursive dist-2.1.24/fs/namei.c linux/fs/namei.c --- dist-2.1.24/fs/namei.c Sun Jan 26 12:38:52 1997 +++ linux/fs/namei.c Sun Jan 26 12:40:35 1997 @@ -160,7 +160,7 @@ struct inode ** result) { struct super_block * sb; - int perm; + int perm, err = 0; *result = NULL; if (!dir) @@ -172,11 +172,12 @@ *result = dir; return 0; } else if ((sb = dir->i_sb) && (dir == sb->s_mounted)) { + if (sb->s_covered) + sb->s_covered->i_count++; /* avoid the race */ iput(dir); dir = sb->s_covered; if (!dir) return -ENOENT; - dir->i_count++; } } if (!dir->i_op || !dir->i_op->lookup) { diff -u --recursive dist-2.1.24/fs/open.c linux/fs/open.c --- dist-2.1.24/fs/open.c Sun Jan 26 12:38:53 1997 +++ linux/fs/open.c Sun Jan 26 12:52:35 1997 @@ -290,7 +290,7 @@ asmlinkage int sys_chdir(const char * filename) { - struct inode * inode; + struct inode * inode, *tmp; int error; lock_kernel(); @@ -306,8 +306,9 @@ iput(inode); goto out; } - iput(current->fs->pwd); + tmp = current->fs->pwd; current->fs->pwd = inode; + iput(tmp); error = 0; out: unlock_kernel(); @@ -316,7 +317,7 @@ asmlinkage int sys_fchdir(unsigned int fd) { - struct inode * inode; + struct inode * inode, *tmp; struct file * file; int error = -EBADF; @@ -331,9 +332,10 @@ goto out; if ((error = permission(inode,MAY_EXEC)) != 0) goto out; - iput(current->fs->pwd); + tmp = current->fs->pwd; current->fs->pwd = inode; inode->i_count++; + iput(tmp); error = 0; out: unlock_kernel(); diff -u --recursive dist-2.1.24/include/linux/fs.h linux/include/linux/fs.h --- dist-2.1.24/include/linux/fs.h Sun Jan 26 12:38:58 1997 +++ linux/include/linux/fs.h Sun Jan 26 15:27:49 1997 @@ -620,6 +620,8 @@ struct inode ** res_inode, struct inode * base); extern int do_mknod(const char * filename, int mode, dev_t dev); extern int do_pipe(int *); +extern void lock_inode(struct inode * inode); +extern void unlock_inode(struct inode * inode); extern void iput(struct inode * inode); extern struct inode * __iget(struct super_block * sb,int nr,int crsmnt); extern struct inode * get_empty_inode(void);