1 /*
2 * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31 */
32
33 #include <linux/sched.h>
34 #include <linux/mm.h>
35 #include <linux/vmalloc.h>
36 #include <linux/swap.h>
37
38 #include "time.h"
39 #include "kmem.h"
40
41 #define MAX_VMALLOCS 6
42 #define MAX_SLAB_SIZE 0x20000
43 #define MAX_SHAKE 8
44
45 static kmem_shake_func_t shake_list[MAX_SHAKE];
46 static DECLARE_MUTEX(shake_sem);
47
48 /*
49 * Old-school memory shaking for vanilla 2.4 kernels (which
50 * have no VM shake callback/registration infrastructure).
51 */
52
53 kmem_shaker_t
kmem_shake_register(kmem_shake_func_t sfunc)54 kmem_shake_register(kmem_shake_func_t sfunc)
55 {
56 int i;
57
58 down(&shake_sem);
59 for (i = 0; i < MAX_SHAKE; i++) {
60 if (shake_list[i] == NULL) {
61 shake_list[i] = sfunc;
62 break;
63 }
64 }
65 if (i == MAX_SHAKE)
66 BUG();
67 up(&shake_sem);
68
69 return (kmem_shaker_t)sfunc;
70 }
71
72 void
kmem_shake_deregister(kmem_shaker_t sfunc)73 kmem_shake_deregister(kmem_shaker_t sfunc)
74 {
75 int i;
76
77 down(&shake_sem);
78 for (i = 0; i < MAX_SHAKE; i++) {
79 if (shake_list[i] == (kmem_shake_func_t)sfunc)
80 break;
81 }
82 if (i == MAX_SHAKE)
83 BUG();
84 for (; i < MAX_SHAKE - 1; i++) {
85 shake_list[i] = shake_list[i+1];
86 }
87 shake_list[i] = NULL;
88 up(&shake_sem);
89 }
90
kmem_shake(void)91 static __inline__ void kmem_shake(void)
92 {
93 int i;
94
95 down(&shake_sem);
96 for (i = 0; i < MAX_SHAKE && shake_list[i]; i++)
97 (*shake_list[i])(0, 0);
98 up(&shake_sem);
99 delay(10);
100 }
101
102 void *
kmem_alloc(size_t size,int flags)103 kmem_alloc(size_t size, int flags)
104 {
105 int retries = 0, lflags = kmem_flags_convert(flags);
106 void *ptr;
107
108 do {
109 if (size < MAX_SLAB_SIZE || retries > MAX_VMALLOCS)
110 ptr = kmalloc(size, lflags);
111 else
112 ptr = __vmalloc(size, lflags, PAGE_KERNEL);
113 if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
114 return ptr;
115 kmem_shake();
116 if (!(++retries % 100))
117 printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
118 __FUNCTION__, lflags);
119 } while (1);
120 }
121
122 void *
kmem_zalloc(size_t size,int flags)123 kmem_zalloc(size_t size, int flags)
124 {
125 void *ptr;
126
127 ptr = kmem_alloc(size, flags);
128 if (ptr)
129 memset((char *)ptr, 0, (int)size);
130 return ptr;
131 }
132
133 void
kmem_free(void * ptr,size_t size)134 kmem_free(void *ptr, size_t size)
135 {
136 if (((unsigned long)ptr < VMALLOC_START) ||
137 ((unsigned long)ptr >= VMALLOC_END)) {
138 kfree(ptr);
139 } else {
140 vfree(ptr);
141 }
142 }
143
144 void *
kmem_realloc(void * ptr,size_t newsize,size_t oldsize,int flags)145 kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags)
146 {
147 void *new;
148
149 new = kmem_alloc(newsize, flags);
150 if (ptr) {
151 if (new)
152 memcpy(new, ptr,
153 ((oldsize < newsize) ? oldsize : newsize));
154 kmem_free(ptr, oldsize);
155 }
156 return new;
157 }
158
159 void *
kmem_zone_alloc(kmem_zone_t * zone,int flags)160 kmem_zone_alloc(kmem_zone_t *zone, int flags)
161 {
162 int retries = 0, lflags = kmem_flags_convert(flags);
163 void *ptr;
164
165 do {
166 ptr = kmem_cache_alloc(zone, lflags);
167 if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
168 return ptr;
169 kmem_shake();
170 if (!(++retries % 100))
171 printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
172 __FUNCTION__, lflags);
173 } while (1);
174 }
175
176 void *
kmem_zone_zalloc(kmem_zone_t * zone,int flags)177 kmem_zone_zalloc(kmem_zone_t *zone, int flags)
178 {
179 void *ptr;
180
181 ptr = kmem_zone_alloc(zone, flags);
182 if (ptr)
183 memset((char *)ptr, 0, kmem_cache_size(zone));
184 return ptr;
185 }
186