Messages in this thread Patch in this message | | | Date | Mon, 14 Jul 1997 21:42:33 -0400 | From | Bill Hawes <> | Subject | Updated patch for pre-patch 2.1.45-5 |
| |
I've updated my patches for pre-patch 2.1.45-5, and everything is working nicely. I got mount/umount/mount to work finally -- the problem seems to be that in fs/super.c after the call to d_umount the root dentry still has d_count=3. Since the call to d_delete won't let go of the inode unless d_count == 1, I added two dputs. I'm sure this isn't what you intended, but it works for now.
A few other changes -- I modified mark_inode_dirty to not do anything unless s_op->write_inode is defined. Otherwise we get dirty pipe inodes on the dirty list.
Also fixed an untested allocation in fs/super.c and an iput() for the dummy inode.
I think you'll find the shift-scrolllock inode display helpful for debugging.
Regards, Bill--- linux-2.1.45/fs/inode.c.old Mon Jul 14 15:25:23 1997 +++ linux-2.1.45/fs/inode.c Mon Jul 14 17:21:55 1997 @@ -18,6 +18,8 @@ * Famous last words. */ +#define INODE_PARANOIA 1 + /* * Inode lookup is no longer as critical as it used to be: * most of the lookups are going to be through the dcache. @@ -54,8 +56,9 @@ struct { int nr_inodes; int nr_free_inodes; - int dummy[10]; -} inodes_stat; + int nr_may_free; + int dummy[9]; +} inodes_stat = {0, 0, 0}; int max_inodes = NR_INODE; @@ -73,14 +76,14 @@ wake_up(&inode->i_wait); } -static void __wait_on_inode(struct inode * inode) +static void __wait_on_inode_flag(struct inode * inode, int bit) { struct wait_queue wait = { current, NULL }; add_wait_queue(&inode->i_wait, &wait); repeat: current->state = TASK_UNINTERRUPTIBLE; - if (test_bit(I_LOCK, &inode->i_state)) { + if (test_bit(bit, &inode->i_state)) { schedule(); goto repeat; } @@ -88,6 +91,11 @@ current->state = TASK_RUNNING; } +static inline void __wait_on_inode(struct inode * inode) +{ + __wait_on_inode_flag(inode, I_LOCK); +} + static inline void wait_on_inode(struct inode *inode) { if (test_bit(I_LOCK, &inode->i_state)) @@ -107,7 +115,6 @@ sema_init(&inode->i_sem, 1); } - /* * Look out! This returns with the inode lock held if * it got an inode.. @@ -130,25 +137,42 @@ size -= sizeof(struct inode); } while (size >= 0); init_once(inode); + inodes_stat.nr_inodes += PAGE_SIZE / sizeof(struct inode); } return inode; } +/* + * This is called with I_LOCK set. + */ static inline void write_inode(struct inode *inode) { - if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) inode->i_sb->s_op->write_inode(inode); } -static inline void sync_list(struct list_head *head, struct list_head *clean) +static void sync_list(struct list_head *head, struct list_head *clean, + kdev_t dev) { - struct list_head * tmp; + struct list_head * tmp, * sentinel = NULL; - while ((tmp = head->prev) != head) { + while ((tmp = head->prev) != head && tmp != sentinel) { struct inode *inode = list_entry(tmp, struct inode, i_list); + list_del(tmp); /* + * If this inode isn't on the device we're syncing, + * move it to the front and set the sentinel. + */ + if (dev && inode->i_dev != dev) { + list_add(tmp, head); + if (sentinel == NULL) + sentinel = tmp; + continue; + } + + /* * If the inode is locked, it's already being written out. * We have to wait for it, though. */ @@ -164,6 +188,7 @@ write_inode(inode); unlock_inode(inode); } + sentinel = NULL; spin_lock(&inode_lock); } } @@ -176,28 +201,51 @@ void sync_inodes(kdev_t dev) { spin_lock(&inode_lock); - sync_list(&inode_dirty, &inode_in_use); + sync_list(&inode_dirty, &inode_in_use, dev); spin_unlock(&inode_lock); } /* - * This is called by the filesystem to tell us - * that the inode is no longer useful. We just - * terminate it with extreme predjudice. + * Non-blocking. Caller must hold the inode lock, and must verify that + * the inode is not locked and that no disk quota exists. (Disk quota is + * released when the use count goes from 1 to 0.) */ -void clear_inode(struct inode *inode) +static void _clear_inode(struct inode *inode) { +#ifdef INODE_PARANOIA + if (test_bit(I_LOCK, &inode->i_state)) + printk("_clear_inode: inode %ld locked\n", inode->i_ino); + if (IS_WRITABLE(inode)) + printk("_clear_inode: inode %ld disk quota not released\n", + inode->i_ino); +#endif + /* + * This must be non-blocking to be completely safe. + */ truncate_inode_pages(inode, 0); - wait_on_inode(inode); - if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op) - inode->i_sb->dq_op->drop(inode); - spin_lock(&inode_lock); inode->i_state = 0; list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); list_del(&inode->i_list); list_add(&inode->i_list, &inode_unused); - spin_unlock(&inode_lock); +} + +/* + * This is called by the filesystem to tell us + * that the inode is no longer useful. We just + * terminate it with extreme prejudice. + */ +void clear_inode(struct inode *inode) +{ +#ifdef INODE_PARANOIA + if (atomic_read(&inode->i_count) != 1) + printk("clear_inode: invalid count %d\n", + atomic_read(&inode->i_count)); + if (!test_bit(I_CLOSING, &inode->i_state)) + printk("clear_inode: closing flag not set\n"); +#endif + set_bit(I_CLEARME, &inode->i_state); } #define CAN_UNUSE(inode) \ @@ -205,85 +253,146 @@ ((inode)->i_nrpages == 0) && \ (!test_bit(I_LOCK, &(inode)->i_state))) -static void invalidate_list(struct list_head *head, kdev_t dev) +/* + * This is called with the inode lock held. + */ +static int invalidate_list(struct list_head *head, kdev_t dev, int check) { - struct list_head *next; + struct list_head *next, *tmp; + int busy = 0; - next = head->next; - for (;;) { - struct list_head * tmp = next; + for (tmp = head->next; tmp != head; tmp = next) { struct inode * inode; - next = next->next; - if (tmp == head) - break; + next = tmp->next; inode = list_entry(tmp, struct inode, i_list); if (inode->i_dev != dev) continue; - if (!CAN_UNUSE(inode)) - continue; - list_del(&inode->i_hash); - list_del(&inode->i_list); - list_add(&inode->i_list, &inode_unused); + if (atomic_read(&(inode)->i_count) == 0 && + !test_bit(I_DIRTY, &inode->i_state) && + !test_bit(I_LOCK , &inode->i_state)) + _clear_inode(inode); + else { + busy = 1; + if (check) { +#ifdef INODE_PARANOIA +printk("invalidate_inodes: device %d inode %ld count=%d state=%x\n", +dev, inode->i_ino, atomic_read(&inode->i_count), inode->i_state); +#endif + break; + } + } } + return busy; } void invalidate_inodes(kdev_t dev) { spin_lock(&inode_lock); - invalidate_list(&inode_in_use, dev); - invalidate_list(&inode_dirty, dev); + invalidate_list(&inode_in_use, dev, 0); + invalidate_list(&inode_dirty , dev, 0); spin_unlock(&inode_lock); } /* - * This is called with the inode lock held. It just looks at the last - * inode on the in-use list, and if the inode is trivially freeable - * we just move it to the unused list. + * This is called with the inode lock held. It checks for potentially + * freeable inodes and uses _clear_inode to free them. * - * Otherwise we just move the inode to be the first inode and expect to - * get back to the problem later.. + * If the first pass fails, we try syncing and check again. */ static void try_to_free_inodes(void) { - struct list_head * tmp; + struct list_head *tmp, *prev; struct list_head *head = &inode_in_use; + int passes = 2, goal = 10, freed = 0; - tmp = head->prev; - if (tmp != head) { - struct inode * inode; + /* + * nr_may_free is an (over)estimate of the number of unused inodes, + * based on zero use counts as seen by iput(). This is useful as + * a futility avoidance measure. + */ + if (!inodes_stat.nr_may_free) + return; + /* + * Adjust our goal if there aren't very many inodes. + */ + if (goal > inodes_stat.nr_may_free) + goal = inodes_stat.nr_may_free; + if (goal > (inodes_stat.nr_inodes >> 5)) + goal = inodes_stat.nr_inodes >> 5; + + while (passes--) { + for (tmp = head->prev; tmp != head; tmp = prev) { + struct inode * inode; + + prev = tmp->prev; + inode = list_entry(tmp, struct inode, i_list); + if (atomic_read(&(inode)->i_count) != 0) + continue; + if (test_bit(I_LOCK, &(inode)->i_state)) + continue; + /* + * Inode is potentially freeable ... should select + * least cost inodes though. + */ + _clear_inode(inode); + inodes_stat.nr_may_free--; + freed++; + if (freed >= goal) + return; + } - list_del(tmp); - inode = list_entry(tmp, struct inode, i_list); - if (CAN_UNUSE(inode)) { - list_del(&inode->i_hash); - head = &inode_unused; + if (freed) + return; + + /* + * First pass failed ... maybe syncing will help. + */ + if (inode_dirty.next != &inode_dirty) { + sync_list(&inode_dirty, &inode_in_use, 0); + } else { + /* + * None available ... reset the "maybe free" counter. + */ + inodes_stat.nr_may_free = 0; + break; } - list_add(tmp, head); } } - +/* + * Called with the inode lock held. + */ static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head) { struct list_head *tmp; struct inode * inode; - tmp = head; - for (;;) { - tmp = tmp->next; - inode = NULL; - if (tmp == head) - break; +repeat: + for (tmp = head->next; tmp != head; tmp = tmp->next) { inode = list_entry(tmp, struct inode, i_hash); if (inode->i_sb != sb) continue; if (inode->i_ino != ino) continue; + if (test_bit(I_CLOSING, &inode->i_state)) + goto wait_for_close; atomic_inc(&inode->i_count); - break; + return inode; } - return inode; + return NULL; + +wait_for_close: + /* + * Inode is being taken out of service ... wait and check again. + */ + spin_unlock(&inode_lock); +#ifdef INODE_PARANOIA +printk("find_inode: inode %ld dev %d closing\n", inode->i_ino, inode->i_dev); +#endif + __wait_on_inode_flag(inode, I_CLOSING); + spin_lock(&inode_lock); + goto repeat; } /* @@ -293,11 +402,12 @@ * i_sb, i_ino, i_count, i_state and the lists have * been initialized elsewhere.. */ -void clean_inode(struct inode *inode) +static void clean_inode(struct inode *inode) { memset(&inode->u, 0, sizeof(inode->u)); inode->i_pipe = 0; inode->i_sock = 0; + inode->i_mode = 0; inode->i_op = NULL; inode->i_nlink = 1; inode->i_writecount = 0; @@ -321,62 +431,81 @@ struct inode * inode; struct list_head * tmp = inode_unused.next; + spin_lock(&inode_lock); if (tmp != &inode_unused) { +get_unused: list_del(tmp); inode = list_entry(tmp, struct inode, i_list); +#ifdef INODE_PARANOIA + if (atomic_read(&inode->i_count)) + printk("get_empty_inode: unused count=%d\n", + atomic_read(&inode->i_count)); +#endif + add_new_inode: INIT_LIST_HEAD(&inode->i_list); INIT_LIST_HEAD(&inode->i_hash); inode->i_sb = NULL; + inode->i_dev = 0; inode->i_ino = ++last_ino; - atomic_set(&inode->i_count, 1); + inode->i_flags = 0; + atomic_inc(&inode->i_count); inode->i_state = 0; + spin_unlock(&inode_lock); clean_inode(inode); return inode; } /* - * Warning: if this succeeded, we will now - * return with the inode lock, and we need to - * unlock it. + * No unused inodes ... try to free some. */ + try_to_free_inodes(); + tmp = inode_unused.next; + if (tmp != &inode_unused) + goto get_unused; + + /* + * Unlock to try to allocate more inodes. + */ + spin_unlock(&inode_lock); inode = grow_inodes(); - if (inode) { - spin_unlock(&inode_lock); + if (inode) goto add_new_inode; - } return inode; } struct inode * get_pipe_inode(void) { extern struct inode_operations pipe_inode_operations; - struct inode *inode = get_empty_inode(); + struct inode *inode; + unsigned long page = __get_free_page(GFP_USER); - if (inode) { - unsigned long page = __get_free_page(GFP_USER); + if (!page) + goto fail; - if (!page) { - iput(inode); - inode = NULL; - } else { - PIPE_BASE(*inode) = (char *) page; - inode->i_op = &pipe_inode_operations; - atomic_set(&inode->i_count, 1); - PIPE_WAIT(*inode) = NULL; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - PIPE_LOCK(*inode) = 0; - inode->i_pipe = 1; - inode->i_mode |= S_IFIFO | S_IRUSR | S_IWUSR; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_blksize = PAGE_SIZE; - } - } + inode = get_empty_inode(); + if (!inode) + goto no_inode; + + PIPE_BASE(*inode) = (char *) page; + inode->i_op = &pipe_inode_operations; + PIPE_WAIT(*inode) = NULL; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; + PIPE_LOCK(*inode) = 0; + inode->i_pipe = 1; + inode->i_mode |= S_IFIFO | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = PAGE_SIZE; return inode; + +no_inode: + free_page(page); +fail: + return NULL; } /* @@ -388,8 +517,14 @@ struct list_head * tmp = inode_unused.next; if (tmp != &inode_unused) { +get_unused: list_del(tmp); inode = list_entry(tmp, struct inode, i_list); +#ifdef INODE_PARANOIA + if (atomic_read(&(inode)->i_count)) + printk("get_new_inode: unused count=%d\n", atomic_read(&(inode)->i_count)); +#endif + add_new_inode: list_add(&inode->i_list, &inode_in_use); list_add(&inode->i_hash, head); @@ -397,7 +532,7 @@ inode->i_dev = sb->s_dev; inode->i_ino = ino; inode->i_flags = sb->s_flags; - atomic_set(&inode->i_count, 1); + atomic_inc(&inode->i_count); inode->i_state = 1 << I_LOCK; spin_unlock(&inode_lock); clean_inode(inode); @@ -406,6 +541,14 @@ } /* + * No unused inodes ... try to free some. + */ + try_to_free_inodes(); + tmp = inode_unused.next; + if (tmp != &inode_unused) + goto get_unused; + + /* * Uhhuh.. We need to expand. Unlock for the allocation, * but note that "grow_inodes()" will return with the * lock held again if the allocation succeeded. @@ -440,7 +583,6 @@ spin_lock(&inode_lock); inode = find_inode(sb, ino, head); if (!inode) { - try_to_free_inodes(); return get_new_inode(sb, ino, head); } spin_unlock(&inode_lock); @@ -451,28 +593,84 @@ void insert_inode_hash(struct inode *inode) { struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino); + spin_lock(&inode_lock); list_add(&inode->i_hash, head); + spin_unlock(&inode_lock); } void iput(struct inode *inode) { - if (inode) { - if (inode->i_pipe) - wake_up_interruptible(&PIPE_WAIT(*inode)); + if (!inode) + return; + + if (inode->i_pipe) + wake_up_interruptible(&PIPE_WAIT(*inode)); + /* + * Last user dropping the inode? + */ + if (atomic_read(&inode->i_count) == 1) { /* - * Last user dropping the inode? - */ - if (atomic_read(&inode->i_count) == 1) { + * Set the I_CLOSING bit so the inode can't be put back + * in use while we're taking it out of service. + */ + set_bit(I_CLOSING, &inode->i_state); + + wait_on_inode(inode); + if (test_and_clear_bit(I_DIRTY, &inode->i_state)) { + set_bit(I_LOCK, &inode->i_state); + write_inode(inode); + unlock_inode(inode); + } + if (inode->i_sb && inode->i_sb->s_op) { void (*put)(struct inode *); - if (inode->i_sb && inode->i_sb->s_op) { - put = inode->i_sb->s_op->put_inode; - if (put) - put(inode); + put = inode->i_sb->s_op->put_inode; + if (put) + put(inode); + } + /* + * Pipe inodes have to be handled specially ... + */ + if (inode->i_pipe) { + free_page((unsigned long)PIPE_BASE(*inode)); + PIPE_BASE(*inode) = NULL; + clear_inode(inode); + } + + /* + * Check whether the filesystem asked us to clear the inode. + */ + if (test_and_clear_bit(I_CLEARME, &inode->i_state)) { + wait_on_inode(inode); + /* + * Drop the disk quota when the use count goes to 0. + */ + if (IS_WRITABLE(inode) && inode->i_sb && + inode->i_sb->dq_op) { + set_bit(I_LOCK, &inode->i_state); + inode->i_sb->dq_op->drop(inode); + unlock_inode(inode); } + + spin_lock(&inode_lock); + _clear_inode(inode); + spin_unlock(&inode_lock); } - atomic_dec(&inode->i_count); + /* + * All safe now ... clear the "closing" flag. + */ + clear_bit(I_CLOSING, &inode->i_state); + wake_up(&inode->i_wait); } + atomic_dec(&inode->i_count); + if (!atomic_read(&inode->i_count)) + inodes_stat.nr_may_free++; + +#ifdef INODE_PARANOIA +if (atomic_read(&inode->i_count) < 0) +printk("iput: open count wrapped dev=%d inode=%ld count=%d\n", +inode->i_dev, inode->i_ino, atomic_read(&inode->i_count)); +#endif } int bmap(struct inode * inode, int block) @@ -499,20 +697,143 @@ } /* - * FIXME! These need to go through the in-use inodes to - * check whether we can mount/umount/remount. + * Check whether we can mount. */ int fs_may_mount(kdev_t dev) { - return 1; + int busy; + + /* + * Shrink the dcache to free all unused dentries ... + */ + shrink_dcache(); + + spin_lock(&inode_lock); + busy = invalidate_list(&inode_in_use, dev, 1) || + invalidate_list(&inode_dirty , dev, 1); + spin_unlock(&inode_lock); +#ifdef INODE_PARANOIA + printk("fs_may_mount: device %d busy=%d\n", dev, busy); +#endif + return !busy; } +/* + * Check whether we can unmount. + */ int fs_may_umount(kdev_t dev, struct dentry * root) { - return 0; + struct list_head * tmp; + struct list_head *head = &inode_in_use; + int busy = 0; + + /* + * Shrink the dcache to free all unused dentries ... + */ + shrink_dcache(); +#ifdef INODE_PARANOIA + printk("fs_may_umount: after shrink d_count=%d\n", root->d_count); +#endif + /* + * ... then sync this device and invalidate the inodes. + */ + spin_lock(&inode_lock); + sync_list(&inode_dirty, &inode_in_use, dev); + invalidate_list(&inode_in_use, dev, 0); + + /* + * Check to see if anything is still in use ... + */ + for (tmp = head->next; tmp != head; tmp = tmp->next) { + struct inode * inode; + + inode = list_entry(tmp, struct inode, i_list); + if (inode->i_dev != dev) + continue; + if (!atomic_read(&inode->i_count)) + continue; + if (inode != root->d_inode) { +#ifdef INODE_PARANOIA + printk("fs_may_umount: inode %ld in use, not root inode %ld\n", + inode->i_ino, root->d_inode->i_ino); +#endif + busy = 1; + break; + } + } + spin_unlock(&inode_lock); + return !busy; } +/* + * Check that no files on this device are currently open for writing. + */ int fs_may_remount_ro(kdev_t dev) { - return 0; + struct file * file; + struct inode * inode; + + for (file = inuse_filps; file; file = file->f_next) { + if (!(inode = file->f_dentry->d_inode)) + continue; + if (inode->i_dev != dev) + continue; + if (S_ISREG(inode->i_mode) && (file->f_mode & 2)) + return 0; + } + return 1; +} + +/* + * Debugging aids + */ +void show_inodes(void) +{ + struct list_head *head; + int nlist, accounted = 0, tot_pages = 0; + static char *list_types[3] = {"CLEAN ","DIRTY ", "UNUSED"}; + + printk("Inodes Allocated: %d\n", inodes_stat.nr_inodes); + printk("List Inodes In-Use Hashed Locked Dirty Pipe Cnt<0" + " Pages\n"); + for (nlist = 0, head = &inode_in_use; nlist < 3; nlist++, head++) { + struct list_head *tmp; + struct inode *inode; + int found, used, pages, hashed, locked, dirty , pipe, negative; + int count; + + found = used = pages = hashed = locked = dirty = pipe = 0; + negative = 0; + for (tmp = head->next; tmp != head; tmp = tmp->next) { + found++; + + inode = list_entry(tmp, struct inode, i_list); + if ((count = atomic_read(&inode->i_count)) > 0) + used++; + if (inode->i_hash.next && + inode->i_hash.next != &inode->i_hash) + hashed++; + if (test_bit(I_LOCK, &inode->i_state)) + locked++; + if (test_bit(I_DIRTY, &inode->i_state)) + dirty++; + /* + * Check for problems ... these shouldn't happen. + */ + if (inode->i_pipe) + pipe++; + if (count < 0) + negative++; + /* + * Count the number of cache pages. + */ + pages += inode->i_nrpages; + } + printk("%6s %6d %6d %6d %6d %5d %4d %5d %6d\n", + list_types[nlist], found, used, hashed, locked, dirty, + pipe, negative, pages); + accounted += found; + tot_pages += pages; + } + printk("Inodes Accounted: %d Total Pages: %d\n", accounted, tot_pages); } --- linux-2.1.45/include/linux/fs.h.old Mon Jul 14 15:41:27 1997 +++ linux-2.1.45/include/linux/fs.h Mon Jul 14 20:59:44 1997 @@ -358,13 +358,8 @@ /* Inode state bits.. */ #define I_DIRTY 0 #define I_LOCK 1 - -extern void __mark_inode_dirty(struct inode *); -static inline void mark_inode_dirty(struct inode *inode) -{ - if (!test_and_set_bit(I_DIRTY, &inode->i_state)) - __mark_inode_dirty(inode); -} +#define I_CLOSING 2 /* Inode count going from 1 -> 0 */ +#define I_CLEARME 3 /* Filesystem asked to clear inode */ struct file { struct file *f_next, **f_pprev; @@ -591,6 +586,20 @@ struct file_system_type * next; }; +extern void __mark_inode_dirty(struct inode *); +/* + * Mark the inode dirty _only_ if it implements s_op->write_inode. + * (We want to keep pipe inodes out of the dirty list.) + */ +static inline void mark_inode_dirty(struct inode *inode) +{ + if (inode->i_sb && + inode->i_sb->s_op && + inode->i_sb->s_op->write_inode && + !test_and_set_bit(I_DIRTY, &inode->i_state)) + __mark_inode_dirty(inode); +} + extern int register_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *); @@ -787,6 +796,7 @@ extern kdev_t ROOT_DEV; extern void show_buffers(void); +extern void show_inode(void); extern void mount_root(void); #ifdef CONFIG_BLK_DEV_INITRD --- linux-2.1.45/include/linux/list.h.old Mon Jul 14 15:25:25 1997 +++ linux-2.1.45/include/linux/list.h Mon Jul 14 16:04:08 1997 @@ -27,10 +27,19 @@ static inline void list_del(struct list_head *entry) { struct list_head *next, *prev; +#ifdef INODE_PARANOIA +if (!entry->next) { +printk("list_del: already removed! (file %s line %s)\n", __FILE__, __LINE__); +return; } +#endif next = entry->next; prev = entry->prev; next->prev = prev; prev->next = next; +#ifdef INODE_PARANOIA +entry->next = NULL; +entry->prev = NULL; +#endif } static inline int list_empty(struct list_head *head) --- linux-2.1.45/fs/super.c.old Mon Jul 14 15:25:24 1997 +++ linux-2.1.45/fs/super.c Mon Jul 14 20:39:32 1997 @@ -466,7 +466,7 @@ } if (!(sb = get_super(dev))) return; - if (sb->s_root != sb->s_root->d_mounts) { + if (sb->s_root && sb->s_root != sb->s_root->d_mounts) { printk("VFS: Mounted device %s - tssk, tssk\n", kdevname(dev)); return; @@ -588,6 +588,7 @@ { if (covers->d_mounts != covers) { printk("VFS: mount - already mounted\n"); + /* WSH: shouldn't we dput(covers) here?? */ return; } covers->d_mounts = dget(dentry); @@ -597,15 +598,18 @@ static int do_umount(kdev_t dev,int unmount_root) { struct super_block * sb; + struct dentry * root; int retval; + sb=get_super(dev); + if (!sb) + return -ENOENT; + if (dev==ROOT_DEV && !unmount_root) { /* * Special case for "unmounting" root. We just try to remount * it readonly, and sync() the device. */ - if (!(sb=get_super(dev))) - return -ENOENT; if (!(sb->s_flags & MS_RDONLY)) { /* * Make sure all quotas are turned off on this device we need to mount @@ -621,9 +625,6 @@ } return 0; } - sb=get_super(dev); - if (!sb) - return -ENOENT; /* * Before checking if the filesystem is still busy make sure the kernel @@ -631,16 +632,23 @@ * too bad there are no quotas running anymore. Turn them on again by hand. */ quota_off(dev, -1); - if (!fs_may_umount(dev, sb->s_root)) + root = sb->s_root; + if (!fs_may_umount(dev, root)) return -EBUSY; /* Clear up the dcache tree. This should be cleaner.. */ - if (sb->s_root) { - d_umount(sb->s_root); - d_delete(sb->s_root); + if (root) { +if (root->d_inode) +printk("do_umount: before d_umount d_count=%d, inode=%ld, i_count=%d\n", +root->d_count, root->d_inode->i_ino, root->d_inode->i_count); + d_umount(root); + /* WSH: ugly hack to get root dentry to let go of inode */ + dput(root); + dput(root); + d_delete(root); + sb->s_root = NULL; } - sb->s_root = NULL; if (sb->s_op && sb->s_op->write_super && sb->s_dirt) sb->s_op->write_super(sb); put_super(dev); @@ -681,24 +689,26 @@ dev = inode->i_rdev; retval = -EACCES; if (IS_NODEV(inode)) { - dput(dentry); - goto out; + goto dput_out; } } else { retval = -EINVAL; if (!inode->i_sb || inode != inode->i_sb->s_root->d_inode) { - dput(dentry); - goto out; + goto dput_out; } dev = inode->i_sb->s_dev; - dput(dentry); + /* WSH: This dentry is dput later ... */ + /* dput(dentry); */ inode = dummy_inode = get_empty_inode(); + if (!dummy_inode) { + retval = -ENOMEM; + goto dput_out; + } inode->i_rdev = dev; } - retval = -ENXIO; if (MAJOR(dev) >= MAX_BLKDEV) { - dput(dentry); - goto out; + retval = -ENXIO; + goto dput_out; } retval = do_umount(dev,0); if (!retval) { @@ -711,8 +721,13 @@ dput(dentry); if (!retval) fsync_dev(dev); + goto out; + +dput_out: + dput(dentry); out: unlock_kernel(); + iput(dummy_inode); return retval; } @@ -1041,6 +1056,7 @@ #endif memset(&filp, 0, sizeof(filp)); + /* WSH: unchecked allocation */ d_inode = get_empty_inode(); d_inode->i_rdev = ROOT_DEV; filp.f_dentry = NULL; --- linux-2.1.45/fs/namei.c.old Mon Jul 14 15:25:24 1997 +++ linux-2.1.45/fs/namei.c Mon Jul 14 16:04:08 1997 @@ -183,11 +183,19 @@ * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things.. + * + * WSH: Check for NULL inode (possibly a zombie in path?) */ int permission(struct inode * inode,int mask) { - int mode = inode->i_mode; + int mode; + if (!inode) { +printk("permission: null inode\n"); + return -EACCES; /* deny access */ + } + + mode = inode->i_mode; if (inode->i_op && inode->i_op->permission) return inode->i_op->permission(inode, mask); else if ((mask & S_IWOTH) && IS_RDONLY(inode) && --- linux-2.1.45/fs/ext2/ialloc.c.old Mon Jul 14 15:25:23 1997 +++ linux-2.1.45/fs/ext2/ialloc.c Mon Jul 14 16:04:08 1997 @@ -404,7 +404,6 @@ sb->s_dirt = 1; inode->i_mode = mode; inode->i_sb = sb; - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; --- linux-2.1.45/arch/i386/mm/init.c.old Mon May 12 13:35:38 1997 +++ linux-2.1.45/arch/i386/mm/init.c Mon Jul 14 16:04:08 1997 @@ -31,6 +31,7 @@ extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); +extern void show_inodes(void); /* * BAD_PAGE is the page that is used for page faults when linux @@ -92,6 +93,7 @@ printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); show_buffers(); + show_inodes(); #ifdef CONFIG_NET show_net_buffers(); #endif --- linux-2.1.45/fs/sysv/ialloc.c.old Thu Jun 26 14:20:54 1997 +++ linux-2.1.45/fs/sysv/ialloc.c Mon Jul 14 16:04:09 1997 @@ -150,23 +150,22 @@ mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); sb->s_dirt = 1; /* and needs time stamp */ - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; inode->i_blocks = inode->i_blksize = 0; + mark_inode_dirty(inode); insert_inode_hash(inode); /* Change directory entry: */ inode->i_mode = 0; /* for sysv_write_inode() */ inode->i_size = 0; /* ditto */ sysv_write_inode(inode); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ - inode->i_dirt = 1; /* cleared by sysv_write_inode() */ + mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ (*sb->sv_sb_total_free_inodes)--; mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */ --- linux-2.1.45/fs/affs/inode.c.old Thu Jun 26 14:20:50 1997 +++ linux-2.1.45/fs/affs/inode.c Mon Jul 14 16:04:09 1997 @@ -899,12 +899,10 @@ return NULL; } - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; - inode->i_dirt = 1; inode->i_ino = block; inode->i_op = NULL; inode->i_blocks = 0; @@ -924,6 +922,7 @@ inode->u.affs_i.i_cache_users = 0; inode->u.affs_i.i_lastblock = -1; + mark_inode_dirty(inode); insert_inode_hash(inode); return inode; --- linux-2.1.45/fs/minix/bitmap.c.old Thu Jun 26 14:20:52 1997 +++ linux-2.1.45/fs/minix/bitmap.c Mon Jul 14 16:04:09 1997 @@ -251,16 +251,15 @@ iput(inode); return NULL; } - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; inode->i_ino = j; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; inode->i_blocks = inode->i_blksize = 0; + mark_inode_dirty(inode); insert_inode_hash(inode); return inode; } | |