OK, that's the first big one.
* we are freeing superblocks when s_count hits zero.
* we are removing superblock from the list when s_active hits zero (at
that point superblock is already doomed).
* functions that used to traverse the list need to restart after
blocking actions. Done.
* sync_inodes_sb() is slightly changed - now it repeats the whole
thing if some of the locked inodes had been dirtied while we were waiting.
As the result, wait_on_dirty() is gone - it's not needed anymore.
* sync_supers() gets shared lock on ->s_umount for the duration of
->write_super(). Since we are already grabbing ->s_lock here it doesn't
add any deadlocks.
diff -urN S8-pre7-s_count/fs/inode.c S8-pre7-freeing/fs/inode.c
--- S8-pre7-s_count/fs/inode.c Thu Aug 9 19:07:32 2001
+++ S8-pre7-freeing/fs/inode.c Thu Aug 9 19:07:32 2001
@@ -258,23 +258,6 @@
__sync_one(list_entry(tmp, struct inode, i_list), 0);
}
-static inline int wait_on_dirty(struct list_head *head)
-{
- struct list_head * tmp;
- list_for_each(tmp, head) {
- struct inode *inode = list_entry(tmp, struct inode, i_list);
- if (!inode->i_state & I_DIRTY)
- continue;
- __iget(inode);
- spin_unlock(&inode_lock);
- __wait_on_inode(inode);
- iput(inode);
- spin_lock(&inode_lock);
- return 1;
- }
- return 0;
-}
-
static inline void wait_on_locked(struct list_head *head)
{
struct list_head * tmp;
@@ -319,23 +302,13 @@
return 1;
}
-/**
- * sync_inodes
- * @dev: device to sync the inodes from.
- *
- * sync_inodes goes through the super block's dirty list,
- * writes them out, and puts them back on the normal list.
- */
-
-/*
- * caller holds exclusive lock on sb->s_umount
- */
-
void sync_inodes_sb(struct super_block *sb)
{
spin_lock(&inode_lock);
- sync_list(&sb->s_dirty);
- wait_on_locked(&sb->s_locked_inodes);
+ while (!list_empty(&sb->s_dirty)||!list_empty(&sb->s_locked_inodes)) {
+ sync_list(&sb->s_dirty);
+ wait_on_locked(&sb->s_locked_inodes);
+ }
spin_unlock(&inode_lock);
}
@@ -365,37 +338,75 @@
spin_unlock(&inode_lock);
}
+/*
+ * Find a superblock with inodes that need to be synced
+ */
+
+static struct super_block *get_super_to_sync(void)
+{
+ struct list_head *p;
+restart:
+ spin_lock(&inode_lock);
+ spin_lock(&sb_lock);
+ list_for_each(p, &super_blocks) {
+ struct super_block *s = list_entry(p,struct super_block,s_list);
+ if (list_empty(&s->s_dirty) && list_empty(&s->s_locked_inodes))
+ continue;
+ s->s_count++;
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+ down_read(&s->s_umount);
+ if (!s->s_root) {
+ up_read(&s->s_umount);
+ spin_lock(&sb_lock);
+ if (!--s->s_count)
+ kfree(s);
+ spin_unlock(&sb_lock);
+ goto restart;
+ }
+ return s;
+ }
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+ return NULL;
+}
+
+/**
+ * sync_inodes
+ * @dev: device to sync the inodes from.
+ *
+ * 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;
+ struct super_block * s;
/*
* Search the super_blocks array for the device(s) to sync.
*/
- spin_lock(&sb_lock);
- sb = sb_entry(super_blocks.next);
- for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
- if (!sb->s_dev)
- continue;
- if (dev && sb->s_dev != dev)
- continue;
- sb->s_count++;
- spin_unlock(&sb_lock);
- down_read(&sb->s_umount);
- if (sb->s_dev && (sb->s_dev == dev || !dev)) {
- spin_lock(&inode_lock);
- do {
- sync_list(&sb->s_dirty);
- } while (wait_on_dirty(&sb->s_locked_inodes));
- spin_unlock(&inode_lock);
+ if (dev) {
+ if ((s = get_super(dev)) != NULL) {
+ down_read(&s->s_umount);
+ if (s->s_root)
+ sync_inodes_sb(s);
+ up_read(&s->s_umount);
+ spin_lock(&sb_lock);
+ if (!--s->s_count)
+ kfree(s);
+ spin_unlock(&sb_lock);
+ }
+ } else {
+ while ((s = get_super_to_sync()) != NULL) {
+ sync_inodes_sb(s);
+ up_read(&s->s_umount);
+ spin_lock(&sb_lock);
+ if (!--s->s_count)
+ kfree(s);
+ spin_unlock(&sb_lock);
}
- up_read(&sb->s_umount);
- spin_lock(&sb_lock);
- sb->s_count--;
- if (dev)
- break;
}
- spin_unlock(&sb_lock);
}
/*
diff -urN S8-pre7-s_count/fs/super.c S8-pre7-freeing/fs/super.c
--- S8-pre7-s_count/fs/super.c Thu Aug 9 19:07:32 2001
+++ S8-pre7-freeing/fs/super.c Thu Aug 9 19:07:32 2001
@@ -644,7 +644,8 @@
static inline void __put_super(struct super_block *sb)
{
spin_lock(&sb_lock);
- sb->s_count--;
+ if (!--sb->s_count)
+ kfree(sb);
spin_unlock(&sb_lock);
}
@@ -652,6 +653,21 @@
{
__put_super(sb);
}
+
+static void put_super(struct super_block *sb)
+{
+ up_write(&sb->s_umount);
+ __put_super(sb);
+}
+
+static inline void write_super(struct super_block *sb)
+{
+ lock_super(sb);
+ if (sb->s_root && sb->s_dirt)
+ if (sb->s_op && sb->s_op->write_super)
+ sb->s_op->write_super(sb);
+ unlock_super(sb);
+}
/*
* Note: check the dirty flag before waiting, so we don't
@@ -662,26 +678,30 @@
{
struct super_block * sb;
- spin_lock(&sb_lock);
- for (sb = sb_entry(super_blocks.next);
- sb != sb_entry(&super_blocks);
- sb = sb_entry(sb->s_list.next)) {
- if (!sb->s_dev)
- continue;
- if (dev && sb->s_dev != dev)
- continue;
- if (!sb->s_dirt)
- continue;
- sb->s_count++;
- spin_unlock(&sb_lock);
- lock_super(sb);
- if (sb->s_dev && sb->s_dirt && (!dev || dev == sb->s_dev))
- if (sb->s_op && sb->s_op->write_super)
- sb->s_op->write_super(sb);
- unlock_super(sb);
- spin_lock(&sb_lock);
- sb->s_count--;
+ if (dev) {
+ sb = get_super(dev);
+ if (sb) {
+ if (sb->s_dirt)
+ write_super(sb);
+ up_read(&sb->s_umount);
+ __put_super(sb);
+ }
+ return;
}
+restart:
+ spin_lock(&sb_lock);
+ sb = sb_entry(super_blocks.next);
+ while (sb != sb_entry(&super_blocks))
+ if (sb->s_dirt) {
+ sb->s_count++;
+ spin_unlock(&sb_lock);
+ down_read(&sb->s_umount);
+ write_super(sb);
+ up_read(&sb->s_umount);
+ __put_super(sb);
+ goto restart;
+ } else
+ sb = sb_entry(sb->s_list.next);
spin_unlock(&sb_lock);
}
@@ -827,6 +847,9 @@
s->s_type = NULL;
unlock_super(s);
atomic_dec(&s->s_active);
+ spin_lock(&sb_lock);
+ list_del(&s->s_list);
+ spin_unlock(&sb_lock);
__put_super(s);
return NULL;
}
@@ -1022,8 +1045,10 @@
bdput(bdev);
} else
put_unnamed_dev(dev);
- up_write(&sb->s_umount);
- __put_super(sb);
+ spin_lock(&sb_lock);
+ list_del(&sb->s_list);
+ spin_unlock(&sb_lock);
+ put_super(sb);
}
/*
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/