summaryrefslogtreecommitdiffstats
path: root/kernel/include/linux/netfilter_ipv4/ip_set_malloc.h
blob: 8bce6675e2678978952c1b829fdc0a825bfc2a02 (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
#ifndef _IP_SET_MALLOC_H
#define _IP_SET_MALLOC_H

#ifdef __KERNEL__
#include <linux/vmalloc.h> 

static size_t max_malloc_size = 0, max_page_size = 0;
static size_t default_max_malloc_size = 131072;			/* Guaranteed: slab.c */

static inline int init_max_page_size(void)
{
/* Compatibility glues to support 2.4.36 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#define __GFP_NOWARN		0

	/* Guaranteed: slab.c */
	max_malloc_size = max_page_size = default_max_malloc_size;
#else
	size_t page_size = 0;

#define CACHE(x) if (max_page_size == 0 || x < max_page_size)	\
			page_size = x;
#include <linux/kmalloc_sizes.h>
#undef CACHE
	if (page_size) {
		if (max_malloc_size == 0)
			max_malloc_size = page_size;

		max_page_size = page_size;

		return 1;
	}
#endif
	return 0;
}

struct harray {
	size_t max_elements;
	void *arrays[0];
};

static inline void * 
__harray_malloc(size_t hashsize, size_t typesize, int flags)
{
	struct harray *harray;
	size_t max_elements, size, i, j;

	BUG_ON(max_page_size == 0);

	if (typesize > max_page_size)
		return NULL;

	max_elements = max_page_size/typesize;
	size = hashsize/max_elements;
	if (hashsize % max_elements)
		size++;
	
	/* Last pointer signals end of arrays */
	harray = kmalloc(sizeof(struct harray) + (size + 1) * sizeof(void *),
			 flags);

	if (!harray)
		return NULL;
	
	for (i = 0; i < size - 1; i++) {
		harray->arrays[i] = kmalloc(max_elements * typesize, flags);
		if (!harray->arrays[i])
			goto undo;
		memset(harray->arrays[i], 0, max_elements * typesize);
	}
	harray->arrays[i] = kmalloc((hashsize - i * max_elements) * typesize, 
				    flags);
	if (!harray->arrays[i])
		goto undo;
	memset(harray->arrays[i], 0, (hashsize - i * max_elements) * typesize);

	harray->max_elements = max_elements;
	harray->arrays[size] = NULL;
	
	return (void *)harray;

    undo:
    	for (j = 0; j < i; j++) {
    		kfree(harray->arrays[j]);
    	}
    	kfree(harray);
    	return NULL;
}

static inline void *
harray_malloc(size_t hashsize, size_t typesize, int flags)
{
	void *harray;
	
	do {
		harray = __harray_malloc(hashsize, typesize, flags|__GFP_NOWARN);
	} while (harray == NULL && init_max_page_size());
	
	return harray;
}		

static inline void harray_free(void *h)
{
	struct harray *harray = (struct harray *) h;
	size_t i;
	
    	for (i = 0; harray->arrays[i] != NULL; i++)
    		kfree(harray->arrays[i]);
    	kfree(harray);
}

static inline void harray_flush(void *h, size_t hashsize, size_t typesize)
{
	struct harray *harray = (struct harray *) h;
	size_t i;
	
    	for (i = 0; harray->arrays[i+1] != NULL; i++)
		memset(harray->arrays[i], 0, harray->max_elements * typesize);
	memset(harray->arrays[i], 0, 
	       (hashsize - i * harray->max_elements) * typesize);
}

#define HARRAY_ELEM(h, type, which)				\
({								\
	struct harray *__h = (struct harray *)(h);		\
	((type)((__h)->arrays[(which)/(__h)->max_elements])	\
		+ (which)%(__h)->max_elements);			\
})

/* General memory allocation and deallocation */
static inline void * ip_set_malloc(size_t bytes)
{
	BUG_ON(max_malloc_size == 0);

	if (bytes > default_max_malloc_size)
		return vmalloc(bytes);
	else
		return kmalloc(bytes, GFP_KERNEL | __GFP_NOWARN);
}

static inline void ip_set_free(void * data, size_t bytes)
{
	BUG_ON(max_malloc_size == 0);

	if (bytes > default_max_malloc_size)
		vfree(data);
	else
		kfree(data);
}

#endif				/* __KERNEL__ */

#endif /*_IP_SET_MALLOC_H*/