shrink_dcache_sb, third alternative

Bill Hawes (whawes@star.net)
Mon, 22 Sep 1997 09:48:37 -0400


This is a multi-part message in MIME format.
--------------1D9883C24CE074BEB17E9A6C
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

The attached patch provides a third alternative implementation of
shrink_dcache_sb. It makes just two passes through the unused list, so
the worst-case execution is much better than the two previous methods,
and the best-case time is almost as good as that of the second method.

The approach is simple: pass one moves the selected dentries to the most
recent end of the unused list, and then pass two frees the dentries.
(Effectively this uses one pass to set up the most optimistic situation
for the algorithm used in the second alternative.)

The patch also modifies the prune_dcache routine to use the
dget/d_drop/dput idiom for freeing dentries.

Regards,
Bill
--------------1D9883C24CE074BEB17E9A6C
Content-Type: text/plain; charset=us-ascii; name="dcache_sb56v3-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="dcache_sb56v3-patch"

--- fs/dcache.c.old Sat Sep 20 08:16:14 1997
+++ fs/dcache.c Mon Sep 22 09:16:12 1997
@@ -131,24 +184,67 @@
INIT_LIST_HEAD(tmp);
dentry = list_entry(tmp, struct dentry, d_lru);
if (!dentry->d_count) {
- struct dentry * parent;
-
- list_del(&dentry->d_hash);
- if (dentry->d_inode) {
- struct inode * inode = dentry->d_inode;
-
- dentry->d_inode = NULL;
- iput(inode);
- }
- parent = dentry->d_parent;
- d_free(dentry);
- dput(parent);
+ dentry->d_count++;
+ d_drop(dentry);
+ dput(dentry);
if (!--count)
break;
}
}
}

+/* N.B. move to include/linux/dcache.h */
+extern void shrink_dcache_sb(struct super_block *);
+/*
+ * Shrink the dcache for the specified super block.
+ * This allows us to unmount a device without disturbing
+ * the dcache for the other devices.
+ *
+ * This implementation makes just two traversals of the
+ * unused list. On the first pass we move the selected
+ * dentries to the most recent end, and on the second
+ * pass we free them. The second pass must restart after
+ * each dput(), but since the target dentries are all at
+ * the end, it's really just a single traversal.
+ */
+void shrink_dcache_sb(struct super_block * sb)
+{
+ struct list_head *tmp, *next;
+ struct dentry *dentry;
+
+ /*
+ * Pass one ... move the dentries for the specified
+ * superblock to the most recent end of the unused list.
+ */
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb == sb) {
+ list_del(tmp);
+ list_add(tmp, &dentry_unused);
+ }
+ }
+
+ /*
+ * Pass two ... free the dentries for this superblock.
+ */
+repeat:
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb == sb && !dentry->d_count) {
+ dentry->d_count++;
+ d_drop(dentry);
+ dput(dentry);
+ goto repeat;
+ }
+ }
+}
+
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)

struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)

--------------1D9883C24CE074BEB17E9A6C--