patch for 2.1.110 misc memory reclamation

Bill Hawes (whawes@star.net)
Wed, 22 Jul 1998 20:50:54 -0400


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

I found another batch of reclaimable pages squirrelled away, this time
in the nfs client dircache. The attached patch adds a new routine
nfs_trim_dircache() to allow the vm system to reclaim the memory.

I've combined this with my previous patch for reclaiming page table
cache memory, as that seemed the best way to integrate it into the vm
system. The call to shm_swap() has been moved into a new
shrink_misc_mem() routine that tries to free shm, page table cache, and
nfs dircache until one succeeds.

The combined effect of the patch makes over 40 extra pages now available
to the system, so it's starting to add up to something worthwhile.
Comments and testing welcome ...

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

--- linux-2.1.110/include/linux/mm.h.old Tue Jul 21 17:50:24 1998
+++ linux-2.1.110/include/linux/mm.h Wed Jul 22 13:13:12 1998
@@ -278,6 +278,7 @@
extern void vmtruncate(struct inode * inode, unsigned long offset);
extern void handle_mm_fault(struct task_struct *tsk,struct vm_area_struct *vma, unsigned long address, int write_access);
extern void check_pgt_cache(void);
+extern int trim_pgt_cache(void);
extern void make_pages_present(unsigned long addr, unsigned long end);

extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
--- linux-2.1.110/include/linux/nfs_fs.h.old Tue Jul 21 17:51:17 1998
+++ linux-2.1.110/include/linux/nfs_fs.h Wed Jul 22 13:13:58 1998
@@ -199,6 +199,7 @@
extern void nfs_free_dircache(void);
extern void nfs_invalidate_dircache(struct inode *);
extern void nfs_invalidate_dircache_sb(struct super_block *);
+extern int nfs_trim_dircache(void);

/*
* linux/fs/nfs/symlink.c
--- linux-2.1.110/kernel/ksyms.c.old Tue Jul 21 14:41:49 1998
+++ linux-2.1.110/kernel/ksyms.c Wed Jul 22 11:24:29 1998
@@ -72,6 +72,9 @@
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
extern int (*do_nfsservctl)(int, void *, void *);
#endif
+#if !defined(CONFIG_NFS_FS) && defined(CONFIG_NFS_FS_MODULE)
+extern int (*nfs_trim_dircache_func)(void);
+#endif

extern void *sys_call_table;

@@ -192,6 +196,10 @@

#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl);
+#endif
+
+#if !defined(CONFIG_NFS_FS) && defined(CONFIG_NFS_FS_MODULE)
+EXPORT_SYMBOL(nfs_trim_dircache_func);
#endif

/* device registration */
--- linux-2.1.110/fs/filesystems.c.old Fri Jul 3 10:32:32 1998
+++ linux-2.1.110/fs/filesystems.c Wed Jul 22 11:34:41 1998
@@ -179,6 +179,10 @@
return err;
}

+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
+int (*nfs_trim_dircache_func)(void) = NULL;
+#endif
+
#ifndef CONFIG_NFSD
#ifdef CONFIG_NFSD_MODULE
int (*do_nfsservctl)(int, void *, void *) = NULL;
--- linux-2.1.110/fs/nfs/dir.c.old Fri Jul 3 10:32:32 1998
+++ linux-2.1.110/fs/nfs/dir.c Wed Jul 22 12:07:51 1998
@@ -352,7 +352,8 @@
if (sb && sb->s_dev != cache->dev)
continue;
if (cache->locked) {
- printk("NFS: cache locked at umount %s\n",
+ printk(KERN_ERR
+ "NFS: cache locked at umount %s\n",
(cache->entry ? "(lost a page!)" : ""));
continue;
}
@@ -362,6 +363,40 @@
cache->entry = NULL;
}
}
+}
+
+/*
+ * Trim a page from the dir cache. Eventually the dir cache
+ * should be implemented as inode (page) cache, but for now
+ * this allows the memory to be reclaimed when needed.
+ */
+int
+nfs_trim_dircache(void)
+{
+ struct nfs_dirent *cache = dircache, *oldest = NULL;
+ unsigned long age = ~0UL;
+ int i;
+
+ /*
+ * Find the oldest cache entry with a freeable page.
+ */
+ for (i = NFS_MAX_DIRCACHE; i--; cache++) {
+ if (cache->locked)
+ continue;
+ if (!cache->entry)
+ continue;
+ if (cache->age <= age) {
+ oldest = cache;
+ age = cache->age;
+ }
+ }
+ if (oldest) {
+ oldest->valid = 0;
+ free_page((unsigned long) oldest->entry);
+ oldest->entry = NULL;
+ return 1;
+ }
+ return 0;
}

