Commit bacdf1d7 authored by Al Viro's avatar Al Viro
Browse files

primitives for maintaining persisitency



* d_make_persistent(dentry, inode) - bump refcount, mark persistent and
make hashed positive.  Return value is a borrowed reference to dentry;
it can be used until something removes persistency (at the very least,
until the parent gets unlocked, but some filesystems may have stronger
exclusion).

* d_make_discardable() - remove persistency mark and drop reference.

d_make_persistent() is similar to combination of d_instantiate(), dget()
and setting flag.  The only difference is that unlike d_instantiate()
it accepts hashed and unhashed negatives alike.  It is always called in
strong locking environment (parent held exclusive, or, in some cases,
dentry coming from d_alloc_name()); if we ever start using it with parent
held only shared and dentry coming from d_alloc_parallel(), we'll need
to copy the in-lookup logics from __d_add().

d_make_discardable() is eqiuvalent to combination of removing flag and
dput(); since flag removal requires ->d_lock, there's no point trying
to avoid taking that for refcount decrement as fast_dput() does.
The slow path of dput() has been taken into a helper and reused in
d_make_discardable() instead.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 8a210cac
Loading
Loading
Loading
Loading
+59 −15
Original line number Diff line number Diff line
@@ -869,6 +869,24 @@ static inline bool fast_dput(struct dentry *dentry)
	return false;
}

static void finish_dput(struct dentry *dentry)
	__releases(dentry->d_lock)
	__releases(RCU)
{
	while (lock_for_kill(dentry)) {
		rcu_read_unlock();
		dentry = __dentry_kill(dentry);
		if (!dentry)
			return;
		if (retain_dentry(dentry, true)) {
			spin_unlock(&dentry->d_lock);
			return;
		}
		rcu_read_lock();
	}
	rcu_read_unlock();
	spin_unlock(&dentry->d_lock);
}

/* 
 * This is dput
@@ -906,21 +924,27 @@ void dput(struct dentry *dentry)
		rcu_read_unlock();
		return;
	}
	while (lock_for_kill(dentry)) {
		rcu_read_unlock();
		dentry = __dentry_kill(dentry);
		if (!dentry)
			return;
		if (retain_dentry(dentry, true)) {
			spin_unlock(&dentry->d_lock);
			return;
	finish_dput(dentry);
}
EXPORT_SYMBOL(dput);

void d_make_discardable(struct dentry *dentry)
{
	spin_lock(&dentry->d_lock);
	/*
	 * By the end of the series we'll add
	 * WARN_ON(!(dentry->d_flags & DCACHE_PERSISTENT);
	 * here, but while object removal is done by a few common helpers,
	 * object creation tends to be open-coded (if nothing else, new inode
	 * needs to be set up), so adding a warning from the very beginning
	 * would make for much messier patch series.
	 */
	dentry->d_flags &= ~DCACHE_PERSISTENT;
	dentry->d_lockref.count--;
	rcu_read_lock();
	finish_dput(dentry);
}
	rcu_read_unlock();
	spin_unlock(&dentry->d_lock);
}
EXPORT_SYMBOL(dput);
EXPORT_SYMBOL(d_make_discardable);

static void to_shrink_list(struct dentry *dentry, struct list_head *list)
__must_hold(&dentry->d_lock)
@@ -1939,7 +1963,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
	unsigned add_flags = d_flags_for_inode(inode);
	WARN_ON(d_in_lookup(dentry));

	spin_lock(&dentry->d_lock);
	/*
	 * The negative counter only tracks dentries on the LRU. Don't dec if
	 * d_lru is on another list.
@@ -1952,7 +1975,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
	__d_set_inode_and_type(dentry, inode, add_flags);
	raw_write_seqcount_end(&dentry->d_seq);
	fsnotify_update_flags(dentry);
	spin_unlock(&dentry->d_lock);
}

/**
@@ -1976,7 +1998,9 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
	if (inode) {
		security_d_instantiate(entry, inode);
		spin_lock(&inode->i_lock);
		spin_lock(&entry->d_lock);
		__d_instantiate(entry, inode);
		spin_unlock(&entry->d_lock);
		spin_unlock(&inode->i_lock);
	}
}
@@ -1995,7 +2019,9 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
	lockdep_annotate_inode_mutex_key(inode);
	security_d_instantiate(entry, inode);
	spin_lock(&inode->i_lock);
	spin_lock(&entry->d_lock);
	__d_instantiate(entry, inode);
	spin_unlock(&entry->d_lock);
	WARN_ON(!(inode->i_state & I_NEW));
	inode->i_state &= ~I_NEW & ~I_CREATING;
	/*
@@ -2754,6 +2780,24 @@ void d_add(struct dentry *entry, struct inode *inode)
}
EXPORT_SYMBOL(d_add);

struct dentry *d_make_persistent(struct dentry *dentry, struct inode *inode)
{
	WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
	WARN_ON(!inode);
	security_d_instantiate(dentry, inode);
	spin_lock(&inode->i_lock);
	spin_lock(&dentry->d_lock);
	__d_instantiate(dentry, inode);
	dentry->d_flags |= DCACHE_PERSISTENT;
	dget_dlock(dentry);
	if (d_unhashed(dentry))
		__d_rehash(dentry);
	spin_unlock(&dentry->d_lock);
	spin_unlock(&inode->i_lock);
	return dentry;
}
EXPORT_SYMBOL(d_make_persistent);

static void swap_names(struct dentry *dentry, struct dentry *target)
{
	if (unlikely(dname_external(target))) {
+2 −0
Original line number Diff line number Diff line
@@ -611,5 +611,7 @@ static inline struct dentry *d_next_sibling(const struct dentry *dentry)
}

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);

#endif	/* __LINUX_DCACHE_H */