Commit 3d48f700 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix writeback_inodes-vs-umount race

Fix bug identified by Chris Mason.

If writeback_inodes is left holding a ref on the superblock's last inode then
the superblock list walk can race with umount and the superblock can be
released.

Take and put a ref against the superblock to fix that.
parent 066479e3
......@@ -397,12 +397,16 @@ writeback_inodes(struct writeback_control *wbc)
spin_lock(&inode_lock);
spin_lock(&sb_lock);
restart:
sb = sb_entry(super_blocks.prev);
for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.prev)) {
if (!list_empty(&sb->s_dirty) || !list_empty(&sb->s_io)) {
sb->s_count++;
spin_unlock(&sb_lock);
sync_sb_inodes(sb, wbc);
spin_lock(&sb_lock);
if (__put_super(sb))
goto restart;
}
if (wbc->nr_to_write <= 0)
break;
......
......@@ -101,6 +101,21 @@ static inline void destroy_super(struct super_block *s)
/* Superblock refcounting */
/*
* Drop a superblock's refcount. Returns non-zero if the superblock was
* destroyed. The caller must hold sb_lock.
*/
int __put_super(struct super_block *sb)
{
int ret = 0;
if (!--sb->s_count) {
destroy_super(sb);
ret = 1;
}
return ret;
}
/**
* put_super - drop a temporary reference to superblock
* @s: superblock in question
......@@ -108,14 +123,14 @@ static inline void destroy_super(struct super_block *s)
* Drops a temporary reference, frees superblock if there's no
* references left.
*/
static inline void put_super(struct super_block *s)
static void put_super(struct super_block *sb)
{
spin_lock(&sb_lock);
if (!--s->s_count)
destroy_super(s);
__put_super(sb);
spin_unlock(&sb_lock);
}
/**
* deactivate_super - drop an active reference to superblock
* @s: superblock to deactivate
......
......@@ -1114,6 +1114,7 @@ struct super_block *sget(struct file_system_type *type,
void *data);
struct super_block *get_sb_pseudo(struct file_system_type *, char *,
struct super_operations *ops, unsigned long);
int __put_super(struct super_block *sb);
void unnamed_dev_init(void);
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment