Skip to content

Commit

Permalink
Replace BKL with superblock lock in fat/msdos/vfat
Browse files Browse the repository at this point in the history
This replaces the use of the BKL in the FAT family of filesystems with the
existing superblock lock instead.

The code already appears to do mostly proper locking with its own private
spinlocks (and mutexes), but while the BKL could possibly have been
dropped entirely, converting it to use the superblock lock (which is just
a regular mutex) is the conservative thing to do.

As a per-filesystem mutex, it not only won't have any of the possible
latency issues related to the BKL, but the lock is obviously private to
the particular filesystem instance and will thus not cause problems for
entirely unrelated users like the BKL can.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
  • Loading branch information
torvalds authored and Jonathan Corbet committed Jun 20, 2008
1 parent 5ca6a93 commit 8f59342
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 50 deletions.
2 changes: 1 addition & 1 deletion fs/fat/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void fat_cache_destroy(void)

static inline struct fat_cache *fat_cache_alloc(struct inode *inode)
{
return kmem_cache_alloc(fat_cache_cachep, GFP_KERNEL);
return kmem_cache_alloc(fat_cache_cachep, GFP_NOFS);
}

static inline void fat_cache_free(struct fat_cache *cache)
Expand Down
4 changes: 2 additions & 2 deletions fs/fat/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
loff_t cpos;
int ret = 0;

lock_kernel();
lock_super(sb);

cpos = filp->f_pos;
/* Fake . and .. for the root directory. */
Expand Down Expand Up @@ -654,7 +654,7 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent,
if (unicode)
__putname(unicode);
out:
unlock_kernel();
unlock_super(sb);
return ret;
}

Expand Down
12 changes: 7 additions & 5 deletions fs/fat/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ static int fat_free(struct inode *inode, int skip)

void fat_truncate(struct inode *inode)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
const unsigned int cluster_size = sbi->cluster_size;
int nr_clusters;

Expand All @@ -242,9 +243,9 @@ void fat_truncate(struct inode *inode)

nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;

lock_kernel();
lock_super(sb);
fat_free(inode, nr_clusters);
unlock_kernel();
unlock_super(sb);
fat_flush_inodes(inode->i_sb, inode, NULL);
}

Expand Down Expand Up @@ -297,12 +298,13 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)

