summaryrefslogtreecommitdiff
path: root/include/linux/dcache.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux/dcache.h')
-rw-r--r--include/linux/dcache.h56
1 files changed, 45 insertions, 11 deletions
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 898c60d21c92..4b1ff99608e0 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -88,6 +88,7 @@ union shortname_store {
#define d_lock d_lockref.lock
#define d_iname d_shortname.string
+struct completion_list;
struct dentry {
/* RCU lookup touched fields */
@@ -115,20 +116,28 @@ struct dentry {
* possible!
*/
- union {
- struct list_head d_lru; /* LRU list */
- wait_queue_head_t *d_wait; /* in-lookup ones only */
- };
+ struct list_head d_lru; /* LRU list */
struct hlist_node d_sib; /* child of parent list */
struct hlist_head d_children; /* our children */
/*
- * d_alias and d_rcu can share memory
+ * the following members can share memory - their uses are
+ * mutually exclusive.
*/
union {
- struct hlist_node d_alias; /* inode alias list */
- struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
+ /* positives: inode alias list */
+ struct hlist_node d_alias;
+ /* in-lookup ones (all negative, live): hash chain */
+ struct hlist_bl_node d_in_lookup_hash;
+ /* killed ones: (already negative) used to schedule freeing */
struct rcu_head d_rcu;
- } d_u;
+ /*
+ * live non-in-lookup negatives: used if shrink_dcache_tree()
+ * races with eviction by another thread and needs to wait for
+ * this dentry to get killed . Remains NULL for almost all
+ * negative dentries.
+ */
+ struct completion_list *waiters;
+ };
};
/*
@@ -198,6 +207,9 @@ enum dentry_flags {
DCACHE_REFERENCED = BIT(6), /* Recently used, don't discard. */
DCACHE_DONTCACHE = BIT(7), /* Purge from memory on final dput() */
DCACHE_CANT_MOUNT = BIT(8),
+ DCACHE_LOOKUP_WAITERS = BIT(9), /* A thread is waiting for
+ * PAR_LOOKUP to clear
+ */
DCACHE_SHRINK_LIST = BIT(10),
DCACHE_OP_WEAK_REVALIDATE = BIT(11),
/*
@@ -244,8 +256,7 @@ extern void d_delete(struct dentry *);
/* allocate/de-allocate */
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
extern struct dentry * d_alloc_anon(struct super_block *);
-extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
- wait_queue_head_t *);
+extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *);
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
/* weird procfs mess; *NOT* exported */
extern struct dentry * d_splice_alias_ops(struct inode *, struct dentry *,
@@ -264,11 +275,12 @@ extern void d_invalidate(struct dentry *);
extern struct dentry * d_make_root(struct inode *);
extern void d_mark_tmpfile(struct file *, struct inode *);
+int d_mark_tmpfile_name(struct file *file, const struct qstr *name);
extern void d_tmpfile(struct file *, struct inode *);
extern struct dentry *d_find_alias(struct inode *);
extern void d_prune_aliases(struct inode *);
-extern void d_dispose_if_unused(struct dentry *, struct list_head *);
+extern bool __move_to_shrink_list(struct dentry *, struct list_head *);
extern void shrink_dentry_list(struct list_head *);
extern struct dentry *d_find_alias_rcu(struct inode *);
@@ -353,6 +365,24 @@ static inline struct dentry *dget(struct dentry *dentry)
return dentry;
}
+/* dentry->d_inode->i_lock must be held by caller */
+static inline bool dget_alias_ilocked(struct dentry *dentry)
+{
+ if (likely(!(READ_ONCE(dentry->d_flags) & DCACHE_NORCU))) {
+ lockref_get(&dentry->d_lockref);
+ return true;
+ }
+ // NORCU dentries with zero refcount MUST NOT be grabbed
+ spin_lock(&dentry->d_lock);
+ if (dentry->d_lockref.count > 0) {
+ dget_dlock(dentry);
+ spin_unlock(&dentry->d_lock);
+ return true;
+ }
+ spin_unlock(&dentry->d_lock);
+ return false;
+}
+
extern struct dentry *dget_parent(struct dentry *dentry);
/**
@@ -615,4 +645,8 @@ void set_default_d_op(struct super_block *, const struct dentry_operations *);
struct dentry *d_make_persistent(struct dentry *, struct inode *);
void d_make_discardable(struct dentry *dentry);
+/* inode->i_lock must be held over that */
+#define for_each_alias(dentry, inode) \
+ hlist_for_each_entry(dentry, &(inode)->i_dentry, d_alias)
+
#endif /* __LINUX_DCACHE_H */