Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8275f96 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Quoth Len:
 "This fixes a merge-window regression due to a conflict
  between error injection and preparation to remove atomicio.c
  Here we fix that regression and complete the removal
  of atomicio.c.

  This also re-orders some idle initialization code to
  complete the merge window series that allows cpuidle
  to cope with bringing processors on-line after boot."

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux:
  Use acpi_os_map_memory() instead of ioremap() in einj driver
  ACPI, APEI, EINJ, cleanup 0 vs NULL confusion
  ACPI, APEI, EINJ Allow empty Trigger Error Action Table
  thermal: Rename generate_netlink_event
  ACPI / PM: Add Sony Vaio VPCCW29FX to nonvs blacklist.
  ACPI: Remove ./drivers/acpi/atomicio.[ch]
  ACPI, APEI: Add RAM mapping support to ACPI
  ACPI, APEI: Add 64-bit read/write support for APEI on i386
  ACPI processor hotplug: Delay acpi_processor_start() call for hotplugged cores
  ACPI processor hotplug: Split up acpi_processor_add
parents a86b4ad6 eb7004e6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -284,7 +284,7 @@ method, the sys I/F structure will be built like this:
The framework includes a simple notification mechanism, in the form of a
netlink event. Netlink socket initialization is done during the _init_
of the framework. Drivers which intend to use the notification mechanism
just need to call generate_netlink_event() with two arguments viz
just need to call thermal_generate_netlink_event() with two arguments viz
(originator, event). Typically the originator will be an integer assigned
to a thermal_zone_device when it registers itself with the framework. The
event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL,
+0 −1
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ obj-y += acpi.o \

# All the builtin files are in the "acpi." module_param namespace.
acpi-y				+= osl.o utils.o reboot.o
acpi-y				+= atomicio.o
acpi-y				+= nvs.o

