aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/ia64/Kconfig4
-rw-r--r--arch/ia64/kernel/Makefile1
-rw-r--r--arch/ia64/kernel/efi.c32
-rw-r--r--arch/ia64/kernel/uncached.c246
-rw-r--r--include/asm-ia64/uncached.h12
-rw-r--r--include/linux/genalloc.h40
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile1
-rw-r--r--lib/genalloc.c188
9 files changed, 530 insertions, 0 deletions
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 3ad2c4af099c..295b5abee72f 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -50,6 +50,10 @@ config SCHED_NO_NO_OMIT_FRAME_POINTER
50 bool 50 bool
51 default y 51 default y
52 52
53config IA64_UNCACHED_ALLOCATOR
54 bool
55 select GENERIC_ALLOCATOR
56
53choice 57choice
54 prompt "System type" 58 prompt "System type"
55 default IA64_GENERIC 59 default IA64_GENERIC
diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile
index c1a02bbc252c..4c73d8ba2e3d 100644
--- a/arch/ia64/kernel/Makefile
+++ b/arch/ia64/kernel/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o domain.o
20obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o 20obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o
21obj-$(CONFIG_IA64_CYCLONE) += cyclone.o 21obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
22obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o 22obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
23obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o
23mca_recovery-y += mca_drv.o mca_drv_asm.o 24mca_recovery-y += mca_drv.o mca_drv_asm.o
24 25
25# The gate DSO image is built using a special linker script. 26# The gate DSO image is built using a special linker script.
diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
index 4a3b1aac43e7..179f230816ed 100644
--- a/arch/ia64/kernel/efi.c
+++ b/arch/ia64/kernel/efi.c
@@ -410,6 +410,38 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg)
410} 410}
411 411
412/* 412/*
413 * Walk the EFI memory map to pull out leftover pages in the lower
414 * memory regions which do not end up in the regular memory map and
415 * stick them into the uncached allocator
416 *
417 * The regular walk function is significantly more complex than the
418 * uncached walk which means it really doesn't make sense to try and
419 * marge the two.
420 */
421void __init
422efi_memmap_walk_uc (efi_freemem_callback_t callback)
423{
424 void *efi_map_start, *efi_map_end, *p;
425 efi_memory_desc_t *md;
426 u64 efi_desc_size, start, end;
427
428 efi_map_start = __va(ia64_boot_param->efi_memmap);
429 efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size;
430 efi_desc_size = ia64_boot_param->efi_memdesc_size;
431
432 for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
433 md = p;
434 if (md->attribute == EFI_MEMORY_UC) {
435 start = PAGE_ALIGN(md->phys_addr);
436 end = PAGE_ALIGN((md->phys_addr+(md->num_pages << EFI_PAGE_SHIFT)) & PAGE_MASK);
437 if ((*callback)(start, end, NULL) < 0)
438 return;
439 }
440 }
441}
442
443
444/*
413 * Look for the PAL_CODE region reported by EFI and maps it using an 445 * Look for the PAL_CODE region reported by EFI and maps it using an
414 * ITR to enable safe PAL calls in virtual mode. See IA-64 Processor 446 * ITR to enable safe PAL calls in virtual mode. See IA-64 Processor
415 * Abstraction Layer chapter 11 in ADAG 447 * Abstraction Layer chapter 11 in ADAG
diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c
new file mode 100644
index 000000000000..490dfc9ab47f
--- /dev/null
+++ b/arch/ia64/kernel/uncached.c
@@ -0,0 +1,246 @@
1/*
2 * Copyright (C) 2001-2005 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
6 * as published by the Free Software Foundation.
7 *
8 * A simple uncached page allocator using the generic allocator. This
9 * allocator first utilizes the spare (spill) pages found in the EFI
10 * memmap and will then start converting cached pages to uncached ones
11 * at a granule at a time. Node awareness is implemented by having a
12 * pool of pages per node.
13 */
14
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/errno.h>
20#include <linux/string.h>
21#include <linux/slab.h>
22#include <linux/efi.h>
23#include <linux/genalloc.h>
24#include <asm/page.h>
25#include <asm/pal.h>
26#include <asm/system.h>
27#include <asm/pgtable.h>
28#include <asm/atomic.h>
29#include <asm/tlbflush.h>
30#include <asm/sn/arch.h>
31
32#define DEBUG 0
33
34#if DEBUG
35#define dprintk printk
36#else
37#define dprintk(x...) do { } while (0)
38#endif
39
40void __init efi_memmap_walk_uc (efi_freemem_callback_t callback);
41
42#define MAX_UNCACHED_GRANULES 5
43static int allocated_granules;
44
45struct gen_pool *uncached_pool[MAX_NUMNODES];
46
47
48static void uncached_ipi_visibility(void *data)
49{
50 int status;
51
52 status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
53 if ((status != PAL_VISIBILITY_OK) &&
54 (status != PAL_VISIBILITY_OK_REMOTE_NEEDED))
55 printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on "
56 "CPU %i\n", status, get_cpu());
57}
58
59
60static void uncached_ipi_mc_drain(void *data)
61{
62 int status;
63 status = ia64_pal_mc_drain();
64 if (status)
65 printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on "
66 "CPU %i\n", status, get_cpu());
67}
68
69
70static unsigned long
71uncached_get_new_chunk(struct gen_pool *poolp)
72{
73 struct page *page;
74 void *tmp;
75 int status, i;
76 unsigned long addr, node;
77
78 if (allocated_granules >= MAX_UNCACHED_GRANULES)
79 return 0;
80
81 node = poolp->private;
82 page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO,
83 IA64_GRANULE_SHIFT-PAGE_SHIFT);
84
85 dprintk(KERN_INFO "get_new_chunk page %p, addr %lx\n",
86 page, (unsigned long)(page-vmem_map) << PAGE_SHIFT);
87
88 /*
89 * Do magic if no mem on local node! XXX
90 */
91 if (!page)
92 return 0;
93 tmp = page_address(page);
94
95 /*
96 * There's a small race here where it's possible for someone to
97 * access the page through /dev/mem halfway through the conversion
98 * to uncached - not sure it's really worth bothering about
99 */
100 for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++)
101 SetPageUncached(&page[i]);
102
103 flush_tlb_kernel_range(tmp, tmp + IA64_GRANULE_SIZE);
104
105 status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
106
107 dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n",
108 status, get_cpu());
109
110 if (!status) {
111 status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1);
112 if (status)
113 printk(KERN_WARNING "smp_call_function failed for "
114 "uncached_ipi_visibility! (%i)\n", status);
115 }
116
117 if (ia64_platform_is("sn2"))
118 sn_flush_all_caches((unsigned long)tmp, IA64_GRANULE_SIZE);
119 else
120 flush_icache_range((unsigned long)tmp,
121 (unsigned long)tmp+IA64_GRANULE_SIZE);
122
123 ia64_pal_mc_drain();
124 status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1);
125 if (status)
126 printk(KERN_WARNING "smp_call_function failed for "
127 "uncached_ipi_mc_drain! (%i)\n", status);
128
129 addr = (unsigned long)tmp - PAGE_OFFSET + __IA64_UNCACHED_OFFSET;
130
131 allocated_granules++;
132 return addr;
133}
134
135
136/*
137 * uncached_alloc_page
138 *
139 * Allocate 1 uncached page. Allocates on the requested node. If no
140 * uncached pages are available on the requested node, roundrobin starting
141 * with higher nodes.
142 */
143unsigned long
144uncached_alloc_page(int nid)
145{
146 unsigned long maddr;
147
148 maddr = gen_pool_alloc(uncached_pool[nid], PAGE_SIZE);
149
150 dprintk(KERN_DEBUG "uncached_alloc_page returns %lx on node %i\n",
151 maddr, nid);
152
153 /*
154 * If no memory is availble on our local node, try the
155 * remaining nodes in the system.
156 */
157 if (!maddr) {
158 int i;
159
160 for (i = MAX_NUMNODES - 1; i >= 0; i--) {
161 if (i == nid || !node_online(i))
162 continue;
163 maddr = gen_pool_alloc(uncached_pool[i], PAGE_SIZE);
164 dprintk(KERN_DEBUG "uncached_alloc_page alternate search "
165 "returns %lx on node %i\n", maddr, i);
166 if (maddr) {
167 break;
168 }
169 }
170 }
171
172 return maddr;
173}
174EXPORT_SYMBOL(uncached_alloc_page);
175
176
177/*
178 * uncached_free_page
179 *
180 * Free a single uncached page.
181 */
182void
183uncached_free_page(unsigned long maddr)
184{
185 int node;
186
187 node = nasid_to_cnodeid(NASID_GET(maddr));
188
189 dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node);
190
191 if ((maddr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET)
192 panic("uncached_free_page invalid address %lx\n", maddr);
193
194 gen_pool_free(uncached_pool[node], maddr, PAGE_SIZE);
195}
196EXPORT_SYMBOL(uncached_free_page);
197
198
199/*
200 * uncached_build_memmap,
201 *
202 * Called at boot time to build a map of pages that can be used for
203 * memory special operations.
204 */
205static int __init
206uncached_build_memmap(unsigned long start, unsigned long end, void *arg)
207{
208 long length;
209 unsigned long vstart, vend;
210 int node;
211
212 length = end - start;
213 vstart = start + __IA64_UNCACHED_OFFSET;
214 vend = end + __IA64_UNCACHED_OFFSET;
215
216 dprintk(KERN_ERR "uncached_build_memmap(%lx %lx)\n", start, end);
217
218 memset((char *)vstart, 0, length);
219
220 node = nasid_to_cnodeid(NASID_GET(start));
221
222 for (; vstart < vend ; vstart += PAGE_SIZE) {
223 dprintk(KERN_INFO "sticking %lx into the pool!\n", vstart);
224 gen_pool_free(uncached_pool[node], vstart, PAGE_SIZE);
225 }
226
227 return 0;
228}
229
230
231static int __init uncached_init(void) {
232 int i;
233
234 for (i = 0; i < MAX_NUMNODES; i++) {
235 if (!node_online(i))
236 continue;
237 uncached_pool[i] = gen_pool_create(0, IA64_GRANULE_SHIFT,
238 &uncached_get_new_chunk, i);
239 }
240
241 efi_memmap_walk_uc(uncached_build_memmap);
242
243 return 0;
244}
245
246__initcall(uncached_init);
diff --git a/include/asm-ia64/uncached.h b/include/asm-ia64/uncached.h
new file mode 100644
index 000000000000..b82d923b73c1
--- /dev/null
+++ b/include/asm-ia64/uncached.h
@@ -0,0 +1,12 @@
1/*
2 * Copyright (C) 2001-2005 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
6 * as published by the Free Software Foundation.
7 *
8 * Prototypes for the uncached page allocator
9 */
10
11extern unsigned long uncached_alloc_page(int nid);
12extern void uncached_free_page(unsigned long);
diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h
new file mode 100644
index 000000000000..7fd0576a4454
--- /dev/null
+++ b/include/linux/genalloc.h
@@ -0,0 +1,40 @@
1/*
2 * Basic general purpose allocator for managing special purpose memory
3 * not managed by the regular kmalloc/kfree interface.
4 * Uses for this includes on-device special memory, uncached memory
5 * etc.
6 *
7 * This code is based on the buddy allocator found in the sym53c8xx_2
8 * driver, adapted for general purpose use.
9 *
10 * This source code is licensed under the GNU General Public License,
11 * Version 2. See the file COPYING for more details.
12 */
13
14#include <linux/spinlock.h>
15
16#define ALLOC_MIN_SHIFT 5 /* 32 bytes minimum */
17/*
18 * Link between free memory chunks of a given size.
19 */
20struct gen_pool_link {
21 struct gen_pool_link *next;
22};
23
24/*
25 * Memory pool descriptor.
26 */
27struct gen_pool {
28 spinlock_t lock;
29 unsigned long (*get_new_chunk)(struct gen_pool *);
30 struct gen_pool *next;
31 struct gen_pool_link *h;
32 unsigned long private;
33 int max_chunk_shift;
34};
35
36unsigned long gen_pool_alloc(struct gen_pool *poolp, int size);
37void gen_pool_free(struct gen_pool *mp, unsigned long ptr, int size);
38struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift,
39 unsigned long (*fp)(struct gen_pool *),
40 unsigned long data);
diff --git a/lib/Kconfig b/lib/Kconfig
index eeb45225248f..2d4d4e3bc4aa 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -40,6 +40,12 @@ config ZLIB_DEFLATE
40 tristate 40 tristate
41 41
42# 42#
43# Generic allocator support is selected if needed
44#
45config GENERIC_ALLOCATOR
46 boolean
47
48#
43# reed solomon support is select'ed if needed 49# reed solomon support is select'ed if needed
44# 50#
45config REED_SOLOMON 51config REED_SOLOMON
diff --git a/lib/Makefile b/lib/Makefile
index 5f10cb898407..dcb4231916e2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
30obj-$(CONFIG_CRC32) += crc32.o 30obj-$(CONFIG_CRC32) += crc32.o
31obj-$(CONFIG_LIBCRC32C) += libcrc32c.o 31obj-$(CONFIG_LIBCRC32C) += libcrc32c.o
32obj-$(CONFIG_GENERIC_IOMAP) += iomap.o 32obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
33obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
33 34
34obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/ 35obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/
35obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/ 36obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/
diff --git a/lib/genalloc.c b/lib/genalloc.c
new file mode 100644
index 000000000000..d6d30d2e7166
--- /dev/null
+++ b/lib/genalloc.c
@@ -0,0 +1,188 @@
1/*
2 * Basic general purpose allocator for managing special purpose memory
3 * not managed by the regular kmalloc/kfree interface.
4 * Uses for this includes on-device special memory, uncached memory
5 * etc.
6 *
7 * This code is based on the buddy allocator found in the sym53c8xx_2
8 * driver Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>,
9 * and adapted for general purpose use.
10 *
11 * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
12 *
13 * This source code is licensed under the GNU General Public License,
14 * Version 2. See the file COPYING for more details.
15 */
16
17#include <linux/module.h>
18#include <linux/stddef.h>
19#include <linux/kernel.h>
20#include <linux/string.h>
21#include <linux/slab.h>
22#include <linux/init.h>
23#include <linux/mm.h>
24#include <linux/spinlock.h>
25#include <linux/genalloc.h>
26
27#include <asm/page.h>
28
29
30struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift,
31 unsigned long (*fp)(struct gen_pool *),
32 unsigned long data)
33{
34 struct gen_pool *poolp;
35 unsigned long tmp;
36 int i;
37
38 /*
39 * This is really an arbitrary limit, +10 is enough for
40 * IA64_GRANULE_SHIFT, aka 16MB. If anyone needs a large limit
41 * this can be increased without problems.
42 */
43 if ((max_chunk_shift > (PAGE_SHIFT + 10)) ||
44 ((max_chunk_shift < ALLOC_MIN_SHIFT) && max_chunk_shift))
45 return NULL;
46
47 if (!max_chunk_shift)
48 max_chunk_shift = PAGE_SHIFT;
49
50 poolp = kmalloc(sizeof(struct gen_pool), GFP_KERNEL);
51 if (!poolp)
52 return NULL;
53 memset(poolp, 0, sizeof(struct gen_pool));
54 poolp->h = kmalloc(sizeof(struct gen_pool_link) *
55 (max_chunk_shift - ALLOC_MIN_SHIFT + 1),
56 GFP_KERNEL);
57 if (!poolp->h) {
58 printk(KERN_WARNING "gen_pool_alloc() failed to allocate\n");
59 kfree(poolp);
60 return NULL;
61 }
62 memset(poolp->h, 0, sizeof(struct gen_pool_link) *
63 (max_chunk_shift - ALLOC_MIN_SHIFT + 1));
64
65 spin_lock_init(&poolp->lock);
66 poolp->get_new_chunk = fp;
67 poolp->max_chunk_shift = max_chunk_shift;
68 poolp->private = data;
69
70 for (i = 0; i < nr_chunks; i++) {
71 tmp = poolp->get_new_chunk(poolp);
72 printk(KERN_INFO "allocated %lx\n", tmp);
73 if (!tmp)
74 break;
75 gen_pool_free(poolp, tmp, (1 << poolp->max_chunk_shift));
76 }
77
78 return poolp;
79}
80EXPORT_SYMBOL(gen_pool_create);
81
82
83/*
84 * Simple power of two buddy-like generic allocator.
85 * Provides naturally aligned memory chunks.
86 */
87unsigned long gen_pool_alloc(struct gen_pool *poolp, int size)
88{
89 int j, i, s, max_chunk_size;
90 unsigned long a, flags;
91 struct gen_pool_link *h = poolp->h;
92
93 max_chunk_size = 1 << poolp->max_chunk_shift;
94
95 if (size > max_chunk_size)
96 return 0;
97
98 i = 0;
99
100 size = max(size, 1 << ALLOC_MIN_SHIFT);
101 s = roundup_pow_of_two(size);
102
103 j = i;
104
105 spin_lock_irqsave(&poolp->lock, flags);
106 while (!h[j].next) {
107 if (s == max_chunk_size) {
108 struct gen_pool_link *ptr;
109 spin_unlock_irqrestore(&poolp->lock, flags);
110 ptr = (struct gen_pool_link *)poolp->get_new_chunk(poolp);
111 spin_lock_irqsave(&poolp->lock, flags);
112 h[j].next = ptr;
113 if (h[j].next)
114 h[j].next->next = NULL;
115 break;
116 }
117 j++;
118 s <<= 1;
119 }
120 a = (unsigned long) h[j].next;
121 if (a) {
122 h[j].next = h[j].next->next;
123 /*
124 * This should be split into a seperate function doing
125 * the chunk split in order to support custom
126 * handling memory not physically accessible by host
127 */
128 while (j > i) {
129 j -= 1;
130 s >>= 1;
131 h[j].next = (struct gen_pool_link *) (a + s);
132 h[j].next->next = NULL;
133 }
134 }
135 spin_unlock_irqrestore(&poolp->lock, flags);
136 return a;
137}
138EXPORT_SYMBOL(gen_pool_alloc);
139
140
141/*
142 * Counter-part of the generic allocator.
143 */
144void gen_pool_free(struct gen_pool *poolp, unsigned long ptr, int size)
145{
146 struct gen_pool_link *q;
147 struct gen_pool_link *h = poolp->h;
148 unsigned long a, b, flags;
149 int i, s, max_chunk_size;
150
151 max_chunk_size = 1 << poolp->max_chunk_shift;
152
153 if (size > max_chunk_size)
154 return;
155
156 i = 0;
157
158 size = max(size, 1 << ALLOC_MIN_SHIFT);
159 s = roundup_pow_of_two(size);
160
161 a = ptr;
162
163 spin_lock_irqsave(&poolp->lock, flags);
164 while (1) {
165 if (s == max_chunk_size) {
166 ((struct gen_pool_link *)a)->next = h[i].next;
167 h[i].next = (struct gen_pool_link *)a;
168 break;
169 }
170 b = a ^ s;
171 q = &h[i];
172
173 while (q->next && q->next != (struct gen_pool_link *)b)
174 q = q->next;
175
176 if (!q->next) {
177 ((struct gen_pool_link *)a)->next = h[i].next;
178 h[i].next = (struct gen_pool_link *)a;
179 break;
180 }
181 q->next = q->next->next;
182 a = a & b;
183 s <<= 1;
184 i++;
185 }
186 spin_unlock_irqrestore(&poolp->lock, flags);
187}
188EXPORT_SYMBOL(gen_pool_free);