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

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  vfs: new helper - d_make_root()
  dcache: use a dispose list in select_parent
  ceph: d_alloc_root() may fail
  ext4: fix failure exits
  isofs: inode leak on mount failure
This commit is contained in:
Linus Torvalds 2012-01-09 17:37:37 -08:00
commit e4e11180df
5 changed files with 63 additions and 53 deletions

View File

@ -636,19 +636,26 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
req->r_num_caps = 2; req->r_num_caps = 2;
err = ceph_mdsc_do_request(mdsc, NULL, req); err = ceph_mdsc_do_request(mdsc, NULL, req);
if (err == 0) { if (err == 0) {
struct inode *inode = req->r_target_inode;
req->r_target_inode = NULL;
dout("open_root_inode success\n"); dout("open_root_inode success\n");
if (ceph_ino(req->r_target_inode) == CEPH_INO_ROOT && if (ceph_ino(inode) == CEPH_INO_ROOT &&
fsc->sb->s_root == NULL) { fsc->sb->s_root == NULL) {
root = d_alloc_root(req->r_target_inode); root = d_alloc_root(inode);
if (!root) {
iput(inode);
root = ERR_PTR(-ENOMEM);
goto out;
}
ceph_init_dentry(root); ceph_init_dentry(root);
} else { } else {
root = d_obtain_alias(req->r_target_inode); root = d_obtain_alias(inode);
} }
req->r_target_inode = NULL;
dout("open_root_inode success, root dentry is %p\n", root); dout("open_root_inode success, root dentry is %p\n", root);
} else { } else {
root = ERR_PTR(err); root = ERR_PTR(err);
} }
out:
ceph_mdsc_put_request(req); ceph_mdsc_put_request(req);
return root; return root;
} }

View File