int fat_setattr(struct dentry *dentry, struct iattr *attr)
{
struct super_block *sb = dentry->d_sb;
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
struct inode *inode = dentry->d_inode;
int mask, error = 0;
unsigned int ia_valid;

lock_kernel();
lock_super(sb);

/*
* Expand the file. Since inode_setattr() updates ->i_size
Expand Down Expand Up @@ -356,7 +358,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
mask = sbi->options.fs_fmask;
inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
out:
unlock_kernel();
unlock_super(sb);
return error;
}
EXPORT_SYMBOL_GPL(fat_setattr);
Expand Down
26 changes: 16 additions & 10 deletions fs/fat/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,13 @@ static void fat_delete_inode(struct inode *inode)

static void fat_clear_inode(struct inode *inode)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);

lock_kernel();
spin_lock(&sbi->inode_hash_lock);
fat_cache_inval_inode(inode);
hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
spin_unlock(&sbi->inode_hash_lock);
unlock_kernel();
}

static void fat_write_super(struct super_block *sb)
Expand Down Expand Up @@ -485,7 +484,7 @@ static struct kmem_cache *fat_inode_cachep;
static struct inode *fat_alloc_inode(struct super_block *sb)
{
struct msdos_inode_info *ei;
ei = kmem_cache_alloc(fat_inode_cachep, GFP_KERNEL);
ei = kmem_cache_alloc(fat_inode_cachep, GFP_NOFS);
if (!ei)
return NULL;
return &ei->vfs_inode;
Expand Down Expand Up @@ -567,7 +566,7 @@ static int fat_write_inode(struct inode *inode, int wait)
if (inode->i_ino == MSDOS_ROOT_INO || !i_pos)
return 0;

lock_kernel();
lock_super(sb);
bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
if (!bh) {
printk(KERN_ERR "FAT: unable to read inode block "
Expand All @@ -579,7 +578,7 @@ static int fat_write_inode(struct inode *inode, int wait)
if (i_pos != MSDOS_I(inode)->i_pos) {
spin_unlock(&sbi->inode_hash_lock);
brelse(bh);
unlock_kernel();
unlock_super(sb);
goto retry;
}

Expand All @@ -606,7 +605,7 @@ static int fat_write_inode(struct inode *inode, int wait)
err = sync_dirty_buffer(bh);
brelse(bh);
out:
unlock_kernel();
unlock_super(sb);
return err;
}

Expand Down Expand Up @@ -736,21 +735,22 @@ fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)

static struct dentry *fat_get_parent(struct dentry *child)
{
struct super_block *sb = child->d_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
loff_t i_pos;
struct dentry *parent;
struct inode *inode;
int err;

lock_kernel();
lock_super(sb);

err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
if (err) {
parent = ERR_PTR(err);
goto out;
}
inode = fat_build_inode(child->d_sb, de, i_pos);
inode = fat_build_inode(sb, de, i_pos);
brelse(bh);
if (IS_ERR(inode)) {
parent = ERR_CAST(inode);
Expand All @@ -762,7 +762,7 @@ static struct dentry *fat_get_parent(struct dentry *child)
parent = ERR_PTR(-ENOMEM);
}
out:
unlock_kernel();
unlock_super(sb);

return parent;
}
Expand Down Expand Up @@ -1172,6 +1172,12 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
long error;
char buf[50];

/*
* GFP_KERNEL is ok here, because while we do hold the
* supeblock lock, memory pressure can't call back into
* the filesystem, since we're only just about to mount
* it and have no inodes etc active!
*/
sbi = kzalloc(sizeof(struct msdos_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
Expand Down
35 changes: 19 additions & 16 deletions fs/msdos/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,

dentry->d_op = &msdos_dentry_operations;

lock_kernel();
lock_super(sb);
res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
if (res == -ENOENT)
goto add;
Expand All @@ -232,7 +232,7 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
if (dentry)
dentry->d_op = &msdos_dentry_operations;
out:
unlock_kernel();
unlock_super(sb);
if (!res)
return dentry;
return ERR_PTR(res);
Expand Down Expand Up @@ -286,7 +286,7 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
unsigned char msdos_name[MSDOS_NAME];
int err, is_hid;

lock_kernel();
lock_super(sb);

err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
msdos_name, &MSDOS_SB(sb)->options);
Expand Down Expand Up @@ -315,7 +315,7 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,

d_instantiate(dentry, inode);
out:
unlock_kernel();
unlock_super(sb);
if (!err)
err = fat_flush_inodes(sb, dir, inode);
return err;
Expand All @@ -324,11 +324,12 @@ static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
/***** Remove a directory */
static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
struct fat_slot_info sinfo;
int err;

lock_kernel();
lock_super(sb);
/*
* Check whether the directory is not in use, then check
* whether it is empty.
Expand All @@ -349,9 +350,9 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
inode->i_ctime = CURRENT_TIME_SEC;
fat_detach(inode);
out:
unlock_kernel();
unlock_super(sb);
if (!err)
err = fat_flush_inodes(inode->i_sb, dir, inode);
err = fat_flush_inodes(sb, dir, inode);

return err;
}
Expand All @@ -366,7 +367,7 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
struct timespec ts;
int err, is_hid, cluster;

lock_kernel();
lock_super(sb);

err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
msdos_name, &MSDOS_SB(sb)->options);
Expand Down Expand Up @@ -404,25 +405,26 @@ static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)

d_instantiate(dentry, inode);

unlock_kernel();
unlock_super(sb);
fat_flush_inodes(sb, dir, inode);
return 0;

out_free:
fat_free_clusters(dir, cluster);
out:
unlock_kernel();
unlock_super(sb);
return err;
}

/***** Unlink a file */
static int msdos_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct super_block *sb= inode->i_sb;
struct fat_slot_info sinfo;
int err;

lock_kernel();
lock_super(sb);
err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
if (err)
goto out;
Expand All @@ -434,9 +436,9 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry)
inode->i_ctime = CURRENT_TIME_SEC;
fat_detach(inode);
out:
unlock_kernel();
unlock_super(sb);
if (!err)
err = fat_flush_inodes(inode->i_sb, dir, inode);
err = fat_flush_inodes(sb, dir, inode);

return err;
}
Expand Down Expand Up @@ -618,10 +620,11 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
struct super_block *sb = old_dir->i_sb;
unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
int err, is_hid;

lock_kernel();
lock_super(sb);

err = msdos_format_name(old_dentry->d_name.name,
old_dentry->d_name.len, old_msdos_name,
Expand All @@ -640,9 +643,9 @@ static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
new_dir, new_msdos_name, new_dentry, is_hid);
out:
unlock_kernel();
unlock_super(sb);
if (!err)
err = fat_flush_inodes(old_dir->i_sb, old_dir, new_dir);
err = fat_flush_inodes(sb, old_dir, new_dir);
return err;
}

Expand Down
Loading

0 comments on commit 8f59342

Please sign in to comment.