patch-2.1.58 linux/fs/dcache.c
Next file: linux/fs/file_table.c
Previous file: linux/fs/binfmt_misc.c
Back to the patch index
Back to the overall index
- Lines: 205
- Date:
Sun Oct 12 11:31:24 1997
- Orig file:
v2.1.57/linux/fs/dcache.c
- Orig date:
Tue Sep 23 16:48:48 1997
diff -u --recursive --new-file v2.1.57/linux/fs/dcache.c linux/fs/dcache.c
@@ -61,37 +61,54 @@
*/
void dput(struct dentry *dentry)
{
- if (dentry) {
- int count;
+ int count;
+
+ if (!dentry)
+ return;
+
repeat:
- count = dentry->d_count-1;
- if (count < 0) {
- printk("Negative d_count (%d) for %s/%s\n",
- count,
- dentry->d_parent->d_name.name,
- dentry->d_name.name);
- *(int *)0 = 0;
- }
+ count = dentry->d_count - 1;
+ if (count != 0)
+ goto out;
+
+ /*
+ * Note that if d_op->d_delete blocks,
+ * the dentry could go back in use.
+ * Each fs will have to watch for this.
+ */
+ if (dentry->d_op && dentry->d_op->d_delete) {
+ dentry->d_op->d_delete(dentry);
+
+ count = dentry->d_count - 1;
+ if (count != 0)
+ goto out;
+ }
+
+ list_del(&dentry->d_lru);
+ if (list_empty(&dentry->d_hash)) {
+ struct inode *inode = dentry->d_inode;
+ struct dentry * parent;
+ if (inode)
+ iput(inode);
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (dentry == parent)
+ return;
+ dentry = parent;
+ goto repeat;
+ }
+ list_add(&dentry->d_lru, &dentry_unused);
+out:
+ if (count >= 0) {
dentry->d_count = count;
- if (!count) {
- list_del(&dentry->d_lru);
- if (dentry->d_op && dentry->d_op->d_delete)
- dentry->d_op->d_delete(dentry);
- if (list_empty(&dentry->d_hash)) {
- struct inode *inode = dentry->d_inode;
- struct dentry * parent;
- if (inode)
- iput(inode);
- parent = dentry->d_parent;
- d_free(dentry);
- if (dentry == parent)
- return;
- dentry = parent;
- goto repeat;
- }
- list_add(&dentry->d_lru, &dentry_unused);
- }
+ return;
}
+
+ printk("Negative d_count (%d) for %s/%s\n",
+ count,
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ *(int *)0 = 0;
}
/*
@@ -99,13 +116,14 @@
* possible. If there are other users of the dentry we
* can't invalidate it.
*
- * This is currently incorrect. We should try to see if
- * we can invalidate any unused children - right now we
- * refuse to invalidate way too much.
+ * We should probably try to see if we can invalidate
+ * any unused children - right now we refuse to invalidate
+ * too much. That would require a better child list
+ * data structure, though.
*/
int d_invalidate(struct dentry * dentry)
{
- /* We should do a partial shrink_dcache here */
+ /* We might want to do a partial shrink_dcache here */
if (dentry->d_count != 1)
return -EBUSY;
@@ -114,6 +132,27 @@
}
/*
+ * Throw away a dentry - free the inode, dput the parent.
+ * This requires that the LRU list has already been
+ * removed.
+ */
+static inline void prune_one_dentry(struct dentry * dentry)
+{
+ 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);
+}
+
+/*
* Shrink the dcache. This is done when we need
* more memory, or simply when we need to unmount
* something (at which point we need to unuse
@@ -131,21 +170,62 @@
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);
+ prune_one_dentry(dentry);
if (!--count)
break;
}
+ }
+}
+
+/*
+ * 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)
+ continue;
+ 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)
+ continue;
+ if (dentry->d_count)
+ continue;
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ prune_one_dentry(dentry);
+ goto repeat;
}
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov