implementation of shrink_dcache_sb

Bill Hawes (whawes@star.net)
Tue, 16 Sep 1997 15:24:50 -0400


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

I've attached a preliminary implementation of a shrink_dcache_sb()
function to be used when unmounting a device or invalidating a device's
inodes. This provides finer control over the dcache by not forcing a
full shrink when we just need to remove a device.

To provide reasonable efficiency I move all leaf nodes to a temporary
list, prune them from there, and then repeat as needed. Note that it
doesn't matter if one of the dentries on the temporary list goes back
into use while waiting to be freed; it will simply be unlinked from the
list.

In testing so far this seems to work fine. I'm calling it from do_umount
and from smbfs when invalidating inodes.

Comments or suggested improvements welcome. The patch is against
pre-2.1.56.

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

--- fs/dcache.c.old Sat Sep 13 21:36:53 1997
+++ fs/dcache.c Tue Sep 16 15:08:26 1997
@@ -119,13 +119,13 @@
* something (at which point we need to unuse
* all dentries).
*/
-void prune_dcache(int count)
+static void __prune_dcache(struct list_head *head, unsigned int count)
{
for (;;) {
struct dentry *dentry;
- struct list_head *tmp = dentry_unused.prev;
+ struct list_head *tmp = head->prev;

- if (tmp == &dentry_unused)
+ if (tmp == head)
break;
list_del(tmp);
INIT_LIST_HEAD(tmp);
@@ -149,6 +149,53 @@
}
}

+void prune_dcache(int count)
+{
+ __prune_dcache(&dentry_unused, count);
+}
+
+/* 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.
+ *
+ * We move the selected dentries to a temporary list,
+ * and then shrink that in the usual manner.
+ */
+void shrink_dcache_sb(struct super_block * sb)
+{
+ struct list_head *tmp, *prev;
+ struct dentry *dentry;
+ LIST_HEAD(temp);
+
+repeat:
+ prev = dentry_unused.prev;
+ while (prev != &dentry_unused) {
+ tmp = prev;
+ prev = tmp->prev;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ /*
+ * If the dentry is in use, we can always remove it.
+ * This makes less work for the next pass ...
+ */
+ if (dentry->d_count) {
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ continue;
+ }
+ if (dentry->d_sb == sb) {
+ list_del(tmp);
+ list_add(tmp, &temp);
+ }
+ }
+ if (!list_empty(&temp)) {
+ __prune_dcache(&temp, 0);
+ goto repeat;
+ }
+}
+
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)

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

--------------4CF2BD87CEAC4B83F96ADB41--