aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh
diff options
context:
space:
mode:
authorMatt Fleming <matt@console-pimps.org>2009-10-06 17:22:22 -0400
committerPaul Mundt <lethal@linux-sh.org>2009-10-08 22:24:09 -0400
commitfc2bdefdde89b54d8fcde7bbf7d0adc0ce5cb044 (patch)
treefbf6f291796ca5f90473d30d5fe0dbd4acbcaa18 /arch/sh
parenta6325247f50628c7e53a483807d0ef2c24a7aa90 (diff)
sh: Plug PMB alloc memory leak
If we fail to allocate a PMB entry in pmb_remap() we must remember to clear and free any PMB entries that we may have previously allocated, e.g. if we were allocating a multiple entry mapping. Signed-off-by: Matt Fleming <matt@console-pimps.org> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-rw-r--r--arch/sh/mm/pmb.c30
1 files changed, 24 insertions, 6 deletions
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c
index b1a714a92b1..58f935896b4 100644
--- a/arch/sh/mm/pmb.c
+++ b/arch/sh/mm/pmb.c
@@ -33,6 +33,8 @@
33 33
34#define NR_PMB_ENTRIES 16 34#define NR_PMB_ENTRIES 16
35 35
36static void __pmb_unmap(struct pmb_entry *);
37
36static struct kmem_cache *pmb_cache; 38static struct kmem_cache *pmb_cache;
37static unsigned long pmb_map; 39static unsigned long pmb_map;
38 40
@@ -218,9 +220,10 @@ static struct {
218long pmb_remap(unsigned long vaddr, unsigned long phys, 220long pmb_remap(unsigned long vaddr, unsigned long phys,
219 unsigned long size, unsigned long flags) 221 unsigned long size, unsigned long flags)
220{ 222{
221 struct pmb_entry *pmbp; 223 struct pmb_entry *pmbp, *pmbe;
222 unsigned long wanted; 224 unsigned long wanted;
223 int pmb_flags, i; 225 int pmb_flags, i;
226 long err;
224 227
225 /* Convert typical pgprot value to the PMB equivalent */ 228 /* Convert typical pgprot value to the PMB equivalent */
226 if (flags & _PAGE_CACHABLE) { 229 if (flags & _PAGE_CACHABLE) {
@@ -236,20 +239,22 @@ long pmb_remap(unsigned long vaddr, unsigned long phys,
236 239
237again: 240again:
238 for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) { 241 for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) {
239 struct pmb_entry *pmbe;
240 int ret; 242 int ret;
241 243
242 if (size < pmb_sizes[i].size) 244 if (size < pmb_sizes[i].size)
243 continue; 245 continue;
244 246
245 pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag); 247 pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag);
246 if (IS_ERR(pmbe)) 248 if (IS_ERR(pmbe)) {
247 return PTR_ERR(pmbe); 249 err = PTR_ERR(pmbe);
250 goto out;
251 }
248 252
249 ret = set_pmb_entry(pmbe); 253 ret = set_pmb_entry(pmbe);
250 if (ret != 0) { 254 if (ret != 0) {
251 pmb_free(pmbe); 255 pmb_free(pmbe);
252 return -EBUSY; 256 err = -EBUSY;
257 goto out;
253 } 258 }
254 259
255 phys += pmb_sizes[i].size; 260 phys += pmb_sizes[i].size;
@@ -270,6 +275,12 @@ again:
270 goto again; 275 goto again;
271 276
272 return wanted - size; 277 return wanted - size;
278
279out:
280 if (pmbp)
281 __pmb_unmap(pmbp);
282
283 return err;
273} 284}
274 285
275void pmb_unmap(unsigned long addr) 286void pmb_unmap(unsigned long addr)
@@ -283,12 +294,19 @@ void pmb_unmap(unsigned long addr)
283 if (unlikely(!pmbe)) 294 if (unlikely(!pmbe))
284 return; 295 return;
285 296
297 __pmb_unmap(pmbe);
298}
299
300static void __pmb_unmap(struct pmb_entry *pmbe)
301{
286 WARN_ON(!test_bit(pmbe->entry, &pmb_map)); 302 WARN_ON(!test_bit(pmbe->entry, &pmb_map));
287 303
288 do { 304 do {
289 struct pmb_entry *pmblink = pmbe; 305 struct pmb_entry *pmblink = pmbe;
290 306
291 clear_pmb_entry(pmbe); 307 if (pmbe->entry != PMB_NO_ENTRY)
308 clear_pmb_entry(pmbe);
309
292 pmbe = pmblink->link; 310 pmbe = pmblink->link;
293 311
294 pmb_free(pmblink); 312 pmb_free(pmblink);