@ -276,15 +276,15 @@ static void dentry_lru_prune(struct dentry *dentry)
} }
} }
static void dentry_lru_move_tail(struct dentry *dentry) static void dentry_lru_move_list(struct dentry *dentry, struct list_head *list)
{ {
spin_lock(&dcache_lru_lock); spin_lock(&dcache_lru_lock);
if (list_empty(&dentry->d_lru)) { if (list_empty(&dentry->d_lru)) {
list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); list_add_tail(&dentry->d_lru, list);
dentry->d_sb->s_nr_dentry_unused++; dentry->d_sb->s_nr_dentry_unused++;
dentry_stat.nr_unused++; dentry_stat.nr_unused++;
} else { } else {
list_move_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); list_move_tail(&dentry->d_lru, list);
} }
spin_unlock(&dcache_lru_lock); spin_unlock(&dcache_lru_lock);
} }
@ -770,14 +770,18 @@ static void shrink_dentry_list(struct list_head *list)
} }
/** /**
* __shrink_dcache_sb - shrink the dentry LRU on a given superblock * prune_dcache_sb - shrink the dcache
* @sb: superblock to shrink dentry LRU. * @sb: superblock
* @count: number of entries to prune * @count: number of entries to try to free
* @flags: flags to control the dentry processing
* *
* If flags contains DCACHE_REFERENCED reference dentries will not be pruned. * Attempt to shrink the superblock dcache LRU by @count entries. This is
* done when we need more memory an called from the superblock shrinker
* function.
*
* This function may fail to free any resources if all the dentries are in
* use.
*/ */
static void __shrink_dcache_sb(struct super_block *sb, int count, int flags) void prune_dcache_sb(struct super_block *sb, int count)
{ {
struct dentry *dentry; struct dentry *dentry;
LIST_HEAD(referenced); LIST_HEAD(referenced);
@ -796,13 +800,7 @@ static void __shrink_dcache_sb(struct super_block *sb, int count, int flags)
goto relock; goto relock;
} }
/* if (dentry->d_flags & DCACHE_REFERENCED) {
* If we are honouring the DCACHE_REFERENCED flag and the
* dentry has this flag set, don't free it. Clear the flag
* and put it back on the LRU.
*/
if (flags & DCACHE_REFERENCED &&
dentry->d_flags & DCACHE_REFERENCED) {
dentry->d_flags &= ~DCACHE_REFERENCED; dentry->d_flags &= ~DCACHE_REFERENCED;
list_move(&dentry->d_lru, &referenced); list_move(&dentry->d_lru, &referenced);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
@ -821,23 +819,6 @@ static void __shrink_dcache_sb(struct super_block *sb, int count, int flags)
shrink_dentry_list(&tmp); shrink_dentry_list(&tmp);
} }
/**
* prune_dcache_sb - shrink the dcache
* @sb: superblock
* @nr_to_scan: number of entries to try to free
*
* Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is
* done when we need more memory an called from the superblock shrinker
* function.
*
* This function may fail to free any resources if all the dentries are in
* use.
*/
void prune_dcache_sb(struct super_block *sb, int nr_to_scan)
{
__shrink_dcache_sb(sb, nr_to_scan, DCACHE_REFERENCED);
}
/** /**
* shrink_dcache_sb - shrink dcache for a superblock * shrink_dcache_sb - shrink dcache for a superblock
* @sb: superblock * @sb: superblock
@ -1092,7 +1073,7 @@ EXPORT_SYMBOL(have_submounts);
* drop the lock and return early due to latency * drop the lock and return early due to latency
* constraints. * constraints.
*/ */
static int select_parent(struct dentry * parent) static int select_parent(struct dentry *parent, struct list_head *dispose)
{ {
struct dentry *this_parent; struct dentry *this_parent;
struct list_head *next; struct list_head *next;
@ -1114,12 +1095,11 @@ static int select_parent(struct dentry * parent)
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
/* /*
* move only zero ref count dentries to the end * move only zero ref count dentries to the dispose list.
* of the unused list for prune_dcache
*/ */
if (!dentry->d_count) { if (!dentry->d_count) {
dentry_lru_move_tail(dentry); dentry_lru_move_list(dentry, dispose);
found++; found++;
} else { } else {
dentry_lru_del(dentry); dentry_lru_del(dentry);
@ -1181,14 +1161,13 @@ static int select_parent(struct dentry * parent)
* *
* Prune the dcache to remove unused children of the parent dentry. * Prune the dcache to remove unused children of the parent dentry.
*/ */
void shrink_dcache_parent(struct dentry * parent) void shrink_dcache_parent(struct dentry * parent)
{ {
struct super_block *sb = parent->d_sb; LIST_HEAD(dispose);
int found; int found;
while ((found = select_parent(parent)) != 0) while ((found = select_parent(parent, &dispose)) != 0)
__shrink_dcache_sb(sb, found, 0); shrink_dentry_list(&dispose);
} }
EXPORT_SYMBOL(shrink_dcache_parent); EXPORT_SYMBOL(shrink_dcache_parent);
@ -1461,6 +1440,23 @@ struct dentry * d_alloc_root(struct inode * root_inode)
} }
EXPORT_SYMBOL(d_alloc_root); EXPORT_SYMBOL(d_alloc_root);
struct dentry *d_make_root(struct inode *root_inode)
{
struct dentry *res = NULL;
if (root_inode) {
static const struct qstr name = { .name = "/", .len = 1 };
res = __d_alloc(root_inode->i_sb, &name);
if (res)
d_instantiate(res, root_inode);
else
iput(root_inode);
}
return res;
}
EXPORT_SYMBOL(d_make_root);
static struct dentry * __d_find_any_alias(struct inode *inode) static struct dentry * __d_find_any_alias(struct inode *inode)
{ {
struct dentry *alias; struct dentry *alias;

View File

@ -3733,10 +3733,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
} }
if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
ext4_msg(sb, KERN_ERR, "corrupt root inode, run e2fsck"); ext4_msg(sb, KERN_ERR, "corrupt root inode, run e2fsck");
iput(root);
goto failed_mount4; goto failed_mount4;
} }
sb->s_root = d_alloc_root(root); sb->s_root = d_alloc_root(root);
if (!sb->s_root) { if (!sb->s_root) {
iput(root);
ext4_msg(sb, KERN_ERR, "get root dentry failed"); ext4_msg(sb, KERN_ERR, "get root dentry failed");
ret = -ENOMEM; ret = -ENOMEM;
goto failed_mount4; goto failed_mount4;
@ -3773,7 +3775,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (err) { if (err) {
ext4_msg(sb, KERN_ERR, "failed to initialize system " ext4_msg(sb, KERN_ERR, "failed to initialize system "
"zone (%d)", err); "zone (%d)", err);
goto failed_mount4; goto failed_mount4a;
} }
ext4_ext_init(sb); ext4_ext_init(sb);
@ -3830,13 +3832,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
failed_mount7: failed_mount7:
ext4_unregister_li_request(sb); ext4_unregister_li_request(sb);
failed_mount6: failed_mount6:
ext4_ext_release(sb);
failed_mount5:
ext4_mb_release(sb); ext4_mb_release(sb);
failed_mount5:
ext4_ext_release(sb);
ext4_release_system_zone(sb); ext4_release_system_zone(sb);
failed_mount4: failed_mount4a:
iput(root); dput(sb->s_root);
sb->s_root = NULL; sb->s_root = NULL;
failed_mount4:
ext4_msg(sb, KERN_ERR, "mount failed"); ext4_msg(sb, KERN_ERR, "mount failed");
destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq); destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq);
failed_mount_wq: failed_mount_wq:

View File

@ -948,8 +948,11 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
/* get the root dentry */ /* get the root dentry */
s->s_root = d_alloc_root(inode); s->s_root = d_alloc_root(inode);
if (!(s->s_root)) if (!(s->s_root)) {
goto out_no_root; iput(inode);
error = -ENOMEM;
goto out_no_inode;
}
kfree(opt.iocharset); kfree(opt.iocharset);

View File

@ -249,6 +249,7 @@ extern int d_invalidate(struct dentry *);
/* only used at mount-time */ /* only used at mount-time */
extern struct dentry * d_alloc_root(struct inode *); extern struct dentry * d_alloc_root(struct inode *);
extern struct dentry * d_make_root(struct inode *);
/* <clickety>-<click> the ramfs-type tree */ /* <clickety>-<click> the ramfs-type tree */
extern void d_genocide(struct dentry *); extern void d_genocide(struct dentry *);