# sleep related files
+4 −31
Original line number Diff line number Diff line
@@ -596,33 +596,19 @@ int apei_read(u64 *val, struct acpi_generic_address *reg)
{
	int rc;
	u64 address;
	u32 tmp, width = reg->bit_width;
	acpi_status status;

	rc = apei_check_gar(reg, &address);
	if (rc)
		return rc;

	if (width == 64)
		width = 32;	/* Break into two 32-bit transfers */

	*val = 0;
	switch(reg->space_id) {
	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
		status = acpi_os_read_memory((acpi_physical_address)
					     address, &tmp, width);
		status = acpi_os_read_memory64((acpi_physical_address)
					     address, val, reg->bit_width);
		if (ACPI_FAILURE(status))
			return -EIO;
		*val = tmp;

		if (reg->bit_width == 64) {
			/* Read the top 32 bits */
			status = acpi_os_read_memory((acpi_physical_address)
						     (address + 4), &tmp, 32);
			if (ACPI_FAILURE(status))
				return -EIO;
			*val |= ((u64)tmp << 32);
		}
		break;
	case ACPI_ADR_SPACE_SYSTEM_IO:
		status = acpi_os_read_port(address, (u32 *)val, reg->bit_width);
@@ -642,31 +628,18 @@ int apei_write(u64 val, struct acpi_generic_address *reg)
{
	int rc;
	u64 address;
	u32 width = reg->bit_width;
	acpi_status status;

	rc = apei_check_gar(reg, &address);
	if (rc)
		return rc;

	if (width == 64)
		width = 32;	/* Break into two 32-bit transfers */

	switch (reg->space_id) {
	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
		status = acpi_os_write_memory((acpi_physical_address)
					      address, ACPI_LODWORD(val),
					      width);
		status = acpi_os_write_memory64((acpi_physical_address)
					      address, val, reg->bit_width);
		if (ACPI_FAILURE(status))
			return -EIO;

		if (reg->bit_width == 64) {
			status = acpi_os_write_memory((acpi_physical_address)
						      (address + 4),
						      ACPI_HIDWORD(val), 32);
			if (ACPI_FAILURE(status))
				return -EIO;
		}
		break;
	case ACPI_ADR_SPACE_SYSTEM_IO:
		status = acpi_os_write_port(address, val, reg->bit_width);
+47 −48
Original line number Diff line number Diff line
@@ -141,21 +141,6 @@ static DEFINE_MUTEX(einj_mutex);

static void *einj_param;

#ifndef readq
static inline __u64 readq(volatile void __iomem *addr)
{
	return ((__u64)readl(addr+4) << 32) + readl(addr);
}
#endif

#ifndef writeq
static inline void writeq(__u64 val, volatile void __iomem *addr)
{
	writel(val, addr);
	writel(val >> 32, addr+4);
}
#endif

static void einj_exec_ctx_init(struct apei_exec_context *ctx)
{
	apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type),
@@ -204,22 +189,21 @@ static int einj_timedout(u64 *t)
static void check_vendor_extension(u64 paddr,
				   struct set_error_type_with_address *v5param)
{
	int	offset = readl(&v5param->vendor_extension);
	int	offset = v5param->vendor_extension;
	struct	vendor_error_type_extension *v;
	u32	sbdf;

	if (!offset)
		return;
	v = ioremap(paddr + offset, sizeof(*v));
	v = acpi_os_map_memory(paddr + offset, sizeof(*v));
	if (!v)
		return;
	sbdf = readl(&v->pcie_sbdf);
	sbdf = v->pcie_sbdf;
	sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
		sbdf >> 24, (sbdf >> 16) & 0xff,
		(sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
		 readw(&v->vendor_id), readw(&v->device_id),
		readb(&v->rev_id));
	iounmap(v);
		 v->vendor_id, v->device_id, v->rev_id);
	acpi_os_unmap_memory(v, sizeof(*v));
}

static void *einj_get_parameter_address(void)
@@ -247,7 +231,7 @@ static void *einj_get_parameter_address(void)
	if (paddrv5) {
		struct set_error_type_with_address *v5param;

		v5param = ioremap(paddrv5, sizeof(*v5param));
		v5param = acpi_os_map_memory(paddrv5, sizeof(*v5param));
		if (v5param) {
			acpi5 = 1;
			check_vendor_extension(paddrv5, v5param);
@@ -257,17 +241,17 @@ static void *einj_get_parameter_address(void)
	if (paddrv4) {
		struct einj_parameter *v4param;

		v4param = ioremap(paddrv4, sizeof(*v4param));
		v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param));
		if (!v4param)
			return 0;
		if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) {
			iounmap(v4param);
			return 0;
			return NULL;
		if (v4param->reserved1 || v4param->reserved2) {
			acpi_os_unmap_memory(v4param, sizeof(*v4param));
			return NULL;
		}
		return v4param;
	}

	return 0;
	return NULL;
}

/* do sanity check to trigger table */
@@ -276,7 +260,7 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
	if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger))
		return -EINVAL;
	if (trigger_tab->table_size > PAGE_SIZE ||
	    trigger_tab->table_size <= trigger_tab->header_size)
	    trigger_tab->table_size < trigger_tab->header_size)
		return -EINVAL;
	if (trigger_tab->entry_count !=
	    (trigger_tab->table_size - trigger_tab->header_size) /
@@ -340,6 +324,11 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
			   "The trigger error action table is invalid\n");
		goto out_rel_header;
	}

	/* No action structures in the TRIGGER_ERROR table, nothing to do */
	if (!trigger_tab->entry_count)
		goto out_rel_header;

	rc = -EIO;
	table_size = trigger_tab->table_size;
	r = request_mem_region(trigger_paddr + sizeof(*trigger_tab),
@@ -435,41 +424,41 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
	if (acpi5) {
		struct set_error_type_with_address *v5param = einj_param;

		writel(type, &v5param->type);
		v5param->type = type;
		if (type & 0x80000000) {
			switch (vendor_flags) {
			case SETWA_FLAGS_APICID:
				writel(param1, &v5param->apicid);
				v5param->apicid = param1;
				break;
			case SETWA_FLAGS_MEM:
				writeq(param1, &v5param->memory_address);
				writeq(param2, &v5param->memory_address_range);
				v5param->memory_address = param1;
				v5param->memory_address_range = param2;
				break;
			case SETWA_FLAGS_PCIE_SBDF:
				writel(param1, &v5param->pcie_sbdf);
				v5param->pcie_sbdf = param1;
				break;
			}
			writel(vendor_flags, &v5param->flags);
			v5param->flags = vendor_flags;
		} else {
			switch (type) {
			case ACPI_EINJ_PROCESSOR_CORRECTABLE:
			case ACPI_EINJ_PROCESSOR_UNCORRECTABLE:
			case ACPI_EINJ_PROCESSOR_FATAL:
				writel(param1, &v5param->apicid);
				writel(SETWA_FLAGS_APICID, &v5param->flags);
				v5param->apicid = param1;
				v5param->flags = SETWA_FLAGS_APICID;
				break;
			case ACPI_EINJ_MEMORY_CORRECTABLE:
			case ACPI_EINJ_MEMORY_UNCORRECTABLE:
			case ACPI_EINJ_MEMORY_FATAL:
				writeq(param1, &v5param->memory_address);
				writeq(param2, &v5param->memory_address_range);
				writel(SETWA_FLAGS_MEM, &v5param->flags);
				v5param->memory_address = param1;
				v5param->memory_address_range = param2;
				v5param->flags = SETWA_FLAGS_MEM;
				break;
			case ACPI_EINJ_PCIX_CORRECTABLE:
			case ACPI_EINJ_PCIX_UNCORRECTABLE:
			case ACPI_EINJ_PCIX_FATAL:
				writel(param1, &v5param->pcie_sbdf);
				writel(SETWA_FLAGS_PCIE_SBDF, &v5param->flags);
				v5param->pcie_sbdf = param1;
				v5param->flags = SETWA_FLAGS_PCIE_SBDF;
				break;
			}
		}
@@ -479,8 +468,8 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
			return rc;
		if (einj_param) {
			struct einj_parameter *v4param = einj_param;
			writeq(param1, &v4param->param1);
			writeq(param2, &v4param->param2);
			v4param->param1 = param1;
			v4param->param2 = param2;
		}
	}
	rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
@@ -731,8 +720,13 @@ static int __init einj_init(void)
	return 0;

err_unmap:
	if (einj_param)
		iounmap(einj_param);
	if (einj_param) {
		acpi_size size = (acpi5) ?
			sizeof(struct set_error_type_with_address) :
			sizeof(struct einj_parameter);

		acpi_os_unmap_memory(einj_param, size);
	}
	apei_exec_post_unmap_gars(&ctx);
err_release:
	apei_resources_release(&einj_resources);
@@ -748,8 +742,13 @@ static void __exit einj_exit(void)
{
	struct apei_exec_context ctx;

	if (einj_param)
		iounmap(einj_param);
	if (einj_param) {
		acpi_size size = (acpi5) ?
			sizeof(struct set_error_type_with_address) :
			sizeof(struct einj_parameter);

		acpi_os_unmap_memory(einj_param, size);
	}
	einj_exec_ctx_init(&ctx);
	apei_exec_post_unmap_gars(&ctx);
	apei_resources_release(&einj_resources);

drivers/acpi/atomicio.c

deleted100644 → 0
+0 −422
Original line number Diff line number Diff line
/*
 * atomicio.c - ACPI IO memory pre-mapping/post-unmapping, then
 * accessing in atomic context.
 *
 * This is used for NMI handler to access IO memory area, because
 * ioremap/iounmap can not be used in NMI handler. The IO memory area
 * is pre-mapped in process context and accessed in NMI handler.
 *
 * Copyright (C) 2009-2010, Intel Corp.
 *	Author: Huang Ying <ying.huang@intel.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/kref.h>
#include <linux/rculist.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <acpi/atomicio.h>

#define ACPI_PFX "ACPI: "

static LIST_HEAD(acpi_iomaps);
/*
 * Used for mutual exclusion between writers of acpi_iomaps list, for
 * synchronization between readers and writer, RCU is used.
 */
static DEFINE_SPINLOCK(acpi_iomaps_lock);

struct acpi_iomap {
	struct list_head list;
	void __iomem *vaddr;
	unsigned long size;
	phys_addr_t paddr;
	struct kref ref;
};

/* acpi_iomaps_lock or RCU read lock must be held before calling */
static struct acpi_iomap *__acpi_find_iomap(phys_addr_t paddr,
					    unsigned long size)
{
	struct acpi_iomap *map;

	list_for_each_entry_rcu(map, &acpi_iomaps, list) {
		if (map->paddr + map->size >= paddr + size &&
		    map->paddr <= paddr)
			return map;
	}
	return NULL;
}

/*
 * Atomic "ioremap" used by NMI handler, if the specified IO memory
 * area is not pre-mapped, NULL will be returned.
 *
 * acpi_iomaps_lock or RCU read lock must be held before calling
 */
static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr,
					 unsigned long size)
{
	struct acpi_iomap *map;

	map = __acpi_find_iomap(paddr, size/8);
	if (map)
		return map->vaddr + (paddr - map->paddr);
	else
		return NULL;
}

/* acpi_iomaps_lock must be held before calling */
static void __iomem *__acpi_try_ioremap(phys_addr_t paddr,
					unsigned long size)
{
	struct acpi_iomap *map;

	map = __acpi_find_iomap(paddr, size);
	if (map) {
		kref_get(&map->ref);
		return map->vaddr + (paddr - map->paddr);
	} else
		return NULL;
}

#ifndef CONFIG_IA64
#define should_use_kmap(pfn)	page_is_ram(pfn)
#else
/* ioremap will take care of cache attributes */
#define should_use_kmap(pfn)	0
#endif

static void __iomem *acpi_map(phys_addr_t pg_off, unsigned long pg_sz)
{
	unsigned long pfn;

	pfn = pg_off >> PAGE_SHIFT;
	if (should_use_kmap(pfn)) {
		if (pg_sz > PAGE_SIZE)
			return NULL;
		return (void __iomem __force *)kmap(pfn_to_page(pfn));
	} else
		return ioremap(pg_off, pg_sz);
}

static void acpi_unmap(phys_addr_t pg_off, void __iomem *vaddr)
{
	unsigned long pfn;

	pfn = pg_off >> PAGE_SHIFT;
	if (page_is_ram(pfn))
		kunmap(pfn_to_page(pfn));
	else
		iounmap(vaddr);
}

/*
 * Used to pre-map the specified IO memory area. First try to find
 * whether the area is already pre-mapped, if it is, increase the
 * reference count (in __acpi_try_ioremap) and return; otherwise, do
 * the real ioremap, and add the mapping into acpi_iomaps list.
 */
static void __iomem *acpi_pre_map(phys_addr_t paddr,
				  unsigned long size)
{
	void __iomem *vaddr;
	struct acpi_iomap *map;
	unsigned long pg_sz, flags;
	phys_addr_t pg_off;

	spin_lock_irqsave(&acpi_iomaps_lock, flags);
	vaddr = __acpi_try_ioremap(paddr, size);
	spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
	if (vaddr)
		return vaddr;

	pg_off = paddr & PAGE_MASK;
	pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off;
	vaddr = acpi_map(pg_off, pg_sz);
	if (!vaddr)
		return NULL;
	map = kmalloc(sizeof(*map), GFP_KERNEL);
	if (!map)
		goto err_unmap;
	INIT_LIST_HEAD(&map->list);
	map->paddr = pg_off;
	map->size = pg_sz;
	map->vaddr = vaddr;
	kref_init(&map->ref);

	spin_lock_irqsave(&acpi_iomaps_lock, flags);
	vaddr = __acpi_try_ioremap(paddr, size);
	if (vaddr) {
		spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
		acpi_unmap(pg_off, map->vaddr);
		kfree(map);
		return vaddr;
	}
	list_add_tail_rcu(&map->list, &acpi_iomaps);
	spin_unlock_irqrestore(&acpi_iomaps_lock, flags);

	return map->vaddr + (paddr - map->paddr);
err_unmap:
	acpi_unmap(pg_off, vaddr);
	return NULL;
}

/* acpi_iomaps_lock must be held before calling */
static void __acpi_kref_del_iomap(struct kref *ref)
{
	struct acpi_iomap *map;

	map = container_of(ref, struct acpi_iomap, ref);
	list_del_rcu(&map->list);
}

/*
 * Used to post-unmap the specified IO memory area. The iounmap is
 * done only if the reference count goes zero.
 */
static void acpi_post_unmap(phys_addr_t paddr, unsigned long size)
{
	struct acpi_iomap *map;
	unsigned long flags;
	int del;

	spin_lock_irqsave(&acpi_iomaps_lock, flags);
	map = __acpi_find_iomap(paddr, size);
	BUG_ON(!map);
	del = kref_put(&map->ref, __acpi_kref_del_iomap);
	spin_unlock_irqrestore(&acpi_iomaps_lock, flags);

	if (!del)
		return;

	synchronize_rcu();
	acpi_unmap(map->paddr, map->vaddr);
	kfree(map);
}

/* In NMI handler, should set silent = 1 */
static int acpi_check_gar(struct acpi_generic_address *reg,
			  u64 *paddr, int silent)
{
	u32 width, space_id;

	width = reg->bit_width;
	space_id = reg->space_id;
	/* Handle possible alignment issues */
	memcpy(paddr, &reg->address, sizeof(*paddr));
	if (!*paddr) {
		if (!silent)
			pr_warning(FW_BUG ACPI_PFX
			"Invalid physical address in GAR [0x%llx/%u/%u]\n",
				   *paddr, width, space_id);
		return -EINVAL;
	}

	if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
		if (!silent)
			pr_warning(FW_BUG ACPI_PFX
				   "Invalid bit width in GAR [0x%llx/%u/%u]\n",
				   *paddr, width, space_id);
		return -EINVAL;
	}

	if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
	    space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
		if (!silent)
			pr_warning(FW_BUG ACPI_PFX
			"Invalid address space type in GAR [0x%llx/%u/%u]\n",
				   *paddr, width, space_id);
		return -EINVAL;
	}

	return 0;
}

