-/* 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*/
};
/* 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 */
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) */
* 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;
}
{
unsigned int key;
- key = (unsigned int)p >> 2;
+ key = (unsigned int)(long)p >> 2;
key = (key * HASH_CONST)%HASH_PRIME;
return key;
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);
{
/* 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;
/************** 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);
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;
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--;
{
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);
}