/*
--- linux-2.1.110/fs/nfs/inode.c.old Tue Jul 21 14:41:46 1998
+++ linux-2.1.110/fs/nfs/inode.c Wed Jul 22 10:37:03 1998
@@ -32,6 +32,8 @@
#include <asm/system.h>
#include <asm/uaccess.h>

+extern int (*nfs_trim_dircache_func)(void);
+
#define CONFIG_NFS_SNAPSHOT 1
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
@@ -135,6 +137,9 @@
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
+
+ /* clear the "trim dircache" pointer */
+ nfs_trim_dircache_func = NULL;
/*
* Invalidate the dircache for this superblock.
*/
@@ -298,6 +303,9 @@
goto out_no_root;
sb->s_root->d_op = &nfs_dentry_operations;
sb->s_root->d_fsdata = root_fh;
+
+ /* install the "trim dircache" pointer */
+ nfs_trim_dircache_func = nfs_trim_dircache;

/* We're airborne */
unlock_super(sb);
--- linux-2.1.110/mm/memory.c.old Fri Jul 3 10:33:11 1998
+++ linux-2.1.110/mm/memory.c Wed Jul 22 09:34:50 1998
@@ -939,6 +954,10 @@
*/
int pgt_cache_water[2] = { 25, 50 };

+/*
+ * Note: the pxx_quicklists are per CPU, so we don't need
+ * the kernel lock for this operation.
+ */
void check_pgt_cache(void)
{
if(pgtable_cache_size > pgt_cache_water[0]) {
@@ -951,4 +970,36 @@
free_pte_slow(get_pte_fast());
} while(pgtable_cache_size > pgt_cache_water[1]);
}
+}
+
+/*
+ * Free a page from the page table cache, cycling
+ * through the lists for each CPU.
+ *
+ * Note: must be called holding the kernel lock.
+ */
+int trim_pgt_cache(void)
+{
+ static int last_CPU = 0;
+ int cpu_id = cpu_logical_map(last_CPU);
+ unsigned long page = 0;
+
+ if (!cpu_data[cpu_id].pgtable_cache_sz)
+ goto out;
+
+ if ((page = remove_pgd_quick(cpu_id)) != 0)
+ goto out_free;
+ if ((page = remove_pmd_quick(cpu_id)) != 0)
+ goto out_free;
+ page = remove_pte_quick(cpu_id);
+
+out_free:
+ cpu_data[cpu_id].pgtable_cache_sz--;
+ free_page(page);
+out:
+ /* advance the CPU counter for next time */
+ last_CPU++;
+ if (last_CPU > smp_num_cpus)
+ last_CPU = 0;
+ return (page != 0);
}
--- linux-2.1.110/mm/vmscan.c.old Tue Jul 21 14:41:49 1998
+++ linux-2.1.110/mm/vmscan.c Wed Jul 22 11:54:57 1998
@@ -440,6 +440,25 @@
}

/*
+ * Try to free memory from various sources.
+ */
+static int shrink_misc_mem(int pri, int gfp_mask)
+{
+ if ((gfp_mask & __GFP_IO) && shm_swap(pri, gfp_mask))
+ return 1;
+ if (trim_pgt_cache())
+ return 1;
+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
+ {
+ extern int (*nfs_trim_dircache_func)(void);
+ if (nfs_trim_dircache_func && nfs_trim_dircache_func())
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
* We are much more aggressive about trying to swap out than we used
* to be. This works out OK, because we now do proper aging on page
* contents.
@@ -469,7 +488,7 @@
return 1;
state = 1;
case 1:
- if ((gfp_mask & __GFP_IO) && shm_swap(i, gfp_mask))
+ if (shrink_misc_mem(i, gfp_mask))
return 1;
state = 2;
case 2:
--- linux-2.1.110/include/asm-i386/pgtable.h.old Tue Jul 21 17:50:24 1998
+++ linux-2.1.110/include/asm-i386/pgtable.h Wed Jul 22 07:57:52 1998
@@ -482,6 +482,33 @@
{
}

+/*
+ * These are used for freeing pages from the page table cache,
+ * so we just return an unsigned long value.
+ */
+extern __inline__ unsigned long remove_pgd_quick(int cpu_id)
+{
+ unsigned long *ret = cpu_data[cpu_id].pgd_quick;
+
+ if (ret)
+ cpu_data[cpu_id].pgd_quick = (unsigned long *)(*ret);
+ return (unsigned long)ret;
+}
+
+extern __inline__ unsigned long remove_pmd_quick(int cpu)
+{
+ return 0;
+}
+
+extern __inline__ unsigned long remove_pte_quick(int cpu_id)
+{
+ unsigned long *ret = cpu_data[cpu_id].pte_quick;
+
+ if (ret)
+ cpu_data[cpu_id].pte_quick = (unsigned long *)(*ret);
+ return (unsigned long)ret;
+}
+
extern void __bad_pte(pmd_t *pmd);
extern void __bad_pte_kernel(pmd_t *pmd);

--------------C1608123956E778D0813CDDA--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html