patch-2.1.60 linux/fs/dcache.c
Next file: linux/fs/exec.c
Previous file: linux/fs/block_dev.c
Back to the patch index
Back to the overall index
- Lines: 233
- Date:
Thu Oct 23 13:30:14 1997
- Orig file:
v2.1.59/linux/fs/dcache.c
- Orig date:
Wed Oct 15 16:04:23 1997
diff -u --recursive --new-file v2.1.59/linux/fs/dcache.c linux/fs/dcache.c
@@ -19,8 +19,13 @@
#include <linux/malloc.h>
#include <linux/init.h>
+#define DCACHE_PARANOIA 1
+/* #define DCACHE_DEBUG 1 */
+
/* For managing the dcache */
-extern int nr_free_pages, free_pages_low;
+extern unsigned long num_physpages, page_cache_size;
+extern int inodes_stat[];
+#define nr_inodes (inodes_stat[0])
/*
* This is the single most critical data structure when it comes
@@ -37,6 +42,14 @@
static struct list_head dentry_hashtable[D_HASHSIZE];
static LIST_HEAD(dentry_unused);
+struct {
+ int nr_dentry;
+ int nr_unused;
+ int age_limit; /* age in seconds */
+ int want_pages; /* pages requested by system */
+ int dummy[2];
+} dentry_stat = {0, 0, 45, 0,};
+
static inline void d_free(struct dentry *dentry)
{
kfree(dentry->d_name.name);
@@ -84,12 +97,16 @@
goto out;
}
+ if (!list_empty(&dentry->d_lru))
+ dentry_stat.nr_unused--;
list_del(&dentry->d_lru);
if (list_empty(&dentry->d_hash)) {
struct inode *inode = dentry->d_inode;
struct dentry * parent;
- if (inode)
+ if (inode) {
+ dentry->d_inode = NULL;
iput(inode);
+ }
parent = dentry->d_parent;
d_free(dentry);
if (dentry == parent)
@@ -98,6 +115,7 @@
goto repeat;
}
list_add(&dentry->d_lru, &dentry_unused);
+ dentry_stat.nr_unused++;
out:
if (count >= 0) {
dentry->d_count = count;
@@ -132,6 +150,94 @@
}
/*
+ * Selects less valuable dentries to be pruned when
+ * we need inodes or memory. The selected dentries
+ * are moved to the old end of the list where
+ * prune_dcache() can find them.
+ */
+int select_dcache(int count, int page_count)
+{
+ struct list_head *tail = &dentry_unused;
+ struct list_head *next = dentry_unused.prev;
+ int forward = 0, young = 0, depth = dentry_stat.nr_unused >> 1;
+ int found = 0, pages = 0;
+
+#ifdef DCACHE_DEBUG
+printk("select_dcache: %d unused, count=%d, pages=%d\n",
+dentry_stat.nr_unused, count, page_count);
+#endif
+ while (next != &dentry_unused && depth--) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_lru);
+ struct inode *inode = dentry->d_inode;
+ unsigned long value = 0;
+
+ next = tmp->prev;
+ if (forward)
+ next = tmp->next;
+ if (dentry->d_count) {
+ dentry_stat.nr_unused--;
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ continue;
+ }
+ /*
+ * Select dentries based on the page cache count ...
+ * should factor in number of uses as well.
+ */
+ if (inode) {
+ if (inode->i_state)
+ continue;
+ value = inode->i_nrpages;
+ }
+ /*
+ * Consider various exemptions ...
+ */
+ if (!page_count) {
+ if (!inode)
+ continue;
+ if (value >= 3)
+ continue;
+ } else if (!forward) {
+ if (inode) {
+ int age = CURRENT_TIME - inode->i_atime;
+ if (age < dentry_stat.age_limit) {
+ if (++young > 8) {
+ forward = 1;
+ next = dentry_unused.next;
+#ifdef DCACHE_DEBUG
+printk("select_dcache: age=%d, pages=%d, scanning forward\n", age, pages);
+#endif
+ }
+ continue;
+ }
+ }
+ } else {
+ /*
+ * If we're scanning from the front, don't take
+ * files with only a trivial amount of memory.
+ */
+ if (value < 3 || value > 15)
+ continue;
+ }
+ /*
+ * Move the dentry behind the tail
+ */
+ if (tmp != tail->prev) {
+ list_del(tmp);
+ list_add(tmp, tail->prev);
+ }
+ tail = tmp;
+ pages += value;
+ if (++found >= count)
+ break;
+ if (page_count && pages >= page_count)
+ break;
+ }
+ return found;
+}
+
+/*
* Throw away a dentry - free the inode, dput the parent.
* This requires that the LRU list has already been
* removed.
@@ -166,6 +272,7 @@
if (tmp == &dentry_unused)
break;
+ dentry_stat.nr_unused--;
list_del(tmp);
INIT_LIST_HEAD(tmp);
dentry = list_entry(tmp, struct dentry, d_lru);
@@ -222,6 +329,7 @@
continue;
if (dentry->d_count)
continue;
+ dentry_stat.nr_unused--;
list_del(tmp);
INIT_LIST_HEAD(tmp);
prune_one_dentry(dentry);
@@ -229,6 +337,45 @@
}
}
+/*
+ * This is called from do_try_to_free_page() to indicate
+ * that we should reduce the dcache and inode cache memory.
+ */
+void shrink_dcache_memory()
+{
+ dentry_stat.want_pages++;
+}
+
+/*
+ * This carries out the request received by the above routine.
+ */
+void check_dcache_memory()
+{
+ if (dentry_stat.want_pages) {
+ unsigned int count, goal = 0;
+ /*
+ * Set the page goal. We don't necessarily need to trim
+ * the dcache just because the system needs memory ...
+ */
+ if (page_cache_size > (num_physpages >> 1))
+ goal = (dentry_stat.want_pages * page_cache_size)
+ / num_physpages;
+ dentry_stat.want_pages = 0;
+ if (goal) {
+ if (goal > 50)
+ goal = 50;
+ count = select_dcache(128, goal);
+#ifdef DCACHE_DEBUG
+printk("check_dcache_memory: goal=%d, count=%d\n", goal, count);
+#endif
+ if (count) {
+ prune_dcache(count);
+ free_inode_memory(count);
+ }
+ }
+ }
+}
+
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
@@ -237,11 +384,15 @@
struct dentry *dentry;
/*
- * Check whether to shrink the dcache ... this greatly reduces
- * the likelyhood that kmalloc() will need additional memory.
+ * Prune the dcache if there are too many unused dentries.
*/
- if (nr_free_pages < free_pages_low)
- shrink_dcache();
+ if (dentry_stat.nr_unused > 3*(nr_inodes >> 1)) {
+#ifdef DCACHE_PARANOIA
+printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused);
+#endif
+ prune_dcache(8);
+ free_inode_memory(8);
+ }
dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL);
if (!dentry)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov