Re: [RFC] possible badness in prune_dcache()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Apr 04, 2008 at 11:29:28AM -0400, Josef Bacik wrote:
> On Fri, Apr 04, 2008 at 06:28:46PM +0300, Alex Lyashkov wrote:
> > Thanks for answer,
> > 
> > Yes this commit - fix issue with umount, but not resolve main question:
> > why prune_dcache kill dentry with diffrent super block? this produce
> > long loop with skip loop instead of early exit and move dentry from
> > select_parent again to end of list.
> >
> 
> probably worth looking at doing something different in the case of shrinking the
> dcache on the parent, and leaving prune_dcache to only be called in the case of
> trying to free up dcache under memory pressure, where the superblock doesn't
> actually matter.  For the RHEL3 issue you are reffering to I fixed it by
> creating a private list when we shrunk the parent, and submitting that list to
> prune_dcache that way we didn't spend all this time looping.  I will see what
> can be done for upstream.
>

If you would like to test the current upstream I have a patch that does what I
described above.  I only compile tested it, so who knows what it will do :).

Signed-off-by: Josef Bacik <jbacik@xxxxxxxxxx>

 
diff --git a/fs/dcache.c b/fs/dcache.c
index 4345577..564df98 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -413,8 +413,8 @@ static void prune_one_dentry(struct dentry * dentry)
 /**
  * prune_dcache - shrink the dcache
  * @count: number of entries to try and free
- * @sb: if given, ignore dentries for other superblocks
- *         which are being unmounted.
+ * @dispose_list: if given, remove dentries from this
+ *		  list instead of from the dentry_unused list.
  *
  * Shrink the dcache. This is done when we need
  * more memory, or simply when we need to unmount
@@ -425,8 +425,15 @@ static void prune_one_dentry(struct dentry * dentry)
  * all the dentries are in use.
  */
  
-static void prune_dcache(int count, struct super_block *sb)
+static void prune_dcache(int count, struct list_head *dispose_list)
 {
+	struct list_head *list;
+
+	if (dispose_list)
+		list = dispose_list;
+	else
+		list = &dentry_unused;
+
 	spin_lock(&dcache_lock);
 	for (; count ; count--) {
 		struct dentry *dentry;
@@ -435,23 +442,11 @@ static void prune_dcache(int count, struct super_block *sb)
 
 		cond_resched_lock(&dcache_lock);
 
-		tmp = dentry_unused.prev;
-		if (sb) {
-			/* Try to find a dentry for this sb, but don't try
-			 * too hard, if they aren't near the tail they will
-			 * be moved down again soon
-			 */
-			int skip = count;
-			while (skip && tmp != &dentry_unused &&
-			    list_entry(tmp, struct dentry, d_lru)->d_sb != sb) {
-				skip--;
-				tmp = tmp->prev;
-			}
-		}
-		if (tmp == &dentry_unused)
+		tmp = list->prev;
+		if (tmp == list)
 			break;
 		list_del_init(tmp);
-		prefetch(dentry_unused.prev);
+		prefetch(list->prev);
  		dentry_stat.nr_unused--;
 		dentry = list_entry(tmp, struct dentry, d_lru);
 
@@ -468,26 +463,16 @@ static void prune_dcache(int count, struct super_block *sb)
 		/* If the dentry was recently referenced, don't free it. */
 		if (dentry->d_flags & DCACHE_REFERENCED) {
 			dentry->d_flags &= ~DCACHE_REFERENCED;
- 			list_add(&dentry->d_lru, &dentry_unused);
+ 			list_add(&dentry->d_lru, list);
  			dentry_stat.nr_unused++;
  			spin_unlock(&dentry->d_lock);
 			continue;
 		}
 		/*
 		 * If the dentry is not DCACHED_REFERENCED, it is time
-		 * to remove it from the dcache, provided the super block is
-		 * NULL (which means we are trying to reclaim memory)
-		 * or this dentry belongs to the same super block that
-		 * we want to shrink.
-		 */
-		/*
-		 * If this dentry is for "my" filesystem, then I can prune it
-		 * without taking the s_umount lock (I already hold it).
+		 * to remove it from the dcache
 		 */
-		if (sb && dentry->d_sb == sb) {
-			prune_one_dentry(dentry);
-			continue;
-		}
+
 		/*
 		 * ...otherwise we need to be sure this filesystem isn't being
 		 * unmounted, otherwise we could race with
@@ -505,13 +490,20 @@ static void prune_dcache(int count, struct super_block *sb)
 				continue;
 			}
 			up_read(s_umount);
+		} else if (dispose_list) {
+			/*
+			 * if we're shrinking dcache for an unmounting fs
+			 * just go ahead and exit
+			 */
+			spin_unlock(&dentry->d_lock);
+			break;
 		}
 		spin_unlock(&dentry->d_lock);
 		/*
 		 * Insert dentry at the head of the list as inserting at the
 		 * tail leads to a cycle.
 		 */
- 		list_add(&dentry->d_lru, &dentry_unused);
+ 		list_add(&dentry->d_lru, list);
 		dentry_stat.nr_unused++;
 	}
 	spin_unlock(&dcache_lock);
@@ -761,19 +753,20 @@ positive:
 
 /*
  * Search the dentry child list for the specified parent,
- * and move any unused dentries to the end of the unused
+ * and move any unused dentries to the end of the dispose
  * list for prune_dcache(). We descend to the next level
  * whenever the d_subdirs list is non-empty and continue
  * searching.
  *
  * It returns zero iff there are no unused children,
  * otherwise  it returns the number of children moved to
- * the end of the unused list. This may not be the total
+ * the end of the dispose list. This may not be the total
  * number of unused children, because select_parent can
  * drop the lock and return early due to latency
  * constraints.
  */
-static int select_parent(struct dentry * parent)
+static int select_parent(struct dentry * parent,
+			 struct list_head *dispose_list)
 {
 	struct dentry *this_parent = parent;
 	struct list_head *next;
@@ -794,7 +787,7 @@ resume:
 		 * of the unused list for prune_dcache
 		 */
 		if (!atomic_read(&dentry->d_count)) {
-			list_add_tail(&dentry->d_lru, &dentry_unused);
+			list_add_tail(&dentry->d_lru, dispose_list);
 			dentry_stat.nr_unused++;
 			found++;
 		}
@@ -838,9 +831,14 @@ out:
 void shrink_dcache_parent(struct dentry * parent)
 {
 	int found;
+	LIST_HEAD(dispose_list);
 
-	while ((found = select_parent(parent)) != 0)
-		prune_dcache(found, parent->d_sb);
+	/*
+	 * select_parent will populate dispose_list with the
+	 * dentry's that we are removing
+	 */
+	while ((found = select_parent(parent, &dispose_list)) != 0)
+		prune_dcache(found, &dispose_list);
 }
 
 /*
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux