summaryrefslogtreecommitdiff
path: root/libdde_linux26/lib/src/arch/l4/kmalloc.c
blob: 816f443cd6829a6940d373f26eccbe01a5d4b375 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * \brief   kmalloc() implementation
 * \author  Christian Helmuth <ch12@os.inf.tu-dresden.de>
 * \date    2007-01-24
 *
 * In Linux 2.6 this resides in mm/slab.c.
 *
 * This implementation of kmalloc() stays with Linux's and uses kmem_caches for
 * some power of two bytes. For larger allocations ddedkit_large_malloc() is
 * used. This way, we optimize for speed and potentially waste memory
 * resources.
 */

/* Linux */
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/mm.h>
#include <asm/io.h>

#include "local.h"

#include <dde26.h>

/* dummy */
int forbid_dac;

/* This stuff is needed by some drivers, e.g. for ethtool.
 * XXX: This is a fake, implement it if you really need ethtool stuff.
 */
struct page* mem_map = NULL;
static bootmem_data_t contig_bootmem_data;
struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
		    unsigned long pfn, unsigned long size, pgprot_t prot)
{
	return 0;
}
EXPORT_SYMBOL(remap_pfn_range);

/*******************
 ** Configuration **
 *******************/

#define DEBUG_MALLOC 0

/********************
 ** Implementation **
 ********************/

/*
 * These are the default caches for kmalloc. Custom caches can have other sizes.
 */
static struct cache_sizes malloc_sizes[] = {
#define CACHE(x) { .cs_size = (x) },
#include <linux/kmalloc_sizes.h>
	CACHE(ULONG_MAX)
#undef CACHE
};


/*
 * kmalloc() cache names
 */
static const char *malloc_names[] = {
#define CACHE(x) "size-" #x,
#include <linux/kmalloc_sizes.h>
	NULL
#undef CACHE
};


/**
 * Find kmalloc() cache for size
 */
static struct kmem_cache *find_cache(size_t size)
{
	struct cache_sizes *sizes;

	for (sizes = malloc_sizes; size > sizes->cs_size; ++sizes) ;

	return sizes->cs_cachep;
}


/**
 * Free previously allocated memory
 * @objp: pointer returned by kmalloc.
 *
 * If @objp is NULL, no operation is performed.
 *
 * Don't free memory not originally allocated by kmalloc()
 * or you will run into trouble.
 */
void kfree(const void *objp)
{
	if (!objp) return;

	/* find cache back-pointer */
	void **p = (void **)objp - 1;

	ddekit_log(DEBUG_MALLOC, "objp=%p cache=%p (%d)",
	           p, *p, *p ? kmem_cache_size(*p) : 0);

	if (*p)
		/* free from cache */
		kmem_cache_free(*p, p);
	else
		/* no cache for this size - use ddekit free */
		ddekit_large_free(p);
}


/**
 * Allocate memory
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate.
 *
 * kmalloc is the normal method of allocating memory
 * in the kernel.
 */
void *__kmalloc(size_t size, gfp_t flags)
{
	/* add space for back-pointer */
	size += sizeof(void *);

	/* find appropriate cache */
	struct kmem_cache *cache = find_cache(size);

	void **p;
	if (cache) {
		/* allocate from cache */
		p = kmem_cache_alloc(cache, flags);
		if (!p) {
			printk("__kmalloc: kmem_cache_alloc %s fails\n",
			       ((char **)cache)[0]);
		}
	}
	else {
		/* no cache for this size - use ddekit malloc */
		p = ddekit_large_malloc(size);
		if (flags & __GFP_ZERO)
			memset (p, 0, size);
		if (!p) {
			printk("__kmalloc: ddekit_large_malloc %d fails\n",
			       size);
		}
	}

	ddekit_log(DEBUG_MALLOC, "size=%d, cache=%p (%d) => %p",
	           size, cache, cache ? kmem_cache_size(cache) : 0, p);

	/* return pointer to actual chunk */
	if (p) {
		*p = cache;
		p++;
	}
	return p;
}


size_t ksize(const void *p)
{
	struct kmem_cache *cache = (struct kmem_cache *)*((void**)p - 1);
	if (cache)
		return kmem_cache_size(cache);
	return -1;
}


void *dma_alloc_coherent(struct device *dev, size_t size, 
                         dma_addr_t *dma_handle, gfp_t flag)
{
	void *ret = (void *)__get_free_pages(flag, get_order(size));

	if (ret != NULL) {
		memset(ret, 0, size);
		*dma_handle = virt_to_bus(ret);
	}
	return ret;
}


void dma_free_coherent(struct device *dev, size_t size,
                       void *vaddr, dma_addr_t dma_handle)
{
	free_pages((unsigned long)vaddr, get_order(size));
}


/********************
 ** Initialization **
 ********************/

/**
 * dde_linux kmalloc initialization
 */
void l4dde26_kmalloc_init(void)
{
	struct cache_sizes  *sizes = malloc_sizes;
	const char         **names = malloc_names;

	/* init malloc sizes array */
	for (; sizes->cs_size != ULONG_MAX; ++sizes, ++names)
		sizes->cs_cachep = kmem_cache_create(*names, sizes->cs_size, 0, 0, 0);
}