i386-user-mode-linux-support-20030513
[openafs.git] / src / afs / LINUX / osi_alloc.c
index 7313c23..0e30d67 100644 (file)
@@ -1,17 +1,30 @@
-/* Copyright (C) 1995, 1989, 1998 Transarc Corporation - All rights reserved */
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
 /*
  * osi_alloc.c - Linux memory allocation routines.
  *
  */
-#include "../afs/param.h"
-#include "../afs/sysincludes.h"
-#include "../afs/afsincludes.h"
-#include "../h/mm.h"
+#include <afsconfig.h>
+#include "afs/param.h"
+
+RCSID("$Header$");
 
-#include "../afs/afs_atomlist.h"
-#include "../afs/afs_lhash.h"
+#include "afs/sysincludes.h"
+#include "afsincludes.h"
+#include "h/mm.h"
+#include "h/slab.h"
 
-#define MAX_KMALLOC_SIZE (131072-16) /* Max we can alloc in physmem */
+#include "afs_atomlist.h"
+#include "afs_lhash.h"
+
+#define MAX_KMALLOC_SIZE PAGE_SIZE /* Max we should alloc with kmalloc */
 #define MAX_BUCKET_LEN 30 /* max. no. of entries per buckets we expect to see */
 #define STAT_INTERVAL 8192 /* we collect stats once every STAT_INTERVAL allocs*/
 
@@ -24,8 +37,8 @@ struct osi_linux_mem {
 };
 
 /* These assume 32-bit pointers */
-#define MEMTYPE(A) (((unsigned int)A) & 0x3)
-#define MEMADDR(A) (void *)((unsigned int)(A) & (~0x3))
+#define MEMTYPE(A) (((unsigned long)A) & 0x3)
+#define MEMADDR(A) (void *)((unsigned long)(A) & (~0x3))
 
 /* globals */
 afs_atomlist *al_mem_pool; /* pool of osi_linux_mem structures */
@@ -37,10 +50,14 @@ unsigned int afs_linux_hash_verify_count = 0; /* used by hash_verify */
 struct afs_lhash_stat afs_linux_lsb; /* hash table statistics */
 unsigned int afs_linux_hash_bucket_dist[MAX_BUCKET_LEN]; /* bucket population distribution in our hash table */
 
+#if defined(AFS_LINUX24_ENV)
+#include "h/vmalloc.h"
+#else
 /* externs : can we do this in a better way. Including vmalloc.h causes other
  * problems.*/
 extern void vfree(void * addr);
 extern void *vmalloc(unsigned long size);
+#endif
 
 /* Allocator support functions (static) */
 
@@ -59,39 +76,51 @@ static int hash_equal(const void *a, const void *b)
  *  returns NULL if we failed to allocate memory.
  *  or pointer to memory if we succeeded.
  */
