Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
  [PATCH] fix SMP ordering hole in fcntl_setlk()
  [PATCH] kill ->put_inode
  [PATCH] fix reservation discarding in affs
This commit is contained in:
Linus Torvalds 2008-05-06 11:39:57 -07:00
commit bb78be8397
10 changed files with 56 additions and 62 deletions

View File

@ -92,7 +92,6 @@ prototypes:
void (*destroy_inode)(struct inode *); void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *); void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int); int (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *); void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);
@ -115,7 +114,6 @@ alloc_inode: no no no
destroy_inode: no destroy_inode: no
dirty_inode: no (must not sleep) dirty_inode: no (must not sleep)
write_inode: no write_inode: no
put_inode: no
drop_inode: no !!!inode_lock!!! drop_inode: no !!!inode_lock!!!
delete_inode: no delete_inode: no
put_super: yes yes no put_super: yes yes no

View File

@ -205,7 +205,6 @@ struct super_operations {
void (*dirty_inode) (struct inode *); void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int); int (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *); void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);
@ -246,9 +245,6 @@ or bottom half).
inode to disc. The second parameter indicates whether the write inode to disc. The second parameter indicates whether the write
should be synchronous or not, not all filesystems check this flag. should be synchronous or not, not all filesystems check this flag.
put_inode: called when the VFS inode is removed from the inode
cache.
drop_inode: called when the last access to the inode is dropped, drop_inode: called when the last access to the inode is dropped,
with the inode_lock spinlock held. with the inode_lock spinlock held.

View File

