diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2006-02-08 05:53:50 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-03-20 04:12:01 -0500 |
commit | 5b0c0572fcd6204675c5f7ddfa572b5017f817dd (patch) | |
tree | 1075a61338e887bd6d4ecd4517646ef95dc09fbc /arch/sparc64/kernel/traps.c | |
parent | ac29c11d4cd4fa1fac968e99998a956405732f2f (diff) |
[SPARC64]: Sun4v interrupt handling.
Sun4v has 4 interrupt queues: cpu, device, resumable errors,
and non-resumable errors. A set of head/tail offset pointers
help maintain a work queue in physical memory. The entries
are 64-bytes in size.
Each queue is allocated then registered with the hypervisor
as we bring cpus up.
The two error queues each get a kernel side buffer that we
use to quickly empty the main interrupt queue before we
call up to C code to log the event and possibly take evasive
action.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/traps.c')
-rw-r--r-- | arch/sparc64/kernel/traps.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 8f3fce24359d..5417ff1b9345 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c | |||
@@ -1668,6 +1668,186 @@ void cheetah_plus_parity_error(int type, struct pt_regs *regs) | |||
1668 | regs->tpc); | 1668 | regs->tpc); |
1669 | } | 1669 | } |
1670 | 1670 | ||
1671 | struct sun4v_error_entry { | ||
1672 | u64 err_handle; | ||
1673 | u64 err_stick; | ||
1674 | |||
1675 | u32 err_type; | ||
1676 | #define SUN4V_ERR_TYPE_UNDEFINED 0 | ||
1677 | #define SUN4V_ERR_TYPE_UNCORRECTED_RES 1 | ||
1678 | #define SUN4V_ERR_TYPE_PRECISE_NONRES 2 | ||
1679 | #define SUN4V_ERR_TYPE_DEFERRED_NONRES 3 | ||
1680 | #define SUN4V_ERR_TYPE_WARNING_RES 4 | ||
1681 | |||
1682 | u32 err_attrs; | ||
1683 | #define SUN4V_ERR_ATTRS_PROCESSOR 0x00000001 | ||
1684 | #define SUN4V_ERR_ATTRS_MEMORY 0x00000002 | ||
1685 | #define SUN4V_ERR_ATTRS_PIO 0x00000004 | ||
1686 | #define SUN4V_ERR_ATTRS_INT_REGISTERS 0x00000008 | ||
1687 | #define SUN4V_ERR_ATTRS_FPU_REGISTERS 0x00000010 | ||
1688 | #define SUN4V_ERR_ATTRS_USER_MODE 0x01000000 | ||
1689 | #define SUN4V_ERR_ATTRS_PRIV_MODE 0x02000000 | ||
1690 | #define SUN4V_ERR_ATTRS_RES_QUEUE_FULL 0x80000000 | ||
1691 | |||
1692 | u64 err_raddr; | ||
1693 | u32 err_size; | ||
1694 | u16 err_cpu; | ||
1695 | u16 err_pad; | ||
1696 | }; | ||
1697 | |||
1698 | static atomic_t sun4v_resum_oflow_cnt = ATOMIC_INIT(0); | ||
1699 | static atomic_t sun4v_nonresum_oflow_cnt = ATOMIC_INIT(0); | ||
1700 | |||
1701 | static const char *sun4v_err_type_to_str(u32 type) | ||
1702 | { | ||
1703 | switch (type) { | ||
1704 | case SUN4V_ERR_TYPE_UNDEFINED: | ||
1705 | return "undefined"; | ||
1706 | case SUN4V_ERR_TYPE_UNCORRECTED_RES: | ||
1707 | return "uncorrected resumable"; | ||
1708 | case SUN4V_ERR_TYPE_PRECISE_NONRES: | ||
1709 | return "precise nonresumable"; | ||
1710 | case SUN4V_ERR_TYPE_DEFERRED_NONRES: | ||
1711 | return "deferred nonresumable"; | ||
1712 | case SUN4V_ERR_TYPE_WARNING_RES: | ||
1713 | return "warning resumable"; | ||
1714 | default: | ||
1715 | return "unknown"; | ||
1716 | }; | ||
1717 | } | ||
1718 | |||
1719 | static void sun4v_log_error(struct sun4v_error_entry *ent, int cpu, const char *pfx, atomic_t *ocnt) | ||
1720 | { | ||
1721 | int cnt; | ||
1722 | |||
1723 | printk("%s: Reporting on cpu %d\n", pfx, cpu); | ||
1724 | printk("%s: err_handle[%lx] err_stick[%lx] err_type[%08x:%s]\n", | ||
1725 | pfx, | ||
1726 | ent->err_handle, ent->err_stick, | ||
1727 | ent->err_type, | ||
1728 | sun4v_err_type_to_str(ent->err_type)); | ||
1729 | printk("%s: err_attrs[%08x:%s %s %s %s %s %s %s %s]\n", | ||
1730 | pfx, | ||
1731 | ent->err_attrs, | ||
1732 | ((ent->err_attrs & SUN4V_ERR_ATTRS_PROCESSOR) ? | ||
1733 | "processor" : ""), | ||
1734 | ((ent->err_attrs & SUN4V_ERR_ATTRS_MEMORY) ? | ||
1735 | "memory" : ""), | ||
1736 | ((ent->err_attrs & SUN4V_ERR_ATTRS_PIO) ? | ||
1737 | "pio" : ""), | ||
1738 | ((ent->err_attrs & SUN4V_ERR_ATTRS_INT_REGISTERS) ? | ||
1739 | "integer-regs" : ""), | ||
1740 | ((ent->err_attrs & SUN4V_ERR_ATTRS_FPU_REGISTERS) ? | ||
1741 | "fpu-regs" : ""), | ||
1742 | ((ent->err_attrs & SUN4V_ERR_ATTRS_USER_MODE) ? | ||
1743 | "user" : ""), | ||
1744 | ((ent->err_attrs & SUN4V_ERR_ATTRS_PRIV_MODE) ? | ||
1745 | "privileged" : ""), | ||
1746 | ((ent->err_attrs & SUN4V_ERR_ATTRS_RES_QUEUE_FULL) ? | ||
1747 | "queue-full" : "")); | ||
1748 | printk("%s: err_raddr[%016lx] err_size[%u] err_cpu[%u]\n", | ||
1749 | pfx, | ||
1750 | ent->err_raddr, ent->err_size, ent->err_cpu); | ||
1751 | |||
1752 | if ((cnt = atomic_read(ocnt)) != 0) { | ||
1753 | atomic_set(ocnt, 0); | ||
1754 | wmb(); | ||
1755 | printk("%s: Queue overflowed %d times.\n", | ||
1756 | pfx, cnt); | ||
1757 | } | ||
1758 | } | ||
1759 | |||
1760 | /* We run with %pil set to 15 and PSTATE_IE enabled in %pstate. | ||
1761 | * Log the event and clear the first word of the entry. | ||
1762 | */ | ||
1763 | void sun4v_resum_error(struct pt_regs *regs, unsigned long offset) | ||
1764 | { | ||
1765 | struct sun4v_error_entry *ent, local_copy; | ||
1766 | struct trap_per_cpu *tb; | ||
1767 | unsigned long paddr; | ||
1768 | int cpu; | ||
1769 | |||
1770 | cpu = get_cpu(); | ||
1771 | |||
1772 | tb = &trap_block[cpu]; | ||
1773 | paddr = tb->resum_kernel_buf_pa + offset; | ||
1774 | ent = __va(paddr); | ||
1775 | |||
1776 | memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry)); | ||
1777 | |||
1778 | /* We have a local copy now, so release the entry. */ | ||
1779 | ent->err_handle = 0; | ||
1780 | wmb(); | ||
1781 | |||
1782 | put_cpu(); | ||
1783 | |||
1784 | sun4v_log_error(&local_copy, cpu, | ||
1785 | KERN_ERR "RESUMABLE ERROR", | ||
1786 | &sun4v_resum_oflow_cnt); | ||
1787 | } | ||
1788 | |||
1789 | /* If we try to printk() we'll probably make matters worse, by trying | ||
1790 | * to retake locks this cpu already holds or causing more errors. So | ||
1791 | * just bump a counter, and we'll report these counter bumps above. | ||
1792 | */ | ||
1793 | void sun4v_resum_overflow(struct pt_regs *regs) | ||
1794 | { | ||
1795 | atomic_inc(&sun4v_resum_oflow_cnt); | ||
1796 | } | ||
1797 | |||
1798 | /* We run with %pil set to 15 and PSTATE_IE enabled in %pstate. | ||
1799 | * Log the event, clear the first word of the entry, and die. | ||
1800 | */ | ||
1801 | void sun4v_nonresum_error(struct pt_regs *regs, unsigned long offset) | ||
1802 | { | ||
1803 | struct sun4v_error_entry *ent, local_copy; | ||
1804 | struct trap_per_cpu *tb; | ||
1805 | unsigned long paddr; | ||
1806 | int cpu; | ||
1807 | |||
1808 | cpu = get_cpu(); | ||
1809 | |||
1810 | tb = &trap_block[cpu]; | ||
1811 | paddr = tb->nonresum_kernel_buf_pa + offset; | ||
1812 | ent = __va(paddr); | ||
1813 | |||
1814 | memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry)); | ||
1815 | |||
1816 | /* We have a local copy now, so release the entry. */ | ||
1817 | ent->err_handle = 0; | ||
1818 | wmb(); | ||
1819 | |||
1820 | put_cpu(); | ||
1821 | |||
1822 | #ifdef CONFIG_PCI | ||
1823 | /* Check for the special PCI poke sequence. */ | ||
1824 | if (pci_poke_in_progress && pci_poke_cpu == cpu) { | ||
1825 | pci_poke_faulted = 1; | ||
1826 | regs->tpc += 4; | ||
1827 | regs->tnpc = regs->tpc + 4; | ||
1828 | return; | ||
1829 | } | ||
1830 | #endif | ||
1831 | |||
1832 | sun4v_log_error(&local_copy, cpu, | ||
1833 | KERN_EMERG "NON-RESUMABLE ERROR", | ||
1834 | &sun4v_nonresum_oflow_cnt); | ||
1835 | |||
1836 | panic("Non-resumable error."); | ||
1837 | } | ||
1838 | |||
1839 | /* If we try to printk() we'll probably make matters worse, by trying | ||
1840 | * to retake locks this cpu already holds or causing more errors. So | ||
1841 | * just bump a counter, and we'll report these counter bumps above. | ||
1842 | */ | ||
1843 | void sun4v_nonresum_overflow(struct pt_regs *regs) | ||
1844 | { | ||
1845 | /* XXX Actually even this can make not that much sense. Perhaps | ||
1846 | * XXX we should just pull the plug and panic directly from here? | ||
1847 | */ | ||
1848 | atomic_inc(&sun4v_nonresum_oflow_cnt); | ||
1849 | } | ||
1850 | |||
1671 | void do_fpe_common(struct pt_regs *regs) | 1851 | void do_fpe_common(struct pt_regs *regs) |
1672 | { | 1852 | { |
1673 | if (regs->tstate & TSTATE_PRIV) { | 1853 | if (regs->tstate & TSTATE_PRIV) { |
@@ -2190,8 +2370,12 @@ void __init trap_init(void) | |||
2190 | offsetof(struct trap_per_cpu, dev_mondo_pa)) || | 2370 | offsetof(struct trap_per_cpu, dev_mondo_pa)) || |
2191 | (TRAP_PER_CPU_RESUM_MONDO_PA != | 2371 | (TRAP_PER_CPU_RESUM_MONDO_PA != |
2192 | offsetof(struct trap_per_cpu, resum_mondo_pa)) || | 2372 | offsetof(struct trap_per_cpu, resum_mondo_pa)) || |
2373 | (TRAP_PER_CPU_RESUM_KBUF_PA != | ||
2374 | offsetof(struct trap_per_cpu, resum_kernel_buf_pa)) || | ||
2193 | (TRAP_PER_CPU_NONRESUM_MONDO_PA != | 2375 | (TRAP_PER_CPU_NONRESUM_MONDO_PA != |
2194 | offsetof(struct trap_per_cpu, nonresum_mondo_pa)) || | 2376 | offsetof(struct trap_per_cpu, nonresum_mondo_pa)) || |
2377 | (TRAP_PER_CPU_NONRESUM_KBUF_PA != | ||
2378 | offsetof(struct trap_per_cpu, nonresum_kernel_buf_pa)) || | ||
2195 | (TRAP_PER_CPU_FAULT_INFO != | 2379 | (TRAP_PER_CPU_FAULT_INFO != |
2196 | offsetof(struct trap_per_cpu, fault_info))) | 2380 | offsetof(struct trap_per_cpu, fault_info))) |
2197 | trap_per_cpu_offsets_are_bolixed_dave(); | 2381 | trap_per_cpu_offsets_are_bolixed_dave(); |