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>
This commit is contained in:
Matt Fleming 2009-10-06 21:22:22 +00:00 committed by Paul Mundt
parent a6325247f5
commit fc2bdefdde

View file

@ -33,6 +33,8 @@
#define NR_PMB_ENTRIES 16 #define NR_PMB_ENTRIES 16
static void __pmb_unmap(struct pmb_entry *);
static struct kmem_cache *pmb_cache; static struct kmem_cache *pmb_cache;
static unsigned long pmb_map; static unsigned long pmb_map;
@ -218,9 +220,10 @@ static struct {
long pmb_remap(unsigned long vaddr, unsigned long phys, long pmb_remap(unsigned long vaddr, unsigned long phys,
unsigned long size, unsigned long flags) unsigned long size, unsigned long flags)
{ {
struct pmb_entry *pmbp; struct pmb_entry *pmbp, *pmbe;
unsigned long wanted; unsigned long wanted;
int pmb_flags, i; int pmb_flags, i;
long err;
/* Convert typical pgprot value to the PMB equivalent */ /* Convert typical pgprot value to the PMB equivalent */
if (flags & _PAGE_CACHABLE) { if (flags & _PAGE_CACHABLE) {
@ -236,20 +239,22 @@ long pmb_remap(unsigned long vaddr, unsigned long phys,
again: again:
for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) { for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) {
struct pmb_entry *pmbe;
int ret; int ret;
if (size < pmb_sizes[i].size) if (size < pmb_sizes[i].size)
continue; continue;
pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag); pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag);
if (IS_ERR(pmbe)) if (IS_ERR(pmbe)) {
return PTR_ERR(pmbe); err = PTR_ERR(pmbe);
goto out;
}
ret = set_pmb_entry(pmbe); ret = set_pmb_entry(pmbe);
if (ret != 0) { if (ret != 0) {
pmb_free(pmbe); pmb_free(pmbe);
return -EBUSY; err = -EBUSY;
goto out;
} }
phys += pmb_sizes[i].size; phys += pmb_sizes[i].size;
@ -270,6 +275,12 @@ long pmb_remap(unsigned long vaddr, unsigned long phys,
goto again; goto again;
return wanted - size; return wanted - size;
out:
if (pmbp)
__pmb_unmap(pmbp);
return err;
} }
void pmb_unmap(unsigned long addr) void pmb_unmap(unsigned long addr)
@ -283,12 +294,19 @@ void pmb_unmap(unsigned long addr)
if (unlikely(!pmbe)) if (unlikely(!pmbe))
return; return;
__pmb_unmap(pmbe);
}
static void __pmb_unmap(struct pmb_entry *pmbe)
{
WARN_ON(!test_bit(pmbe->entry, &pmb_map)); WARN_ON(!test_bit(pmbe->entry, &pmb_map));
do { do {
struct pmb_entry *pmblink = pmbe; struct pmb_entry *pmblink = pmbe;
clear_pmb_entry(pmbe); if (pmbe->entry != PMB_NO_ENTRY)
clear_pmb_entry(pmbe);
pmbe = pmblink->link; pmbe = pmblink->link;
pmb_free(pmblink); pmb_free(pmblink);