/* Pre-map, working on GAR */
int acpi_pre_map_gar(struct acpi_generic_address *reg)
{
	u64 paddr;
	void __iomem *vaddr;
	int rc;

	if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
		return 0;

	rc = acpi_check_gar(reg, &paddr, 0);
	if (rc)
		return rc;

	vaddr = acpi_pre_map(paddr, reg->bit_width / 8);
	if (!vaddr)
		return -EIO;

	return 0;
}
EXPORT_SYMBOL_GPL(acpi_pre_map_gar);

/* Post-unmap, working on GAR */
int acpi_post_unmap_gar(struct acpi_generic_address *reg)
{
	u64 paddr;
	int rc;

	if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
		return 0;

	rc = acpi_check_gar(reg, &paddr, 0);
	if (rc)
		return rc;

	acpi_post_unmap(paddr, reg->bit_width / 8);

	return 0;
}
EXPORT_SYMBOL_GPL(acpi_post_unmap_gar);

#ifdef readq
static inline u64 read64(const volatile void __iomem *addr)
{
	return readq(addr);
}
#else
static inline u64 read64(const volatile void __iomem *addr)
{
	u64 l, h;
	l = readl(addr);
	h = readl(addr+4);
	return l | (h << 32);
}
#endif

/*
 * Can be used in atomic (including NMI) or process context. RCU read
 * lock can only be released after the IO memory area accessing.
 */
static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width)
{
	void __iomem *addr;

	rcu_read_lock();
	addr = __acpi_ioremap_fast(paddr, width);
	switch (width) {
	case 8:
		*val = readb(addr);
		break;
	case 16:
		*val = readw(addr);
		break;
	case 32:
		*val = readl(addr);
		break;
	case 64:
		*val = read64(addr);
		break;
	default:
		return -EINVAL;
	}
	rcu_read_unlock();

	return 0;
}

#ifdef writeq
static inline void write64(u64 val, volatile void __iomem *addr)
{
	writeq(val, addr);
}
#else
static inline void write64(u64 val, volatile void __iomem *addr)
{
	writel(val, addr);
	writel(val>>32, addr+4);
}
#endif

static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width)
{
	void __iomem *addr;

	rcu_read_lock();
	addr = __acpi_ioremap_fast(paddr, width);
	switch (width) {
	case 8:
		writeb(val, addr);
		break;
	case 16:
		writew(val, addr);
		break;
	case 32:
		writel(val, addr);
		break;
	case 64:
		write64(val, addr);
		break;
	default:
		return -EINVAL;
	}
	rcu_read_unlock();

	return 0;
}

/* GAR accessing in atomic (including NMI) or process context */
int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg)
{
	u64 paddr;
	int rc;

	rc = acpi_check_gar(reg, &paddr, 1);
	if (rc)
		return rc;

	*val = 0;
	switch (reg->space_id) {
	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
		return acpi_atomic_read_mem(paddr, val, reg->bit_width);
	case ACPI_ADR_SPACE_SYSTEM_IO:
		return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
	default:
		return -EINVAL;
	}
}
EXPORT_SYMBOL_GPL(acpi_atomic_read);

int acpi_atomic_write(u64 val, struct acpi_generic_address *reg)
{
	u64 paddr;
	int rc;

	rc = acpi_check_gar(reg, &paddr, 1);
	if (rc)
		return rc;

	switch (reg->space_id) {
	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
		return acpi_atomic_write_mem(paddr, val, reg->bit_width);
	case ACPI_ADR_SPACE_SYSTEM_IO:
		return acpi_os_write_port(paddr, val, reg->bit_width);
	default:
		return -EINVAL;
	}
}
EXPORT_SYMBOL_GPL(acpi_atomic_write);
Loading