mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
dcache stuff for this cycle
change of locking rules for __dentry_kill(), regularized refcounting rules in that area, assorted cleanups and removal of weird corner cases (e.g. now ->d_iput() on child is always called before the parent might hit __dentry_kill(), etc.) Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCZZ+sQQAKCRBZ7Krx/gZQ 6ybjAQDM5jiS93IUzfHjCWq0nVBX5YGbDAkZOeqxbmIdQb+2UAEA6elP5r0fBBcA seo3bry4DirQMDaA/Cjh4+8r71YSOQs= =7+Hk -----END PGP SIGNATURE----- Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull dcache updates from Al Viro: "Change of locking rules for __dentry_kill(), regularized refcounting rules in that area, assorted cleanups and removal of weird corner cases (e.g. now ->d_iput() on child is always called before the parent might hit __dentry_kill(), etc)" * tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (40 commits) dcache: remove unnecessary NULL check in dget_dlock() kill DCACHE_MAY_FREE __d_unalias() doesn't use inode argument d_alloc_parallel(): in-lookup hash insertion doesn't need an RCU variant get rid of DCACHE_GENOCIDE d_genocide(): move the extern into fs/internal.h simple_fill_super(): don't bother with d_genocide() on failure nsfs: use d_make_root() d_alloc_pseudo(): move setting ->d_op there from the (sole) caller kill d_instantate_anon(), fold __d_instantiate_anon() into remaining caller retain_dentry(): introduce a trimmed-down lockless variant __dentry_kill(): new locking scheme d_prune_aliases(): use a shrink list switch select_collect{,2}() to use of to_shrink_list() to_shrink_list(): call only if refcount is 0 fold dentry_kill() into dput() don't try to cut corners in shrink_lock_dentry() fold the call of retain_dentry() into fast_dput() Call retain_dentry() with refcount 0 dentry_kill(): don't bother with retain_dentry() on slow path ...
This commit is contained in:
commit
499aa1ca4e
@ -1091,6 +1091,40 @@ would need to do so.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
The list of children anchored in parent dentry got turned into hlist now.
|
||||
Field names got changed (->d_children/->d_sib instead of ->d_subdirs/->d_child
|
||||
for anchor/entries resp.), so any affected places will be immediately caught
|
||||
by compiler.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
->d_delete() instances are now called for dentries with ->d_lock held
|
||||
and refcount equal to 0. They are not permitted to drop/regain ->d_lock.
|
||||
None of in-tree instances did anything of that sort. Make sure yours do not...
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
->d_prune() instances are now called without ->d_lock held on the parent.
|
||||
->d_lock on dentry itself is still held; if you need per-parent exclusions (none
|
||||
of the in-tree instances did), use your own spinlock.
|
||||
|
||||
->d_iput() and ->d_release() are called with victim dentry still in the
|
||||
list of parent's children. It is still unhashed, marked killed, etc., just not
|
||||
removed from parent's ->d_children yet.
|
||||
|
||||
Anyone iterating through the list of children needs to be aware of the
|
||||
half-killed dentries that might be seen there; taking ->d_lock on those will
|
||||
see them negative, unhashed and with negative refcount, which means that most
|
||||
of the in-kernel users would've done the right thing anyway without any adjustment.
|
||||
|
||||
---
|
||||
|
||||
**recommended**
|
||||
|
||||
Block device freezing and thawing have been moved to holder operations.
|
||||
|
@ -145,10 +145,11 @@ spufs_evict_inode(struct inode *inode)
|
||||
|
||||
static void spufs_prune_dir(struct dentry *dir)
|
||||
{
|
||||
struct dentry *dentry, *tmp;
|
||||
struct dentry *dentry;
|
||||
struct hlist_node *n;
|
||||
|
||||
inode_lock(d_inode(dir));
|
||||
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (simple_positive(dentry)) {
|
||||
dget_dlock(dentry);
|
||||
|
@ -373,7 +373,7 @@ error:
|
||||
void afs_dynroot_depopulate(struct super_block *sb)
|
||||
{
|
||||
struct afs_net *net = afs_sb2net(sb);
|
||||
struct dentry *root = sb->s_root, *subdir, *tmp;
|
||||
struct dentry *root = sb->s_root, *subdir;
|
||||
|
||||
/* Prevent more subdirs from being created */
|
||||
mutex_lock(&net->proc_cells_lock);
|
||||
@ -382,10 +382,11 @@ void afs_dynroot_depopulate(struct super_block *sb)
|
||||
mutex_unlock(&net->proc_cells_lock);
|
||||
|
||||
if (root) {
|
||||
struct hlist_node *n;
|
||||
inode_lock(root->d_inode);
|
||||
|
||||
/* Remove all the pins for dirs created for manually added cells */
|
||||
list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_safe(subdir, n, &root->d_children, d_sib) {
|
||||
if (subdir->d_fsdata) {
|
||||
subdir->d_fsdata = NULL;
|
||||
dput(subdir);
|
||||
|
@ -73,12 +73,9 @@ done:
|
||||
/* p->d_lock held */
|
||||
static struct dentry *positive_after(struct dentry *p, struct dentry *child)
|
||||
{
|
||||
if (child)
|
||||
child = list_next_entry(child, d_child);
|
||||
else
|
||||
child = list_first_entry(&p->d_subdirs, struct dentry, d_child);
|
||||
child = child ? d_next_sibling(child) : d_first_child(p);
|
||||
|
||||
list_for_each_entry_from(child, &p->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_from(child, d_sib) {
|
||||
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(child)) {
|
||||
dget_dlock(child);
|
||||
|
@ -174,7 +174,7 @@ __dcache_find_get_entry(struct dentry *parent, u64 idx,
|
||||
/*
|
||||
* When possible, we try to satisfy a readdir by peeking at the
|
||||
* dcache. We make this work by carefully ordering dentries on
|
||||
* d_child when we initially get results back from the MDS, and
|
||||
* d_children when we initially get results back from the MDS, and
|
||||
* falling back to a "normal" sync readdir if any dentries in the dir
|
||||
* are dropped.
|
||||
*
|
||||
|
@ -2128,7 +2128,7 @@ static bool drop_negative_children(struct dentry *dentry)
|
||||
goto out;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &dentry->d_children, d_sib) {
|
||||
if (d_really_is_positive(child)) {
|
||||
all_negative = false;
|
||||
break;
|
||||
|
@ -93,13 +93,13 @@ static void coda_flag_children(struct dentry *parent, int flag)
|
||||
struct dentry *de;
|
||||
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each_entry(de, &parent->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(de, &parent->d_children, d_sib) {
|
||||
struct inode *inode = d_inode_rcu(de);
|
||||
/* don't know what to do with negative dentries */
|
||||
if (d_inode(de) )
|
||||
coda_flag_inode(d_inode(de), flag);
|
||||
if (inode)
|
||||
coda_flag_inode(inode, flag);
|
||||
}
|
||||
spin_unlock(&parent->d_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
void coda_flag_inode_children(struct inode *inode, int flag)
|
||||
|
652
fs/dcache.c
652
fs/dcache.c
File diff suppressed because it is too large
Load Diff
@ -316,9 +316,6 @@ struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
|
||||
const char *name, int flags,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
static const struct dentry_operations anon_ops = {
|
||||
.d_dname = simple_dname
|
||||
};
|
||||
struct qstr this = QSTR_INIT(name, strlen(name));
|
||||
struct path path;
|
||||
struct file *file;
|
||||
@ -326,8 +323,6 @@ struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
|
||||
path.dentry = d_alloc_pseudo(mnt->mnt_sb, &this);
|
||||
if (!path.dentry)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!mnt->mnt_sb->s_d_op)
|
||||
d_set_d_op(path.dentry, &anon_ops);
|
||||
path.mnt = mntget(mnt);
|
||||
d_instantiate(path.dentry, inode);
|
||||
file = alloc_file(&path, flags, fops);
|
||||
|
@ -214,6 +214,11 @@ extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *)
|
||||
extern char *simple_dname(struct dentry *, char *, int);
|
||||
extern void dput_to_list(struct dentry *, struct list_head *);
|
||||
extern void shrink_dentry_list(struct list_head *);
|
||||
extern void shrink_dcache_for_umount(struct super_block *);
|
||||
extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *);
|
||||
extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
const struct qstr *name, unsigned *seq);
|
||||
extern void d_genocide(struct dentry *);
|
||||
|
||||
/*
|
||||
* pipe.c
|
||||
|
62
fs/libfs.c
62
fs/libfs.c
@ -104,15 +104,16 @@ EXPORT_SYMBOL(dcache_dir_close);
|
||||
* If no such element exists, NULL is returned.
|
||||
*/
|
||||
static struct dentry *scan_positives(struct dentry *cursor,
|
||||
struct list_head *p,
|
||||
struct hlist_node **p,
|
||||
loff_t count,
|
||||
struct dentry *last)
|
||||
{
|
||||
struct dentry *dentry = cursor->d_parent, *found = NULL;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
while ((p = p->next) != &dentry->d_subdirs) {
|
||||
struct dentry *d = list_entry(p, struct dentry, d_child);
|
||||
while (*p) {
|
||||
struct dentry *d = hlist_entry(*p, struct dentry, d_sib);
|
||||
p = &d->d_sib.next;
|
||||
// we must at least skip cursors, to avoid livelocks
|
||||
if (d->d_flags & DCACHE_DENTRY_CURSOR)
|
||||
continue;
|
||||
@ -126,8 +127,10 @@ static struct dentry *scan_positives(struct dentry *cursor,
|
||||
count = 1;
|
||||
}
|
||||
if (need_resched()) {
|
||||
list_move(&cursor->d_child, p);
|
||||
p = &cursor->d_child;
|
||||
if (!hlist_unhashed(&cursor->d_sib))
|
||||
__hlist_del(&cursor->d_sib);
|
||||
hlist_add_behind(&cursor->d_sib, &d->d_sib);
|
||||
p = &cursor->d_sib.next;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
cond_resched();
|
||||
spin_lock(&dentry->d_lock);
|
||||
@ -159,13 +162,12 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
|
||||
inode_lock_shared(dentry->d_inode);
|
||||
|
||||
if (offset > 2)
|
||||
to = scan_positives(cursor, &dentry->d_subdirs,
|
||||
to = scan_positives(cursor, &dentry->d_children.first,
|
||||
offset - 2, NULL);
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_del_init(&cursor->d_sib);
|
||||
if (to)
|
||||
list_move(&cursor->d_child, &to->d_child);
|
||||
else
|
||||
list_del_init(&cursor->d_child);
|
||||
hlist_add_behind(&cursor->d_sib, &to->d_sib);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(to);
|
||||
|
||||
@ -187,19 +189,16 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct dentry *cursor = file->private_data;
|
||||
struct list_head *anchor = &dentry->d_subdirs;
|
||||
struct dentry *next = NULL;
|
||||
struct list_head *p;
|
||||
struct hlist_node **p;
|
||||
|
||||
if (!dir_emit_dots(file, ctx))
|
||||
return 0;
|
||||
|
||||
if (ctx->pos == 2)
|
||||
p = anchor;
|
||||
else if (!list_empty(&cursor->d_child))
|
||||
p = &cursor->d_child;
|
||||
p = &dentry->d_children.first;
|
||||
else
|
||||
return 0;
|
||||
p = &cursor->d_sib.next;
|
||||
|
||||
while ((next = scan_positives(cursor, p, 1, next)) != NULL) {
|
||||
if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
|
||||
@ -207,13 +206,12 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
|
||||
fs_umode_to_dtype(d_inode(next)->i_mode)))
|
||||
break;
|
||||
ctx->pos++;
|
||||
p = &next->d_child;
|
||||
p = &next->d_sib.next;
|
||||
}
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_del_init(&cursor->d_sib);
|
||||
if (next)
|
||||
list_move_tail(&cursor->d_child, &next->d_child);
|
||||
else
|
||||
list_del_init(&cursor->d_child);
|
||||
hlist_add_before(&cursor->d_sib, &next->d_sib);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(next);
|
||||
|
||||
@ -500,12 +498,11 @@ const struct file_operations simple_offset_dir_operations = {
|
||||
|
||||
static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
|
||||
{
|
||||
struct dentry *child = NULL;
|
||||
struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs;
|
||||
struct dentry *child = NULL, *d;
|
||||
|
||||
spin_lock(&parent->d_lock);
|
||||
while ((p = p->next) != &parent->d_subdirs) {
|
||||
struct dentry *d = container_of(p, struct dentry, d_child);
|
||||
d = prev ? d_next_sibling(prev) : d_first_child(parent);
|
||||
hlist_for_each_entry_from(d, d_sib) {
|
||||
if (simple_positive(d)) {
|
||||
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(d))
|
||||
@ -666,7 +663,7 @@ int simple_empty(struct dentry *dentry)
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &dentry->d_children, d_sib) {
|
||||
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(child)) {
|
||||
spin_unlock(&child->d_lock);
|
||||
@ -920,7 +917,6 @@ int simple_fill_super(struct super_block *s, unsigned long magic,
|
||||
const struct tree_descr *files)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct dentry *root;
|
||||
struct dentry *dentry;
|
||||
int i;
|
||||
|
||||
@ -943,8 +939,8 @@ int simple_fill_super(struct super_block *s, unsigned long magic,
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
set_nlink(inode, 2);
|
||||
root = d_make_root(inode);
|
||||
if (!root)
|
||||
s->s_root = d_make_root(inode);
|
||||
if (!s->s_root)
|
||||
return -ENOMEM;
|
||||
for (i = 0; !files->name || files->name[0]; i++, files++) {
|
||||
if (!files->name)
|
||||
@ -956,13 +952,13 @@ int simple_fill_super(struct super_block *s, unsigned long magic,
|
||||
"with an index of 1!\n", __func__,
|
||||
s->s_type->name);
|
||||
|
||||
dentry = d_alloc_name(root, files->name);
|
||||
dentry = d_alloc_name(s->s_root, files->name);
|
||||
if (!dentry)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
inode = new_inode(s);
|
||||
if (!inode) {
|
||||
dput(dentry);
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
}
|
||||
inode->i_mode = S_IFREG | files->mode;
|
||||
simple_inode_init_ts(inode);
|
||||
@ -970,13 +966,7 @@ int simple_fill_super(struct super_block *s, unsigned long magic,
|
||||
inode->i_ino = i;
|
||||
d_add(dentry, inode);
|
||||
}
|
||||
s->s_root = root;
|
||||
return 0;
|
||||
out:
|
||||
d_genocide(root);
|
||||
shrink_dcache_parent(root);
|
||||
dput(root);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(simple_fill_super);
|
||||
|
||||
|
@ -1242,63 +1242,34 @@ static inline void _nfsd_symlink(struct dentry *parent, const char *name,
|
||||
|
||||
#endif
|
||||
|
||||
static void clear_ncl(struct inode *inode)
|
||||
static void clear_ncl(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct nfsdfs_client *ncl = inode->i_private;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
inode->i_private = NULL;
|
||||
spin_unlock(&inode->i_lock);
|
||||
kref_put(&ncl->cl_ref, ncl->cl_release);
|
||||
}
|
||||
|
||||
static struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode)
|
||||
{
|
||||
struct nfsdfs_client *nc = inode->i_private;
|
||||
|
||||
if (nc)
|
||||
kref_get(&nc->cl_ref);
|
||||
return nc;
|
||||
}
|
||||
|
||||
struct nfsdfs_client *get_nfsdfs_client(struct inode *inode)
|
||||
{
|
||||
struct nfsdfs_client *nc;
|
||||
|
||||
inode_lock_shared(inode);
|
||||
nc = __get_nfsdfs_client(inode);
|
||||
inode_unlock_shared(inode);
|
||||
spin_lock(&inode->i_lock);
|
||||
nc = inode->i_private;
|
||||
if (nc)
|
||||
kref_get(&nc->cl_ref);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return nc;
|
||||
}
|
||||
/* from __rpc_unlink */
|
||||
static void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int ret;
|
||||
|
||||
clear_ncl(d_inode(dentry));
|
||||
dget(dentry);
|
||||
ret = simple_unlink(dir, dentry);
|
||||
d_drop(dentry);
|
||||
fsnotify_unlink(dir, dentry);
|
||||
dput(dentry);
|
||||
WARN_ON_ONCE(ret);
|
||||
}
|
||||
|
||||
static void nfsdfs_remove_files(struct dentry *root)
|
||||
{
|
||||
struct dentry *dentry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) {
|
||||
if (!simple_positive(dentry)) {
|
||||
WARN_ON_ONCE(1); /* I think this can't happen? */
|
||||
continue;
|
||||
}
|
||||
nfsdfs_remove_file(d_inode(root), dentry);
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: cut'n'paste from simple_fill_super; figure out if we could share
|
||||
* code instead. */
|
||||
static int nfsdfs_create_files(struct dentry *root,
|
||||
const struct tree_descr *files,
|
||||
struct nfsdfs_client *ncl,
|
||||
struct dentry **fdentries)
|
||||
{
|
||||
struct inode *dir = d_inode(root);
|
||||
@ -1317,8 +1288,9 @@ static int nfsdfs_create_files(struct dentry *root,
|
||||
dput(dentry);
|
||||
goto out;
|
||||
}
|
||||
kref_get(&ncl->cl_ref);
|
||||
inode->i_fop = files->ops;
|
||||
inode->i_private = __get_nfsdfs_client(dir);
|
||||
inode->i_private = ncl;
|
||||
d_add(dentry, inode);
|
||||
fsnotify_create(dir, dentry);
|
||||
if (fdentries)
|
||||
@ -1327,7 +1299,6 @@ static int nfsdfs_create_files(struct dentry *root,
|
||||
inode_unlock(dir);
|
||||
return 0;
|
||||
out:
|
||||
nfsdfs_remove_files(root);
|
||||
inode_unlock(dir);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -1347,7 +1318,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
||||
dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name);
|
||||
if (IS_ERR(dentry)) /* XXX: tossing errors? */
|
||||
return NULL;
|
||||
ret = nfsdfs_create_files(dentry, files, fdentries);
|
||||
ret = nfsdfs_create_files(dentry, files, ncl, fdentries);
|
||||
if (ret) {
|
||||
nfsd_client_rmdir(dentry);
|
||||
return NULL;
|
||||
@ -1358,20 +1329,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
||||
/* Taken from __rpc_rmdir: */
|
||||
void nfsd_client_rmdir(struct dentry *dentry)
|
||||
{
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
int ret;
|
||||
|
||||
inode_lock(dir);
|
||||
nfsdfs_remove_files(dentry);
|
||||
clear_ncl(inode);
|
||||
dget(dentry);
|
||||
ret = simple_rmdir(dir, dentry);
|
||||
WARN_ON_ONCE(ret);
|
||||
d_drop(dentry);
|
||||
fsnotify_rmdir(dir, dentry);
|
||||
dput(dentry);
|
||||
inode_unlock(dir);
|
||||
simple_recursive_removal(dentry, clear_ncl);
|
||||
}
|
||||
|
||||
static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
@ -124,7 +124,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
|
||||
* d_flags to indicate parental interest (their parent is the
|
||||
* original inode) */
|
||||
spin_lock(&alias->d_lock);
|
||||
list_for_each_entry(child, &alias->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &alias->d_children, d_sib) {
|
||||
if (!child->d_inode)
|
||||
continue;
|
||||
|
||||
|
@ -90,12 +90,9 @@ slow:
|
||||
inode->i_fop = &ns_file_operations;
|
||||
inode->i_private = ns;
|
||||
|
||||
dentry = d_alloc_anon(mnt->mnt_sb);
|
||||
if (!dentry) {
|
||||
iput(inode);
|
||||
dentry = d_make_root(inode); /* not the normal use, but... */
|
||||
if (!dentry)
|
||||
return -ENOMEM;
|
||||
}
|
||||
d_instantiate(dentry, inode);
|
||||
dentry->d_fsdata = (void *)ns->ops;
|
||||
d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
|
||||
if (d) {
|
||||
|
@ -289,7 +289,6 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
|
||||
{
|
||||
struct dentry *lower = lowerpath ? lowerpath->dentry : NULL;
|
||||
struct dentry *upper = upper_alias ?: index;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode = NULL;
|
||||
struct ovl_entry *oe;
|
||||
struct ovl_inode_params oip = {
|
||||
@ -320,27 +319,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
|
||||
if (upper)
|
||||
ovl_set_flag(OVL_UPPERDATA, inode);
|
||||
|
||||
dentry = d_find_any_alias(inode);
|
||||
if (dentry)
|
||||
goto out_iput;
|
||||
|
||||
dentry = d_alloc_anon(inode->i_sb);
|
||||
if (unlikely(!dentry))
|
||||
goto nomem;
|
||||
|
||||
if (upper_alias)
|
||||
ovl_dentry_set_upper_alias(dentry);
|
||||
|
||||
ovl_dentry_init_reval(dentry, upper, OVL_I_E(inode));
|
||||
|
||||
return d_instantiate_anon(dentry, inode);
|
||||
|
||||
nomem:
|
||||
dput(dentry);
|
||||
dentry = ERR_PTR(-ENOMEM);
|
||||
out_iput:
|
||||
iput(inode);
|
||||
return dentry;
|
||||
return d_obtain_alias(inode);
|
||||
}
|
||||
|
||||
/* Get the upper or lower dentry in stack whose on layer @idx */
|
||||
|
@ -199,21 +199,17 @@ static void change_gid(struct dentry *dentry, kgid_t gid)
|
||||
*/
|
||||
static void set_gid(struct dentry *parent, kgid_t gid)
|
||||
{
|
||||
struct dentry *this_parent;
|
||||
struct list_head *next;
|
||||
struct dentry *this_parent, *dentry;
|
||||
|
||||
this_parent = parent;
|
||||
spin_lock(&this_parent->d_lock);
|
||||
|
||||
change_gid(this_parent, gid);
|
||||
repeat:
|
||||
next = this_parent->d_subdirs.next;
|
||||
dentry = d_first_child(this_parent);
|
||||
resume:
|
||||
while (next != &this_parent->d_subdirs) {
|
||||
hlist_for_each_entry_from(dentry, d_sib) {
|
||||
struct tracefs_inode *ti;
|
||||
struct list_head *tmp = next;
|
||||
struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
|
||||
next = tmp->next;
|
||||
|
||||
/* Note, getdents() can add a cursor dentry with no inode */
|
||||
if (!dentry->d_inode)
|
||||
@ -228,7 +224,7 @@ resume:
|
||||
if (ti && (ti->flags & TRACEFS_EVENT_INODE))
|
||||
eventfs_update_gid(dentry, gid);
|
||||
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
if (!hlist_empty(&dentry->d_children)) {
|
||||
spin_unlock(&this_parent->d_lock);
|
||||
spin_release(&dentry->d_lock.dep_map, _RET_IP_);
|
||||
this_parent = dentry;
|
||||
@ -243,21 +239,20 @@ resume:
|
||||
rcu_read_lock();
|
||||
ascend:
|
||||
if (this_parent != parent) {
|
||||
struct dentry *child = this_parent;
|
||||
this_parent = child->d_parent;
|
||||
dentry = this_parent;
|
||||
this_parent = dentry->d_parent;
|
||||
|
||||
spin_unlock(&child->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&this_parent->d_lock);
|
||||
|
||||
/* go into the first sibling still alive */
|
||||
do {
|
||||
next = child->d_child.next;
|
||||
if (next == &this_parent->d_subdirs)
|
||||
goto ascend;
|
||||
child = list_entry(next, struct dentry, d_child);
|
||||
} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
hlist_for_each_entry_continue(dentry, d_sib) {
|
||||
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
}
|
||||
}
|
||||
goto ascend;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock(&this_parent->d_lock);
|
||||
|
@ -68,12 +68,12 @@ extern const struct qstr dotdot_name;
|
||||
* large memory footprint increase).
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
# define DNAME_INLINE_LEN 32 /* 192 bytes */
|
||||
# define DNAME_INLINE_LEN 40 /* 192 bytes */
|
||||
#else
|
||||
# ifdef CONFIG_SMP
|
||||
# define DNAME_INLINE_LEN 36 /* 128 bytes */
|
||||
# else
|
||||
# define DNAME_INLINE_LEN 40 /* 128 bytes */
|
||||
# else
|
||||
# define DNAME_INLINE_LEN 44 /* 128 bytes */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -101,8 +101,8 @@ struct dentry {
|
||||
struct list_head d_lru; /* LRU list */
|
||||
wait_queue_head_t *d_wait; /* in-lookup ones only */
|
||||
};
|
||||
struct list_head d_child; /* child of parent list */
|
||||
struct list_head d_subdirs; /* our children */
|
||||
struct hlist_node d_sib; /* child of parent list */
|
||||
struct hlist_head d_children; /* our children */
|
||||
/*
|
||||
* d_alias and d_rcu can share memory
|
||||
*/
|
||||
@ -111,7 +111,7 @@ struct dentry {
|
||||
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
|
||||
struct rcu_head d_rcu;
|
||||
} d_u;
|
||||
} __randomize_layout;
|
||||
};
|
||||
|
||||
/*
|
||||
* dentry->d_lock spinlock nesting subclasses:
|
||||
@ -151,13 +151,13 @@ struct dentry_operations {
|
||||
*/
|
||||
|
||||
/* d_flags entries */
|
||||
#define DCACHE_OP_HASH 0x00000001
|
||||
#define DCACHE_OP_COMPARE 0x00000002
|
||||
#define DCACHE_OP_REVALIDATE 0x00000004
|
||||
#define DCACHE_OP_DELETE 0x00000008
|
||||
#define DCACHE_OP_PRUNE 0x00000010
|
||||
#define DCACHE_OP_HASH BIT(0)
|
||||
#define DCACHE_OP_COMPARE BIT(1)
|
||||
#define DCACHE_OP_REVALIDATE BIT(2)
|
||||
#define DCACHE_OP_DELETE BIT(3)
|
||||
#define DCACHE_OP_PRUNE BIT(4)
|
||||
|
||||
#define DCACHE_DISCONNECTED 0x00000020
|
||||
#define DCACHE_DISCONNECTED BIT(5)
|
||||
/* This dentry is possibly not currently connected to the dcache tree, in
|
||||
* which case its parent will either be itself, or will have this flag as
|
||||
* well. nfsd will not use a dentry with this bit set, but will first
|
||||
@ -168,50 +168,46 @@ struct dentry_operations {
|
||||
* dentry into place and return that dentry rather than the passed one,
|
||||
* typically using d_splice_alias. */
|
||||
|
||||
#define DCACHE_REFERENCED 0x00000040 /* Recently used, don't discard. */
|
||||
#define DCACHE_REFERENCED BIT(6) /* Recently used, don't discard. */
|
||||
|
||||
#define DCACHE_DONTCACHE 0x00000080 /* Purge from memory on final dput() */
|
||||
#define DCACHE_DONTCACHE BIT(7) /* Purge from memory on final dput() */
|
||||
|
||||
#define DCACHE_CANT_MOUNT 0x00000100
|
||||
#define DCACHE_GENOCIDE 0x00000200
|
||||
#define DCACHE_SHRINK_LIST 0x00000400
|
||||
#define DCACHE_CANT_MOUNT BIT(8)
|
||||
#define DCACHE_SHRINK_LIST BIT(10)
|
||||
|
||||
#define DCACHE_OP_WEAK_REVALIDATE 0x00000800
|
||||
#define DCACHE_OP_WEAK_REVALIDATE BIT(11)
|
||||
|
||||
#define DCACHE_NFSFS_RENAMED 0x00001000
|
||||
#define DCACHE_NFSFS_RENAMED BIT(12)
|
||||
/* this dentry has been "silly renamed" and has to be deleted on the last
|
||||
* dput() */
|
||||
#define DCACHE_COOKIE 0x00002000 /* For use by dcookie subsystem */
|
||||
#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x00004000
|
||||
#define DCACHE_FSNOTIFY_PARENT_WATCHED BIT(14)
|
||||
/* Parent inode is watched by some fsnotify listener */
|
||||
|
||||
#define DCACHE_DENTRY_KILLED 0x00008000
|
||||
#define DCACHE_DENTRY_KILLED BIT(15)
|
||||
|
||||
#define DCACHE_MOUNTED 0x00010000 /* is a mountpoint */
|
||||
#define DCACHE_NEED_AUTOMOUNT 0x00020000 /* handle automount on this dir */
|
||||
#define DCACHE_MANAGE_TRANSIT 0x00040000 /* manage transit from this dirent */
|
||||
#define DCACHE_MOUNTED BIT(16) /* is a mountpoint */
|
||||
#define DCACHE_NEED_AUTOMOUNT BIT(17) /* handle automount on this dir */
|
||||
#define DCACHE_MANAGE_TRANSIT BIT(18) /* manage transit from this dirent */
|
||||
#define DCACHE_MANAGED_DENTRY \
|
||||
(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
|
||||
|
||||
#define DCACHE_LRU_LIST 0x00080000
|
||||
#define DCACHE_LRU_LIST BIT(19)
|
||||
|
||||
#define DCACHE_ENTRY_TYPE 0x00700000
|
||||
#define DCACHE_MISS_TYPE 0x00000000 /* Negative dentry (maybe fallthru to nowhere) */
|
||||
#define DCACHE_WHITEOUT_TYPE 0x00100000 /* Whiteout dentry (stop pathwalk) */
|
||||
#define DCACHE_DIRECTORY_TYPE 0x00200000 /* Normal directory */
|
||||
#define DCACHE_AUTODIR_TYPE 0x00300000 /* Lookupless directory (presumed automount) */
|
||||
#define DCACHE_REGULAR_TYPE 0x00400000 /* Regular file type (or fallthru to such) */
|
||||
#define DCACHE_SPECIAL_TYPE 0x00500000 /* Other file type (or fallthru to such) */
|
||||
#define DCACHE_SYMLINK_TYPE 0x00600000 /* Symlink (or fallthru to such) */
|
||||
#define DCACHE_ENTRY_TYPE (7 << 20) /* bits 20..22 are for storing type: */
|
||||
#define DCACHE_MISS_TYPE (0 << 20) /* Negative dentry */
|
||||
#define DCACHE_WHITEOUT_TYPE (1 << 20) /* Whiteout dentry (stop pathwalk) */
|
||||
#define DCACHE_DIRECTORY_TYPE (2 << 20) /* Normal directory */
|
||||
#define DCACHE_AUTODIR_TYPE (3 << 20) /* Lookupless directory (presumed automount) */
|
||||
#define DCACHE_REGULAR_TYPE (4 << 20) /* Regular file type */
|
||||
#define DCACHE_SPECIAL_TYPE (5 << 20) /* Other file type */
|
||||
#define DCACHE_SYMLINK_TYPE (6 << 20) /* Symlink */
|
||||
|
||||
#define DCACHE_MAY_FREE 0x00800000
|
||||
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
|
||||
#define DCACHE_NOKEY_NAME 0x02000000 /* Encrypted name encoded without key */
|
||||
#define DCACHE_OP_REAL 0x04000000
|
||||
#define DCACHE_NOKEY_NAME BIT(25) /* Encrypted name encoded without key */
|
||||
#define DCACHE_OP_REAL BIT(26)
|
||||
|
||||
#define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */
|
||||
#define DCACHE_DENTRY_CURSOR 0x20000000
|
||||
#define DCACHE_NORCU 0x40000000 /* No RCU delay for freeing */
|
||||
#define DCACHE_PAR_LOOKUP BIT(28) /* being looked up (with parent locked shared) */
|
||||
#define DCACHE_DENTRY_CURSOR BIT(29)
|
||||
#define DCACHE_NORCU BIT(30) /* No RCU delay for freeing */
|
||||
|
||||
extern seqlock_t rename_lock;
|
||||
|
||||
@ -220,8 +216,6 @@ extern seqlock_t rename_lock;
|
||||
*/
|
||||
extern void d_instantiate(struct dentry *, struct inode *);
|
||||
extern void d_instantiate_new(struct dentry *, struct inode *);
|
||||
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
|
||||
extern struct dentry * d_instantiate_anon(struct dentry *, struct inode *);
|
||||
extern void __d_drop(struct dentry *dentry);
|
||||
extern void d_drop(struct dentry *dentry);
|
||||
extern void d_delete(struct dentry *);
|
||||
@ -242,15 +236,11 @@ extern struct dentry * d_obtain_alias(struct inode *);
|
||||
extern struct dentry * d_obtain_root(struct inode *);
|
||||
extern void shrink_dcache_sb(struct super_block *);
|
||||
extern void shrink_dcache_parent(struct dentry *);
|
||||
extern void shrink_dcache_for_umount(struct super_block *);
|
||||
extern void d_invalidate(struct dentry *);
|
||||
|
||||
/* only used at mount-time */
|
||||
extern struct dentry * d_make_root(struct inode *);
|
||||
|
||||
/* <clickety>-<click> the ramfs-type tree */
|
||||
extern void d_genocide(struct dentry *);
|
||||
|
||||
extern void d_mark_tmpfile(struct file *, struct inode *);
|
||||
extern void d_tmpfile(struct file *, struct inode *);
|
||||
|
||||
@ -274,12 +264,8 @@ extern void d_move(struct dentry *, struct dentry *);
|
||||
extern void d_exchange(struct dentry *, struct dentry *);
|
||||
extern struct dentry *d_ancestor(struct dentry *, struct dentry *);
|
||||
|
||||
/* appendix may either be NULL or be used for transname suffixes */
|
||||
extern struct dentry *d_lookup(const struct dentry *, const struct qstr *);
|
||||
extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
|
||||
extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *);
|
||||
extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
const struct qstr *name, unsigned *seq);
|
||||
|
||||
static inline unsigned d_count(const struct dentry *dentry)
|
||||
{
|
||||
@ -301,20 +287,40 @@ extern char *dentry_path(const struct dentry *, char *, int);
|
||||
/* Allocation counts.. */
|
||||
|
||||
/**
|
||||
* dget, dget_dlock - get a reference to a dentry
|
||||
* @dentry: dentry to get a reference to
|
||||
* dget_dlock - get a reference to a dentry
|
||||
* @dentry: dentry to get a reference to
|
||||
*
|
||||
* Given a dentry or %NULL pointer increment the reference count
|
||||
* if appropriate and return the dentry. A dentry will not be
|
||||
* destroyed when it has references.
|
||||
* Given a live dentry, increment the reference count and return the dentry.
|
||||
* Caller must hold @dentry->d_lock. Making sure that dentry is alive is
|
||||
* caller's resonsibility. There are many conditions sufficient to guarantee
|
||||
* that; e.g. anything with non-negative refcount is alive, so's anything
|
||||
* hashed, anything positive, anyone's parent, etc.
|
||||
*/
|
||||
static inline struct dentry *dget_dlock(struct dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
dentry->d_lockref.count++;
|
||||
dentry->d_lockref.count++;
|
||||
return dentry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dget - get a reference to a dentry
|
||||
* @dentry: dentry to get a reference to
|
||||
*
|
||||
* Given a dentry or %NULL pointer increment the reference count
|
||||
* if appropriate and return the dentry. A dentry will not be
|
||||
* destroyed when it has references. Conversely, a dentry with
|
||||
* no references can disappear for any number of reasons, starting
|
||||
* with memory pressure. In other words, that primitive is
|
||||
* used to clone an existing reference; using it on something with
|
||||
* zero refcount is a bug.
|
||||
*
|
||||
* NOTE: it will spin if @dentry->d_lock is held. From the deadlock
|
||||
* avoidance point of view it is equivalent to spin_lock()/increment
|
||||
* refcount/spin_unlock(), so calling it under @dentry->d_lock is
|
||||
* always a bug; so's calling it under ->d_lock on any of its descendents.
|
||||
*
|
||||
*/
|
||||
static inline struct dentry *dget(struct dentry *dentry)
|
||||
{
|
||||
if (dentry)
|
||||
@ -325,12 +331,11 @@ static inline struct dentry *dget(struct dentry *dentry)
|
||||
extern struct dentry *dget_parent(struct dentry *dentry);
|
||||
|
||||
/**
|
||||
* d_unhashed - is dentry hashed
|
||||
* @dentry: entry to check
|
||||
* d_unhashed - is dentry hashed
|
||||
* @dentry: entry to check
|
||||
*
|
||||
* Returns true if the dentry passed is not currently hashed.
|
||||
* Returns true if the dentry passed is not currently hashed.
|
||||
*/
|
||||
|
||||
static inline int d_unhashed(const struct dentry *dentry)
|
||||
{
|
||||
return hlist_bl_unhashed(&dentry->d_hash);
|
||||
@ -490,14 +495,6 @@ static inline int simple_positive(const struct dentry *dentry)
|
||||
return d_really_is_positive(dentry) && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
extern void d_set_fallthru(struct dentry *dentry);
|
||||
|
||||
static inline bool d_is_fallthru(const struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_flags & DCACHE_FALLTHRU;
|
||||
}
|
||||
|
||||
|
||||
extern int sysctl_vfs_cache_pressure;
|
||||
|
||||
static inline unsigned long vfs_pressure_ratio(unsigned long val)
|
||||
@ -546,21 +543,6 @@ static inline struct inode *d_backing_inode(const struct dentry *upper)
|
||||
return inode;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_backing_dentry - Get upper or lower dentry we should be using
|
||||
* @upper: The upper layer
|
||||
*
|
||||
* This is the helper that should be used to get the dentry of the inode that
|
||||
* will be used if this dentry were opened as a file. It may be the upper
|
||||
* dentry or it may be a lower dentry pinned by the upper.
|
||||
*
|
||||
* Normal filesystems should not use this to access their own dentries.
|
||||
*/
|
||||
static inline struct dentry *d_backing_dentry(struct dentry *upper)
|
||||
{
|
||||
return upper;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_real - Return the real dentry
|
||||
* @dentry: the dentry to query
|
||||
@ -600,4 +582,14 @@ struct name_snapshot {
|
||||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||
|
||||
static inline struct dentry *d_first_child(const struct dentry *dentry)
|
||||
{
|
||||
return hlist_entry_safe(dentry->d_children.first, struct dentry, d_sib);
|
||||
}
|
||||
|
||||
static inline struct dentry *d_next_sibling(const struct dentry *dentry)
|
||||
{
|
||||
return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_DCACHE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user