Messages in this thread Patch in this message | | | Date | Fri, 08 Aug 1997 21:48:37 -0400 | From | Bill Hawes <> | Subject | patch for 2.1.48 fs/inode.c sync_inodes |
| |
Attached is a cleaned-up patch against 2.1.48 for sync_inodes by device, using a per super-block dirty list for efficiency.
The patch also modifies fs_may_mount and fs_may_umount to use the return codes from the invalidate_inodes operation. The mount test becomes !invalidate_inodes(dev), and unmounting is root->d_count == 1 && !invalidate_inodes_except(sb->s_dev, root->d_inode). This stronger test ensures that no inodes are being stranded by unmounting a device.
Regards, Bill--- linux-2.1.48/fs/inode.c.old Tue Aug 5 10:46:01 1997 +++ linux-2.1.48/fs/inode.c Fri Aug 8 21:04:20 1997 @@ -30,16 +30,15 @@ * Each inode can be on two separate lists. One is * the hash list of the inode, used for lookups. The * other linked list is the "type" list: - * "in_use" - valid inode, hashed - * "dirty" - valid inode, hashed, dirty. + * "in_use" - valid inode, hashed if i_nlink > 0 + * "dirty" - valid inode, hashed if i_nlink > 0, dirty. * "unused" - ready to be re-used. Not hashed. * - * The two first versions also have a dirty list, allowing - * for low-overhead inode sync() operations. + * A "dirty" list is maintained for each super block, + * allowing for low-overhead inode sync() operations. */ static LIST_HEAD(inode_in_use); -static LIST_HEAD(inode_dirty); static LIST_HEAD(inode_unused); static struct list_head inode_hashtable[HASH_SIZE]; @@ -59,12 +58,19 @@ int max_inodes = NR_INODE; +/* + * Put the inode on the super block's dirty list + */ void __mark_inode_dirty(struct inode *inode) { - spin_lock(&inode_lock); - list_del(&inode->i_list); - list_add(&inode->i_list, &inode_dirty); - spin_unlock(&inode_lock); + struct super_block * sb = inode->i_sb; + + if (sb) { + spin_lock(&inode_lock); + list_del(&inode->i_list); + list_add(&inode->i_list, &sb->s_dirty); + spin_unlock(&inode_lock); + } } static inline void unlock_inode(struct inode *inode) @@ -169,14 +175,28 @@ } /* - * "sync_inodes()" goes through the dirty list - * and writes them out and puts them back on - * the normal list. + * "sync_inodes()" goes through the super block's dirty list, + * writes them out, and puts them back on the normal list. */ void sync_inodes(kdev_t dev) { + struct super_block * sb = super_blocks + 0; + int i; + + /* + * Search the super_blocks array for the device(s) to sync. + */ spin_lock(&inode_lock); - sync_list(&inode_dirty, &inode_in_use); + for (i = NR_SUPER ; i-- ; sb++) { + if (!sb->s_dev) + continue; + if (dev && sb->s_dev != dev) + continue; + + sync_list(&sb->s_dirty, &inode_in_use); + if (dev) + break; + } spin_unlock(&inode_lock); } @@ -185,10 +205,17 @@ */ void write_inode_now(struct inode *inode) { - spin_lock(&inode_lock); - if (test_bit(I_DIRTY, &inode->i_state)) - sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode); - spin_unlock(&inode_lock); + struct super_block * sb = inode->i_sb; + + if (sb) { + spin_lock(&inode_lock); + if (test_bit(I_DIRTY, &inode->i_state)) + sync_one(&sb->s_dirty, &inode_in_use, &inode->i_list, + inode); + spin_unlock(&inode_lock); + } + else + printk("write_inode_now: no super block\n"); } /* @@ -232,7 +259,11 @@ spin_unlock(&inode_lock); } -static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose) +/* + * Invalidate all inodes for a device, except for the root inode. + */ +static int invalidate_list(struct list_head *head, kdev_t dev, + struct inode * root, struct list_head * dispose) { struct list_head *next; int busy = 0; @@ -248,6 +279,8 @@ inode = list_entry(tmp, struct inode, i_list); if (inode->i_dev != dev) continue; + if (inode == root) + continue; if (!inode->i_count && !inode->i_state) { list_del(&inode->i_hash); INIT_LIST_HEAD(&inode->i_hash); @@ -267,14 +300,16 @@ * is because we don't want to sleep while messing * with the global lists.. */ -int invalidate_inodes(kdev_t dev) +static int invalidate_inodes_except(kdev_t dev, struct inode * root) { + struct super_block * sb = get_super(dev); int busy; LIST_HEAD(throw_away); spin_lock(&inode_lock); - busy = invalidate_list(&inode_in_use, dev, &throw_away); - busy |= invalidate_list(&inode_dirty, dev, &throw_away); + busy = invalidate_list(&inode_in_use, dev, root, &throw_away); + if (sb) + busy |= invalidate_list(&sb->s_dirty, dev, root, &throw_away); spin_unlock(&inode_lock); dispose_list(&throw_away); @@ -282,6 +317,11 @@ return busy; } +int invalidate_inodes(kdev_t dev) +{ + return invalidate_inodes_except(dev, NULL); +} + /* * 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 @@ -534,18 +574,34 @@ } /* - * 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; + return !invalidate_inodes(dev); } +/* + * Check whether we can unmount. + */ int fs_may_umount(struct super_block *sb, struct dentry * root) { + int busy; + shrink_dcache(); - return root->d_count == 1; + + if (!root->d_inode || root->d_inode->i_dev != sb->s_dev) { + printk("fs_may_umount: root inode not on device??\n"); + return 0; + } + + /* + * Invalidate the inodes for this device. Device has been synced + * prior to call, so there should be no dirty inodes. + */ + busy = invalidate_inodes_except(sb->s_dev, root->d_inode); + + return (root->d_count == 1) && !busy; } /* This belongs in file_table.c, not here... */ --- linux-2.1.48/fs/super.c.old Tue Aug 5 10:46:01 1997 +++ linux-2.1.48/fs/super.c Tue Aug 5 12:58:36 1997 @@ -1067,7 +1083,16 @@ __initfunc(void mount_root(void)) { + struct super_block * sb = super_blocks; + int i; + memset(super_blocks, 0, sizeof(super_blocks)); + /* + * Initialize the dirty inode list headers for the super blocks + */ + for (i = NR_SUPER ; i-- ; sb++) + INIT_LIST_HEAD(&sb->s_dirty); + do_mount_root(); } --- linux-2.1.48/include/linux/fs.h.old Tue Aug 5 10:48:14 1997 +++ linux-2.1.48/include/linux/fs.h Fri Aug 8 16:26:32 1997 @@ -501,6 +501,7 @@ struct inode *s_ibasket; short int s_ibasket_count; short int s_ibasket_max; + struct list_head s_dirty; /* dirty inodes */ union { struct minix_sb_info minix_sb; | |