@ -48,7 +48,7 @@ struct affs_ext_key {
* affs fs inode data in memory * affs fs inode data in memory
*/ */
struct affs_inode_info { struct affs_inode_info {
u32 i_opencnt; atomic_t i_opencnt;
struct semaphore i_link_lock; /* Protects internal inode access. */ struct semaphore i_link_lock; /* Protects internal inode access. */
struct semaphore i_ext_lock; /* Protects internal inode access. */ struct semaphore i_ext_lock; /* Protects internal inode access. */
#define i_hash_lock i_ext_lock #define i_hash_lock i_ext_lock
@ -170,8 +170,6 @@ extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry,
extern unsigned long affs_parent_ino(struct inode *dir); extern unsigned long affs_parent_ino(struct inode *dir);
extern struct inode *affs_new_inode(struct inode *dir); extern struct inode *affs_new_inode(struct inode *dir);
extern int affs_notify_change(struct dentry *dentry, struct iattr *attr); extern int affs_notify_change(struct dentry *dentry, struct iattr *attr);
extern void affs_put_inode(struct inode *inode);
extern void affs_drop_inode(struct inode *inode);
extern void affs_delete_inode(struct inode *inode); extern void affs_delete_inode(struct inode *inode);
extern void affs_clear_inode(struct inode *inode); extern void affs_clear_inode(struct inode *inode);
extern struct inode *affs_iget(struct super_block *sb, extern struct inode *affs_iget(struct super_block *sb,

View File

@ -48,8 +48,9 @@ affs_file_open(struct inode *inode, struct file *filp)
{ {
if (atomic_read(&filp->f_count) != 1) if (atomic_read(&filp->f_count) != 1)
return 0; return 0;
pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt); pr_debug("AFFS: open(%lu,%d)\n",
AFFS_I(inode)->i_opencnt++; inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
atomic_inc(&AFFS_I(inode)->i_opencnt);
return 0; return 0;
} }
@ -58,10 +59,16 @@ affs_file_release(struct inode *inode, struct file *filp)
{ {
if (atomic_read(&filp->f_count) != 0) if (atomic_read(&filp->f_count) != 0)
return 0; return 0;
pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt); pr_debug("AFFS: release(%lu, %d)\n",
AFFS_I(inode)->i_opencnt--; inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
if (!AFFS_I(inode)->i_opencnt)
if (atomic_dec_and_test(&AFFS_I(inode)->i_opencnt)) {
mutex_lock(&inode->i_mutex);
if (inode->i_size != AFFS_I(inode)->mmu_private)
affs_truncate(inode);
affs_free_prealloc(inode); affs_free_prealloc(inode);
mutex_unlock(&inode->i_mutex);
}
return 0; return 0;
} }
@ -180,7 +187,7 @@ affs_get_extblock(struct inode *inode, u32 ext)
/* inline the simplest case: same extended block as last time */ /* inline the simplest case: same extended block as last time */
struct buffer_head *bh = AFFS_I(inode)->i_ext_bh; struct buffer_head *bh = AFFS_I(inode)->i_ext_bh;
if (ext == AFFS_I(inode)->i_ext_last) if (ext == AFFS_I(inode)->i_ext_last)
atomic_inc(&bh->b_count); get_bh(bh);
else else
/* we have to do more (not inlined) */ /* we have to do more (not inlined) */
bh = affs_get_extblock_slow(inode, ext); bh = affs_get_extblock_slow(inode, ext);
@ -306,7 +313,7 @@ store_ext:
affs_brelse(AFFS_I(inode)->i_ext_bh); affs_brelse(AFFS_I(inode)->i_ext_bh);
AFFS_I(inode)->i_ext_last = ext; AFFS_I(inode)->i_ext_last = ext;
AFFS_I(inode)->i_ext_bh = bh; AFFS_I(inode)->i_ext_bh = bh;
atomic_inc(&bh->b_count); get_bh(bh);
return bh; return bh;
@ -324,7 +331,6 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
pr_debug("AFFS: get_block(%u, %lu)\n", (u32)inode->i_ino, (unsigned long)block); pr_debug("AFFS: get_block(%u, %lu)\n", (u32)inode->i_ino, (unsigned long)block);
BUG_ON(block > (sector_t)0x7fffffffUL); BUG_ON(block > (sector_t)0x7fffffffUL);
if (block >= AFFS_I(inode)->i_blkcnt) { if (block >= AFFS_I(inode)->i_blkcnt) {
@ -827,6 +833,8 @@ affs_truncate(struct inode *inode)
res = mapping->a_ops->write_begin(NULL, mapping, size, 0, 0, &page, &fsdata); res = mapping->a_ops->write_begin(NULL, mapping, size, 0, 0, &page, &fsdata);
if (!res) if (!res)
res = mapping->a_ops->write_end(NULL, mapping, size, 0, 0, page, fsdata); res = mapping->a_ops->write_end(NULL, mapping, size, 0, 0, page, fsdata);
else
inode->i_size = AFFS_I(inode)->mmu_private;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return; return;
} else if (inode->i_size == AFFS_I(inode)->mmu_private) } else if (inode->i_size == AFFS_I(inode)->mmu_private)
@ -862,6 +870,7 @@ affs_truncate(struct inode *inode)
blk++; blk++;
} else } else
AFFS_HEAD(ext_bh)->first_data = 0; AFFS_HEAD(ext_bh)->first_data = 0;
AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(i);
size = AFFS_SB(sb)->s_hashsize; size = AFFS_SB(sb)->s_hashsize;
if (size > blkcnt - blk + i) if (size > blkcnt - blk + i)
size = blkcnt - blk + i; size = blkcnt - blk + i;

View File

@ -58,7 +58,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
AFFS_I(inode)->i_extcnt = 1; AFFS_I(inode)->i_extcnt = 1;
AFFS_I(inode)->i_ext_last = ~1; AFFS_I(inode)->i_ext_last = ~1;
AFFS_I(inode)->i_protect = prot; AFFS_I(inode)->i_protect = prot;
AFFS_I(inode)->i_opencnt = 0; atomic_set(&AFFS_I(inode)->i_opencnt, 0);
AFFS_I(inode)->i_blkcnt = 0; AFFS_I(inode)->i_blkcnt = 0;
AFFS_I(inode)->i_lc = NULL; AFFS_I(inode)->i_lc = NULL;
AFFS_I(inode)->i_lc_size = 0; AFFS_I(inode)->i_lc_size = 0;
@ -108,8 +108,6 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
inode->i_mode |= S_IFDIR; inode->i_mode |= S_IFDIR;
} else } else
inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
if (tail->link_chain)
inode->i_nlink = 2;
/* Maybe it should be controlled by mount parameter? */ /* Maybe it should be controlled by mount parameter? */
//inode->i_mode |= S_ISVTX; //inode->i_mode |= S_ISVTX;
inode->i_op = &affs_dir_inode_operations; inode->i_op = &affs_dir_inode_operations;
@ -244,32 +242,13 @@ out:
return error; return error;
} }
void
affs_put_inode(struct inode *inode)
{
pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
affs_free_prealloc(inode);
}
void
affs_drop_inode(struct inode *inode)
{
mutex_lock(&inode->i_mutex);
if (inode->i_size != AFFS_I(inode)->mmu_private)
affs_truncate(inode);
mutex_unlock(&inode->i_mutex);
generic_drop_inode(inode);
}
void void
affs_delete_inode(struct inode *inode) affs_delete_inode(struct inode *inode)
{ {
pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(&inode->i_data, 0);
inode->i_size = 0; inode->i_size = 0;
if (S_ISREG(inode->i_mode)) affs_truncate(inode);
affs_truncate(inode);
clear_inode(inode); clear_inode(inode);
affs_free_block(inode->i_sb, inode->i_ino); affs_free_block(inode->i_sb, inode->i_ino);
} }
@ -277,9 +256,12 @@ affs_delete_inode(struct inode *inode)
void void
affs_clear_inode(struct inode *inode) affs_clear_inode(struct inode *inode)
{ {
unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc; unsigned long cache_page;
pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
affs_free_prealloc(inode);
cache_page = (unsigned long)AFFS_I(inode)->i_lc;
if (cache_page) { if (cache_page) {
pr_debug("AFFS: freeing ext cache\n"); pr_debug("AFFS: freeing ext cache\n");
AFFS_I(inode)->i_lc = NULL; AFFS_I(inode)->i_lc = NULL;
@ -316,7 +298,7 @@ affs_new_inode(struct inode *dir)
inode->i_ino = block; inode->i_ino = block;
inode->i_nlink = 1; inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
AFFS_I(inode)->i_opencnt = 0; atomic_set(&AFFS_I(inode)->i_opencnt, 0);
AFFS_I(inode)->i_blkcnt = 0; AFFS_I(inode)->i_blkcnt = 0;
AFFS_I(inode)->i_lc = NULL; AFFS_I(inode)->i_lc = NULL;
AFFS_I(inode)->i_lc_size = 0; AFFS_I(inode)->i_lc_size = 0;
@ -369,12 +351,12 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3
switch (type) { switch (type) {
case ST_LINKFILE: case ST_LINKFILE:
case ST_LINKDIR: case ST_LINKDIR:
inode_bh = bh;
retval = -ENOSPC; retval = -ENOSPC;
block = affs_alloc_block(dir, dir->i_ino); block = affs_alloc_block(dir, dir->i_ino);
if (!block) if (!block)
goto err; goto err;
retval = -EIO; retval = -EIO;
inode_bh = bh;
bh = affs_getzeroblk(sb, block); bh = affs_getzeroblk(sb, block);
if (!bh) if (!bh)
goto err; goto err;

View File

@ -234,7 +234,8 @@ affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
int int
affs_unlink(struct inode *dir, struct dentry *dentry) affs_unlink(struct inode *dir, struct dentry *dentry)
{ {
pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino, pr_debug("AFFS: unlink(dir=%d, %lu \"%.*s\")\n", (u32)dir->i_ino,
dentry->d_inode->i_ino,
(int)dentry->d_name.len, dentry->d_name.name); (int)dentry->d_name.len, dentry->d_name.name);
return affs_remove_header(dentry); return affs_remove_header(dentry);
@ -302,7 +303,8 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
int int
affs_rmdir(struct inode *dir, struct dentry *dentry) affs_rmdir(struct inode *dir, struct dentry *dentry)
{ {
pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino, pr_debug("AFFS: rmdir(dir=%u, %lu \"%.*s\")\n", (u32)dir->i_ino,
dentry->d_inode->i_ino,
(int)dentry->d_name.len, dentry->d_name.name); (int)dentry->d_name.len, dentry->d_name.name);
return affs_remove_header(dentry); return affs_remove_header(dentry);

View File

@ -71,12 +71,18 @@ static struct kmem_cache * affs_inode_cachep;
static struct inode *affs_alloc_inode(struct super_block *sb) static struct inode *affs_alloc_inode(struct super_block *sb)
{ {
struct affs_inode_info *ei; struct affs_inode_info *i;
ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, GFP_KERNEL);
if (!ei) i = kmem_cache_alloc(affs_inode_cachep, GFP_KERNEL);
if (!i)
return NULL; return NULL;
ei->vfs_inode.i_version = 1;
return &ei->vfs_inode; i->vfs_inode.i_version = 1;
i->i_lc = NULL;
i->i_ext_bh = NULL;
i->i_pa_cnt = 0;
return &i->vfs_inode;
} }
static void affs_destroy_inode(struct inode *inode) static void affs_destroy_inode(struct inode *inode)
@ -114,8 +120,6 @@ static const struct super_operations affs_sops = {
.alloc_inode = affs_alloc_inode, .alloc_inode = affs_alloc_inode,
.destroy_inode = affs_destroy_inode, .destroy_inode = affs_destroy_inode,
.write_inode = affs_write_inode, .write_inode = affs_write_inode,
.put_inode = affs_put_inode,
.drop_inode = affs_drop_inode,
.delete_inode = affs_delete_inode, .delete_inode = affs_delete_inode,
.clear_inode = affs_clear_inode, .clear_inode = affs_clear_inode,
.put_super = affs_put_super, .put_super = affs_put_super,

View File

@ -1153,9 +1153,6 @@ void iput(struct inode *inode)
BUG_ON(inode->i_state == I_CLEAR); BUG_ON(inode->i_state == I_CLEAR);
if (op && op->put_inode)
op->put_inode(inode);
if (atomic_dec_and_lock(&inode->i_count, &inode_lock)) if (atomic_dec_and_lock(&inode->i_count, &inode_lock))
iput_final(inode); iput_final(inode);
} }

View File

@ -1753,6 +1753,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
struct file_lock *file_lock = locks_alloc_lock(); struct file_lock *file_lock = locks_alloc_lock();
struct flock flock; struct flock flock;
struct inode *inode; struct inode *inode;
struct file *f;
int error; int error;
if (file_lock == NULL) if (file_lock == NULL)
@ -1825,7 +1826,15 @@ again:
* Attempt to detect a close/fcntl race and recover by * Attempt to detect a close/fcntl race and recover by
* releasing the lock that was just acquired. * releasing the lock that was just acquired.
*/ */
if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) { /*
* we need that spin_lock here - it prevents reordering between
* update of inode->i_flock and check for it done in close().
* rcu_read_lock() wouldn't do.
*/
spin_lock(&current->files->file_lock);
f = fcheck(fd);
spin_unlock(&current->files->file_lock);
if (!error && f != filp && flock.l_type != F_UNLCK) {
flock.l_type = F_UNLCK; flock.l_type = F_UNLCK;
goto again; goto again;
} }
@ -1881,6 +1890,7 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
struct file_lock *file_lock = locks_alloc_lock(); struct file_lock *file_lock = locks_alloc_lock();
struct flock64 flock; struct flock64 flock;
struct inode *inode; struct inode *inode;
struct file *f;
int error; int error;
if (file_lock == NULL) if (file_lock == NULL)
@ -1953,7 +1963,10 @@ again:
* Attempt to detect a close/fcntl race and recover by * Attempt to detect a close/fcntl race and recover by
* releasing the lock that was just acquired. * releasing the lock that was just acquired.
*/ */
if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) { spin_lock(&current->files->file_lock);
f = fcheck(fd);
spin_unlock(&current->files->file_lock);
if (!error && f != filp && flock.l_type != F_UNLCK) {
flock.l_type = F_UNLCK; flock.l_type = F_UNLCK;
goto again; goto again;
} }

View File

@ -1289,17 +1289,12 @@ extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
extern ssize_t vfs_writev(struct file *, const struct iovec __user *, extern ssize_t vfs_writev(struct file *, const struct iovec __user *,
unsigned long, loff_t *); unsigned long, loff_t *);
/*
* NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
* without the big kernel lock held in all filesystems.
*/
struct super_operations { struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb); struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *); void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *); void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int); int (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *); void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *); void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *); void (*put_super) (struct super_block *);