From 073b4964b3b75fd9e19bf3933b26d9c23591c9db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:37:31 +0100 Subject: ACPI: Do not export functions that are only used in osl.c The functions acpi_os_map_generic_address() and acpi_os_unmap_generic_address() are only used in drivers/acpi/osl.c, so make them static and remove the extern definitions of them from include/linux/acpi_io.h. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c90c76aa7f8b..ff2189d3fa06 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -397,7 +397,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } -int acpi_os_map_generic_address(struct acpi_generic_address *addr) +static int acpi_os_map_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; @@ -413,9 +413,8 @@ int acpi_os_map_generic_address(struct acpi_generic_address *addr) return 0; } -EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) +static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; unsigned long flags; @@ -433,7 +432,6 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) acpi_os_unmap_memory(virt, size); } -EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); #ifdef ACPI_FUTURE_USAGE acpi_status -- cgit v1.2.3 From 7bbb890358b96cb6f77adc6815f2072bdf813d5d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:37:42 +0100 Subject: ACPI: Change acpi_ioremap_lock into a mutex There's no reason why acpi_ioremap_lock has to be a spinlock, because all of the functions it is used in may sleep anyway and there's no reason why it should be locked with interrupts off. Use a mutex instead (that's going to allow us to put some more operations under the lock later). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index ff2189d3fa06..97061928249a 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -109,7 +109,7 @@ struct acpi_ioremap { }; static LIST_HEAD(acpi_ioremaps); -static DEFINE_SPINLOCK(acpi_ioremap_lock); +static DEFINE_MUTEX(acpi_ioremap_lock); static void __init acpi_osi_setup_late(void); @@ -303,7 +303,6 @@ void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { struct acpi_ioremap *map, *tmp_map; - unsigned long flags; void __iomem *virt; acpi_physical_address pg_off; acpi_size pg_sz; @@ -334,18 +333,18 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) map->size = pg_sz; kref_init(&map->ref); - spin_lock_irqsave(&acpi_ioremap_lock, flags); + mutex_lock(&acpi_ioremap_lock); /* Check if page has already been mapped. */ tmp_map = acpi_map_lookup(phys, size); if (tmp_map) { kref_get(&tmp_map->ref); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_unlock(&acpi_ioremap_lock); iounmap(map->virt); kfree(map); return tmp_map->virt + (phys - tmp_map->phys); } list_add_tail_rcu(&map->list, &acpi_ioremaps); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_unlock(&acpi_ioremap_lock); return map->virt + (phys - map->phys); } @@ -362,7 +361,6 @@ static void acpi_kref_del_iomap(struct kref *ref) void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { struct acpi_ioremap *map; - unsigned long flags; int del; if (!acpi_gbl_permanent_mmap) { @@ -370,17 +368,17 @@ void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) return; } - spin_lock_irqsave(&acpi_ioremap_lock, flags); + mutex_lock(&acpi_ioremap_lock); map = acpi_map_lookup_virt(virt, size); if (!map) { - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_unlock(&acpi_ioremap_lock); printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); dump_stack(); return; } del = kref_put(&map->ref, acpi_kref_del_iomap); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_unlock(&acpi_ioremap_lock); if (!del) return; @@ -417,7 +415,6 @@ static int acpi_os_map_generic_address(struct acpi_generic_address *addr) static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; - unsigned long flags; acpi_size size = addr->bit_width / 8; if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) @@ -426,9 +423,9 @@ static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) if (!addr->address || !addr->bit_width) return; - spin_lock_irqsave(&acpi_ioremap_lock, flags); + mutex_lock(&acpi_ioremap_lock); virt = acpi_map_vaddr_lookup(addr->address, size); - spin_unlock_irqrestore(&acpi_ioremap_lock, flags); + mutex_unlock(&acpi_ioremap_lock); acpi_os_unmap_memory(virt, size); } -- cgit v1.2.3 From 7fe135dc058faea0ce319a03e3b6f98c5049955c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:37:53 +0100 Subject: ACPI: Avoid walking the list of memory mappings in osl.c twice in a row Make acpi_os_unmap_generic_address() use acpi_map_lookup() to find the desired iomap and drop the reference to it directly (and eventually remove it if necessary) instead of calling acpi_os_unmap_memory(), which requires us to walk the list of ACPI iomaps twice in a row (first, to get the virtual address associated with the iomap and second, to get the iomap itself). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 97061928249a..f6785bced215 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -358,6 +358,13 @@ static void acpi_kref_del_iomap(struct kref *ref) list_del_rcu(&map->list); } +static void acpi_os_remove_map(struct acpi_ioremap *map) +{ + synchronize_rcu(); + iounmap(map->virt); + kfree(map); +} + void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { struct acpi_ioremap *map; @@ -372,20 +379,14 @@ void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) map = acpi_map_lookup_virt(virt, size); if (!map) { mutex_unlock(&acpi_ioremap_lock); - printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); - dump_stack(); + WARN(true, PREFIX "%s: bad address %p\n", __func__, virt); return; } - del = kref_put(&map->ref, acpi_kref_del_iomap); mutex_unlock(&acpi_ioremap_lock); - if (!del) - return; - - synchronize_rcu(); - iounmap(map->virt); - kfree(map); + if (del) + acpi_os_remove_map(map); } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); @@ -414,8 +415,8 @@ static int acpi_os_map_generic_address(struct acpi_generic_address *addr) static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { - void __iomem *virt; - acpi_size size = addr->bit_width / 8; + struct acpi_ioremap *map; + int del; if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return; @@ -424,10 +425,16 @@ static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) return; mutex_lock(&acpi_ioremap_lock); - virt = acpi_map_vaddr_lookup(addr->address, size); + map = acpi_map_lookup(addr->address, addr->bit_width / 8); + if (!map) { + mutex_unlock(&acpi_ioremap_lock); + return; + } + del = kref_put(&map->ref, acpi_kref_del_iomap); mutex_unlock(&acpi_ioremap_lock); - acpi_os_unmap_memory(virt, size); + if (del) + acpi_os_remove_map(map); } #ifdef ACPI_FUTURE_USAGE -- cgit v1.2.3 From 7ffd0443f2502478545e23e194b7eb8e16376072 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:38:05 +0100 Subject: ACPI: Make acpi_os_map_memory() avoid creating unnecessary mappings Modify acpi_os_map_memory() so that it doesn't call acpi_os_ioremap() unconditionally every time it is executed (except when acpi_gbl_permanent_mmap is unset), which pretty much defeats the purpose of maintaining the list of ACPI iomaps in osl.c. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f6785bced215..445f205752a2 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -302,7 +302,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { - struct acpi_ioremap *map, *tmp_map; + struct acpi_ioremap *map; void __iomem *virt; acpi_physical_address pg_off; acpi_size pg_sz; @@ -315,14 +315,25 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) if (!acpi_gbl_permanent_mmap) return __acpi_map_table((unsigned long)phys, size); + mutex_lock(&acpi_ioremap_lock); + /* Check if there's a suitable mapping already. */ + map = acpi_map_lookup(phys, size); + if (map) { + kref_get(&map->ref); + goto out; + } + map = kzalloc(sizeof(*map), GFP_KERNEL); - if (!map) + if (!map) { + mutex_unlock(&acpi_ioremap_lock); return NULL; + } pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; virt = acpi_os_ioremap(pg_off, pg_sz); if (!virt) { + mutex_unlock(&acpi_ioremap_lock); kfree(map); return NULL; } @@ -333,19 +344,10 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) map->size = pg_sz; kref_init(&map->ref); - mutex_lock(&acpi_ioremap_lock); - /* Check if page has already been mapped. */ - tmp_map = acpi_map_lookup(phys, size); - if (tmp_map) { - kref_get(&tmp_map->ref); - mutex_unlock(&acpi_ioremap_lock); - iounmap(map->virt); - kfree(map); - return tmp_map->virt + (phys - tmp_map->phys); - } list_add_tail_rcu(&map->list, &acpi_ioremaps); - mutex_unlock(&acpi_ioremap_lock); + out: + mutex_unlock(&acpi_ioremap_lock); return map->virt + (phys - map->phys); } EXPORT_SYMBOL_GPL(acpi_os_map_memory); -- cgit v1.2.3 From b7c1fadd6c2eead56d0664a3a921980120de0c11 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:38:15 +0100 Subject: ACPI: Do not use krefs under a mutex in osl.c The reference counting of ACPI iomaps is carried out entirely under acpi_ioremap_lock, so it is sufficient to use simple counters instead of krefs for this purpose. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 445f205752a2..5389f9f2e2ff 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -105,7 +105,7 @@ struct acpi_ioremap { void __iomem *virt; acpi_physical_address phys; acpi_size size; - struct kref ref; + unsigned long refcount; }; static LIST_HEAD(acpi_ioremaps); @@ -319,7 +319,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) /* Check if there's a suitable mapping already. */ map = acpi_map_lookup(phys, size); if (map) { - kref_get(&map->ref); + map->refcount++; goto out; } @@ -342,7 +342,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) map->virt = virt; map->phys = pg_off; map->size = pg_sz; - kref_init(&map->ref); + map->refcount = 1; list_add_tail_rcu(&map->list, &acpi_ioremaps); @@ -352,25 +352,24 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) } EXPORT_SYMBOL_GPL(acpi_os_map_memory); -static void acpi_kref_del_iomap(struct kref *ref) +static void acpi_os_drop_map_ref(struct acpi_ioremap *map) { - struct acpi_ioremap *map; - - map = container_of(ref, struct acpi_ioremap, ref); - list_del_rcu(&map->list); + if (!--map->refcount) + list_del_rcu(&map->list); } -static void acpi_os_remove_map(struct acpi_ioremap *map) +static void acpi_os_map_cleanup(struct acpi_ioremap *map) { - synchronize_rcu(); - iounmap(map->virt); - kfree(map); + if (!map->refcount) { + synchronize_rcu(); + iounmap(map->virt); + kfree(map); + } } void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) { struct acpi_ioremap *map; - int del; if (!acpi_gbl_permanent_mmap) { __acpi_unmap_table(virt, size); @@ -384,11 +383,10 @@ void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) WARN(true, PREFIX "%s: bad address %p\n", __func__, virt); return; } - del = kref_put(&map->ref, acpi_kref_del_iomap); + acpi_os_drop_map_ref(map); mutex_unlock(&acpi_ioremap_lock); - if (del) - acpi_os_remove_map(map); + acpi_os_map_cleanup(map); } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); @@ -418,7 +416,6 @@ static int acpi_os_map_generic_address(struct acpi_generic_address *addr) static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { struct acpi_ioremap *map; - int del; if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return; @@ -432,11 +429,10 @@ static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) mutex_unlock(&acpi_ioremap_lock); return; } - del = kref_put(&map->ref, acpi_kref_del_iomap); + acpi_os_drop_map_ref(map); mutex_unlock(&acpi_ioremap_lock); - if (del) - acpi_os_remove_map(map); + acpi_os_map_cleanup(map); } #ifdef ACPI_FUTURE_USAGE -- cgit v1.2.3 From 13606a2de1996f8d83a9ce296f74022bdbadf712 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:38:25 +0100 Subject: ACPI: Introduce acpi_os_get_iomem() Introduce function acpi_os_get_iomem() that may be used by its callers to get a reference to an ACPI iomap. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5389f9f2e2ff..52ca8721bc9c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -285,6 +285,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) return NULL; } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + void __iomem *virt = NULL; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(phys, size); + if (map) { + virt = map->virt + (phys - map->phys); + map->refcount++; + } + mutex_unlock(&acpi_ioremap_lock); + return virt; +} +EXPORT_SYMBOL_GPL(acpi_os_get_iomem); + /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ static struct acpi_ioremap * acpi_map_lookup_virt(void __iomem *virt, acpi_size size) -- cgit v1.2.3 From 23fe36306ea489eef7dd88506bdcefdc8da39c91 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:48:16 +0100 Subject: ACPI: Avoid calling request_irq() many times for the same interrupt In principle acpi_os_install_interrupt_handler() may be called multiple times for different interrupts, either from acpi_ev_get_gpe_xrupt_block(), or from acpi_ev_install_sci_handler(). However, it always attempts to request the same interrupt, acpi_gbl_FADT.sci_interrupt and it doesn't check whether or not this interrupt has already been requested. Modify this function so that it refuses to request interrupts other than acpi_gbl_FADT.sci_interrupt and change acpi_os_remove_interrupt_handler() so that it refuses to free such interrupts. Use the observation that the only supported ACPI interrupt must be equal to acpi_gbl_FADT.sci_interrupt and drop an unnecessary variable acpi_irq_irq. This change has been tested on Toshiba Portege R500 and HP nx6325 without introducing any visible problems. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/osl.c') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c90c76aa7f8b..187dff96356b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -76,7 +76,6 @@ EXPORT_SYMBOL(acpi_in_debugger); extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ -static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; @@ -516,11 +515,15 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_stats_init(); /* - * Ignore the GSI from the core, and use the value in our copy of the - * FADT. It may not be the same if an interrupt source override exists - * for the SCI. + * ACPI interrupts different from the SCI in our copy of the FADT are + * not supported. */ - gsi = acpi_gbl_FADT.sci_interrupt; + if (gsi != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + if (acpi_irq_handler) + return AE_ALREADY_ACQUIRED; + if (acpi_gsi_to_irq(gsi, &irq) < 0) { printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", gsi); @@ -531,20 +534,20 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_context = context; if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); + acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; } - acpi_irq_irq = irq; return AE_OK; } acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) { - if (irq) { - free_irq(irq, acpi_irq); - acpi_irq_handler = NULL; - acpi_irq_irq = 0; - } + if (irq != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + free_irq(irq, acpi_irq); + acpi_irq_handler = NULL; return AE_OK; } @@ -1603,7 +1606,7 @@ acpi_status __init acpi_os_initialize1(void) acpi_status acpi_os_terminate(void) { if (acpi_irq_handler) { - acpi_os_remove_interrupt_handler(acpi_irq_irq, + acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt, acpi_irq_handler); } -- cgit v1.2.3