2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 * osi_alloc.c - Linux memory allocation routines.
14 #include <afsconfig.h>
15 #include "afs/param.h"
18 #include "afs/sysincludes.h"
19 #include "afsincludes.h"
21 #include <linux/slab.h>
23 #include "afs_atomlist.h"
24 #include "afs_lhash.h"
26 #define MAX_KMALLOC_SIZE PAGE_SIZE /* Max we should alloc with kmalloc */
27 #define MAX_BUCKET_LEN 30 /* max. no. of entries per buckets we expect to see */
28 #define STAT_INTERVAL 8192 /* we collect stats once every STAT_INTERVAL allocs */
31 #define KM_TYPE 1 /* kmalloc */
32 #define VM_TYPE 2 /* vmalloc */
34 struct osi_linux_mem {
38 /* These assume 32-bit pointers */
39 #define MEMTYPE(A) (((unsigned long)A) & 0x3)
40 #define MEMADDR(A) (void *)((unsigned long)(A) & (~0x3))
43 afs_atomlist *al_mem_pool; /* pool of osi_linux_mem structures */
44 afs_lhash *lh_mem_htab; /* mem hash table */
45 unsigned int allocator_init = 0; /* has the allocator been initialized? */
46 unsigned int afs_linux_cur_allocs = 0;
47 unsigned int afs_linux_total_allocs = 0;
48 unsigned int afs_linux_hash_verify_count = 0; /* used by hash_verify */
49 struct afs_lhash_stat afs_linux_lsb; /* hash table statistics */
50 unsigned int afs_linux_hash_bucket_dist[MAX_BUCKET_LEN]; /* bucket population distribution in our hash table */
52 #include <linux/vmalloc.h>
54 /* Allocator support functions (static) */
57 hash_equal(const void *a, const void *b)
59 return (MEMADDR(((struct osi_linux_mem *)a)->chunk) ==
60 MEMADDR(((struct osi_linux_mem *)b)->chunk));
64 /* linux_alloc : Allocates memory from the linux kernel. It uses
65 * kmalloc if possible. Otherwise, we use vmalloc.
67 * asize - size of memory required in bytes
69 * returns NULL if we failed to allocate memory.
70 * or pointer to memory if we succeeded.
73 linux_alloc(unsigned int asize, int drop_glock)
77 int haveGlock = ISAFS_GLOCK();
79 /* if we can use kmalloc use it to allocate the required memory. */
80 while (!new && max_retry) {
81 if (asize <= MAX_KMALLOC_SIZE) {
82 new = (void *)(unsigned long)kmalloc(asize, GFP_NOFS);
83 if (new) /* piggy back alloc type */
84 new = (void *)(KM_TYPE | (unsigned long)new);
86 osi_Assert(drop_glock || !haveGlock);
87 if (drop_glock && haveGlock)
89 new = (void *)vmalloc(asize);
90 if (drop_glock && haveGlock)
92 if (new) /* piggy back alloc type */
93 new = (void *)(VM_TYPE | (unsigned long)new);
97 #ifdef set_current_state
98 set_current_state(TASK_INTERRUPTIBLE);
100 current->state = TASK_INTERRUPTIBLE;
102 if (drop_glock && haveGlock)
104 schedule_timeout(HZ);
105 if (drop_glock && haveGlock)
107 #ifdef set_current_state
108 set_current_state(TASK_RUNNING);
110 current->state = TASK_RUNNING;
116 memset(MEMADDR(new), 0, asize);
125 /* mask out the type information from the pointer and
126 * use the appropriate free routine to free the chunk.
128 switch (MEMTYPE(p)) {
136 printf("afs_osi_Free: Asked to free unknown type %d at 0x%lx\n",
137 (int)MEMTYPE(p), (unsigned long)MEMADDR(p));
143 /* hash_chunk() receives a pointer to a chunk and hashes it to produce a
144 * key that the hashtable can use. The key is obtained by
145 * right shifting out the 2 LSBs and then multiplying the
146 * result by a constant no. and dividing it with a large prime.
148 #define HASH_CONST 32786
149 #define HASH_PRIME 79367
155 key = (unsigned int)(long)p >> 2;
156 key = (key * HASH_CONST) % HASH_PRIME;
161 /* hash_free() : Invoked by osi_linux_free_afs_memory(), thru
162 * afs_lhash_iter(), this function is called by the lhash
163 * module for every entry in the hash table. hash_free
164 * frees the memory associated with the entry as well
165 * as returning the osi_linux_mem struct to its pool.
168 hash_free(size_t index, unsigned key, void *data)
170 linux_free(((struct osi_linux_mem *)data)->chunk);
171 afs_atomlist_put(al_mem_pool, data);
174 /* hash_verify() is invoked by osi_linux_verify_alloced_memory() thru
175 * afs_lhash_iter() and is called by the lhash module for every element
177 * hash_verify() verifies (within limits) that the memory passed to it is
181 hash_verify(size_t index, unsigned key, void *data)
183 struct osi_linux_mem *lmp = (struct osi_linux_mem *)data;
186 memtype = MEMTYPE(lmp->chunk);
187 if (memtype != KM_TYPE && memtype != VM_TYPE) {
189 ("osi_linux_verify_alloced_memory: unknown type %d at 0x%lx, index=%lu\n",
190 (int)memtype, (unsigned long)lmp->chunk, (unsigned long)index);
192 afs_linux_hash_verify_count++;
196 /* local_free() : wrapper for vfree(), to deal with incompatible protoypes */
198 local_free(void *p, size_t n)
203 /* linux_alloc_init(): Initializes the kernel memory allocator. As part
204 * of this process, it also initializes a pool of osi_linux_mem
205 * structures as well as the hash table itself.
211 linux_alloc_init(void)
213 /* initiate our pool of osi_linux_mem structs */
215 afs_atomlist_create(sizeof(struct osi_linux_mem), sizeof(long) * 1024,
216 (void *)vmalloc, local_free);
218 printf("afs_osi_Alloc: Error in initialization(atomlist_create)\n");
222 /* initialize the hash table to hold references to alloc'ed chunks */
223 lh_mem_htab = afs_lhash_create(hash_equal, (void *)vmalloc, local_free);
225 printf("afs_osi_Alloc: Error in initialization(lhash_create)\n");
233 /* hash_bucket_stat() : Counts the no. of elements in each bucket and
234 * stores results in our bucket stats vector.
236 static unsigned int cur_bucket, cur_bucket_len;
238 hash_bucket_stat(size_t index, unsigned key, void *data)
240 if (index == cur_bucket) {
241 /* while still on the same bucket, inc len & return */
244 } else { /* if we're on the next bucket, store the distribution */
245 if (cur_bucket_len < MAX_BUCKET_LEN)
246 afs_linux_hash_bucket_dist[cur_bucket_len]++;
249 ("afs_get_hash_stats: Warning! exceeded max bucket len %d\n",
256 /* get_hash_stats() : get hash table statistics */
262 afs_lhash_stat(lh_mem_htab, &afs_linux_lsb);
264 /* clear out the bucket stat vector */
265 for (i = 0; i < MAX_BUCKET_LEN; i++, afs_linux_hash_bucket_dist[i] = 0);
266 cur_bucket = cur_bucket_len = 00;
268 /* populate the bucket stat vector */
269 afs_lhash_iter(lh_mem_htab, hash_bucket_stat);
272 /************** Linux memory allocator interface functions **********/
274 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
275 DEFINE_MUTEX(afs_linux_alloc_sem);
277 DECLARE_MUTEX(afs_linux_alloc_sem);
281 osi_linux_alloc(unsigned int asize, int drop_glock)
284 struct osi_linux_mem *lmem;
286 new = linux_alloc(asize, drop_glock); /* get a chunk of memory of size asize */
289 printf("afs_osi_Alloc: Can't vmalloc %d bytes.\n", asize);
293 mutex_lock(&afs_linux_alloc_sem);
295 /* allocator hasn't been initialized yet */
296 if (allocator_init == 0) {
297 if (linux_alloc_init() == 0) {
300 allocator_init = 1; /* initialization complete */
303 /* get an atom to store the pointer to the chunk */
304 lmem = (struct osi_linux_mem *)afs_atomlist_get(al_mem_pool);
306 printf("afs_osi_Alloc: atomlist_get() failed.");
309 /* store the chunk reference */
312 /* hash in the chunk */
313 if (afs_lhash_enter(lh_mem_htab, hash_chunk(new), lmem) != 0) {
314 printf("afs_osi_Alloc: lhash_enter failed\n");
317 afs_linux_cur_allocs++; /* no. of current allocations */
318 afs_linux_total_allocs++; /* total no. of allocations done so far */
319 if ((afs_linux_cur_allocs % STAT_INTERVAL) == 0) {
323 mutex_unlock(&afs_linux_alloc_sem);
336 /* osi_linux_free() - free chunk of memory passed to us.
339 osi_linux_free(void *addr)
341 struct osi_linux_mem lmem, *lmp;
343 mutex_lock(&afs_linux_alloc_sem);
346 /* remove this chunk from our hash table */
348 (struct osi_linux_mem *)afs_lhash_remove(lh_mem_htab,
349 hash_chunk(addr), &lmem))) {
350 linux_free(lmp->chunk); /* this contains the piggybacked type info */
351 afs_atomlist_put(al_mem_pool, lmp); /* return osi_linux_mem struct to pool */
352 afs_linux_cur_allocs--;
354 printf("osi_linux_free: failed to remove chunk from hashtable\n");
358 mutex_unlock(&afs_linux_alloc_sem);
361 /* osi_linux_free_afs_memory() - free all chunks of memory allocated.
364 osi_linux_free_afs_memory(void)
366 mutex_lock(&afs_linux_alloc_sem);
368 if (allocator_init) {
369 /* iterate through all elements in the hash table and free both
370 * the chunk and the atom associated with it.
372 afs_lhash_iter(lh_mem_htab, hash_free);
374 /* free the atomlist. */
375 afs_atomlist_destroy(al_mem_pool);
377 /* free the hashlist. */
378 afs_lhash_destroy(lh_mem_htab);
380 /* change the state so that the allocator is now uninitialized. */
383 mutex_unlock(&afs_linux_alloc_sem);
386 /* osi_linux_verify_alloced_memory(): verify all chunks of alloced memory in
390 osi_linux_verify_alloced_memory()
392 mutex_lock(&afs_linux_alloc_sem);
394 /* count of times hash_verify was called. reset it to 0 before iteration */
395 afs_linux_hash_verify_count = 0;
397 /* iterate thru elements in the hash table */
398 afs_lhash_iter(lh_mem_htab, hash_verify);
400 if (afs_linux_hash_verify_count != afs_linux_cur_allocs) {
401 /* hmm, some pieces of memory are missing. */
403 ("osi_linux_verify_alloced_memory: %d chunks of memory are not accounted for during verify!\n",
404 afs_linux_hash_verify_count - afs_linux_cur_allocs);
407 mutex_unlock(&afs_linux_alloc_sem);
411 #ifdef AFS_PRIVATE_OSI_ALLOCSPACES
414 osi_FreeLargeSpace(void *p)
420 osi_FreeSmallSpace(void *p)
426 osi_AllocLargeSpace(size_t size)
428 if (size > AFS_LRALLOCSIZ)
429 osi_Panic("osi_AllocLargeSpace: size=%d\n", (int) size);
430 return kmalloc(AFS_LRALLOCSIZ, GFP_NOFS);
434 osi_AllocSmallSpace(size_t size)
436 if (size > AFS_SMALLOCSIZ)
437 osi_Panic("osi_AllocSmallS: size=%d\n", (int)size);
438 return kmalloc(AFS_SMALLOCSIZ, GFP_NOFS);
440 #endif /* AFS_PRIVATE_OSI_ALLOCSPACES */