-static void *linux_alloc(unsigned int asize)
+static void *linux_alloc(unsigned int asize, int drop_glock)
 {
     void *new = NULL;
-    int has_afs_glock = ISAFS_GLOCK();
-
-    /* if global lock has been held save this info and unlock it. */
-    if (has_afs_glock)
-        AFS_GUNLOCK();
+    int max_retry = 10;
+    int haveGlock = ISAFS_GLOCK();
 
     /*  if we can use kmalloc use it to allocate the required memory. */
-    if (asize <  MAX_KMALLOC_SIZE) {
-        new = (void *)kmalloc(asize, GFP_KERNEL);
+    while(!new && max_retry)
+    {
+        if (asize <=  MAX_KMALLOC_SIZE) {
+        new = (void *)(unsigned long)kmalloc(asize, 
+#ifdef GFP_NOFS
+                                            GFP_NOFS
+#else
+                                            GFP_KERNEL
+#endif
+                                            );
         if (new) /* piggy back alloc type */
-            (unsigned int)new |= KM_TYPE;
-    }
-    if (!new) { /* otherwise use vmalloc  */
-       int max_wait = 10;
-        while (!(new = (void *)vmalloc(asize))) {
-            if (--max_wait <=0) {
-               break;
+            (unsigned long)new |= KM_TYPE;
+        } else {
+            new = (void *)vmalloc(asize);
+           if (new) /* piggy back alloc type */
+               (unsigned long)new |= VM_TYPE;
             }
-           schedule();
+
+       if (!new) {
+#ifdef set_current_state
+           set_current_state(TASK_INTERRUPTIBLE);
+#else
+           current->state = TASK_INTERRUPTIBLE;
+#endif
+           if (drop_glock && haveGlock) AFS_GUNLOCK();
+           schedule_timeout(HZ);
+           if (drop_glock && haveGlock) AFS_GLOCK();
+#ifdef set_current_state
+            set_current_state(TASK_RUNNING);
+#else
+            current->state = TASK_RUNNING;
+#endif
+            --max_retry;
         }
-       if (new) /* piggy back alloc type */
-           (unsigned int)new |= VM_TYPE;
     }
     if (new)
        memset(MEMADDR(new), 0, asize);
 
-    /* if the global lock had been held, lock it again. */
-    if (has_afs_glock)
-        AFS_GLOCK();
-
     return new;
 }
 
@@ -127,7 +156,7 @@ static unsigned hash_chunk(void *p)
 {
     unsigned int key;
 
-    key = (unsigned int)p >> 2;
+    key = (unsigned int)(long)p >> 2;
     key = (key * HASH_CONST)%HASH_PRIME;
 
     return key;
@@ -159,9 +188,15 @@ hash_verify(size_t index, unsigned key, void *data)
     int memtype;
 
     memtype = MEMTYPE(lmp->chunk);
+#if defined(AFS_SPARC64_LINUX24_ENV) || defined(AFS_I386_UMLINUX20_ENV)
+    if ((memtype == KM_TYPE) && (!VALID_PAGE(virt_to_page(lmp->chunk)))) {
+       printf("osi_linux_verify_alloced_memory: address 0x%x outside range, index=%d, key=%d\n", lmp->chunk, index, key);
+    }
+#else
     if ((memtype == KM_TYPE) && (AFS_LINUX_MAP_NR(lmp->chunk) > max_mapnr)) {
        printf("osi_linux_verify_alloced_memory: address 0x%x outside range, index=%d, key=%d\n", lmp->chunk, index, key);
     }
+#endif
     
     if (memtype != KM_TYPE && memtype != VM_TYPE) {
        printf("osi_linux_verify_alloced_memory: unknown type %d at 0x%x, index=%d\n",    memtype, lmp->chunk, index);
@@ -187,14 +222,15 @@ static int linux_alloc_init()
 {
     /* initiate our pool of osi_linux_mem structs */
     al_mem_pool = afs_atomlist_create(sizeof(struct osi_linux_mem),
-                                  sizeof(long)*1024, vmalloc, local_free);
+                                     sizeof(long)*1024, (void *)vmalloc, 
+                                     local_free);
     if (!al_mem_pool) {
         printf("afs_osi_Alloc: Error in initialization(atomlist_create)\n");
         return 0;
     }
 
     /* initialize the hash table to hold references to alloc'ed chunks */
-    lh_mem_htab = afs_lhash_create(hash_equal, vmalloc, local_free);
+    lh_mem_htab = afs_lhash_create(hash_equal, (void *)vmalloc, local_free);
     if (!lh_mem_htab) {
         printf("afs_osi_Alloc: Error in initialization(lhash_create)\n");
         return 0;
@@ -241,27 +277,33 @@ static void get_hash_stats()
 
 /************** Linux memory allocator interface functions **********/
 
+#if defined(AFS_LINUX24_ENV)
+DECLARE_MUTEX(afs_linux_alloc_sem);
+#else
 struct semaphore afs_linux_alloc_sem = MUTEX;
+#endif
 
-void *osi_linux_alloc(unsigned int asize)
+void *osi_linux_alloc(unsigned int asize, int drop_glock)
 {
     void *new = NULL;
     struct osi_linux_mem *lmem;
 
+    new = linux_alloc(asize, drop_glock); /* get a chunk of memory of size asize */
+
+    if (!new) {
+       printf("afs_osi_Alloc: Can't vmalloc %d bytes.\n", asize);
+       return new;
+    }
+
     down(&afs_linux_alloc_sem);
 
-    if (allocator_init == 0) { /* allocator hasn't been initialized yet */
+    /* allocator hasn't been initialized yet */
+    if (allocator_init == 0) {
        if (linux_alloc_init() == 0) {
            goto error;
         }
        allocator_init = 1; /* initialization complete */
     }
-
-    new = linux_alloc(asize); /* get a chunk of memory of size asize */
-    if (!new) {
-       printf("afs_osi_Alloc: Can't vmalloc %d bytes.\n", asize);
-       goto error;
-    }
     
     /* get an atom to store the pointer to the chunk */
     lmem = (struct osi_linux_mem *)afs_atomlist_get(al_mem_pool);
@@ -287,8 +329,11 @@ void *osi_linux_alloc(unsigned int asize)
     return MEMADDR(new);
 
   free_error:
-    if (new)
-        linux_free(new);
+    if (new) {
+       up(&afs_linux_alloc_sem);
+       linux_free(new);
+       down(&afs_linux_alloc_sem);
+    }
     new = NULL;
     goto error;
 
@@ -305,7 +350,7 @@ void osi_linux_free(void *addr)
     
     lmem.chunk = addr;
     /* remove this chunk from our hash table */
-    if ( lmp = (struct osi_linux_mem *)afs_lhash_remove(lh_mem_htab, hash_chunk(addr), &lmem)) {
+    if ((lmp = (struct osi_linux_mem *)afs_lhash_remove(lh_mem_htab, hash_chunk(addr), &lmem))) {
         linux_free(lmp->chunk); /* this contains the piggybacked type info*/
         afs_atomlist_put(al_mem_pool, lmp); /* return osi_linux_mem struct to pool*/
         afs_linux_cur_allocs--;
@@ -323,20 +368,21 @@ void osi_linux_free_afs_memory(void)
 {
     down(&afs_linux_alloc_sem);
 
-    /* iterate through all elements in the hash table and free both 
-     * the chunk and the atom associated with it.
-     */
-    afs_lhash_iter(lh_mem_htab, hash_free);
-
-    /*  free the atomlist. */
-    afs_atomlist_destroy(al_mem_pool);
-
-    /* free the hashlist. */
-    afs_lhash_destroy(lh_mem_htab);
+    if (allocator_init) {
+       /* iterate through all elements in the hash table and free both 
+        * the chunk and the atom associated with it.
+        */
+       afs_lhash_iter(lh_mem_htab, hash_free);
 
-    /* change the state so that the allocator is now uninitialized. */
-    allocator_init = 0;
+       /*  free the atomlist. */
+       afs_atomlist_destroy(al_mem_pool);
 
+       /* free the hashlist. */
+       afs_lhash_destroy(lh_mem_htab);
+       
+       /* change the state so that the allocator is now uninitialized. */
+       allocator_init = 0;
+    }
     up(&afs_linux_alloc_sem);    
 }