diff options
author | Stephane Eranian <eranian@google.com> | 2014-02-11 10:20:12 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2014-02-21 15:49:08 -0500 |
commit | b9e1ab6d4c0582cad97699285a6b3cf992251b00 (patch) | |
tree | 26828975e359c41744f1c03d7f62c71fcc766ebe /arch/x86 | |
parent | 001e413f7e7a4a68dc1c3231f72b5be173939c8f (diff) |
perf/x86/uncore: add SNB/IVB/HSW client uncore memory controller support
This patch adds a new uncore PMU for Intel SNB/IVB/HSW client
CPUs. It adds the Integrated Memory Controller (IMC) PMU. This
new PMU provides a set of events to measure memory bandwidth utilization.
The IMC on those processor is PCI-space based. This patch
exposes a new uncore PMU on those processor: uncore_imc
Two new events are defined:
- name: data_reads
- code: 0x1
- unit: 64 bytes
- number of full cacheline read requests to the IMC
- name: data_writes
- code: 0x2
- unit: 64 bytes
- number of full cacheline write requests to the IMC
Documentation available at:
http://software.intel.com/en-us/articles/monitoring-integrated-memory-controller-requests-in-the-2nd-3rd-and-4th-generation-intel
Cc: mingo@elte.hu
Cc: acme@redhat.com
Cc: ak@linux.intel.com
Cc: zheng.z.yan@intel.com
Cc: peterz@infradead.org
Signed-off-by: Stephane Eranian <eranian@google.com>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1392132015-14521-7-git-send-email-eranian@google.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_uncore.c | 365 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_uncore.h | 1 |
2 files changed, 366 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index acbbdde5751c..66ce5e50cb44 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c | |||
@@ -66,6 +66,11 @@ DEFINE_UNCORE_FORMAT_ATTR(mask_vnw, mask_vnw, "config2:3-4"); | |||
66 | DEFINE_UNCORE_FORMAT_ATTR(mask0, mask0, "config2:0-31"); | 66 | DEFINE_UNCORE_FORMAT_ATTR(mask0, mask0, "config2:0-31"); |
67 | DEFINE_UNCORE_FORMAT_ATTR(mask1, mask1, "config2:32-63"); | 67 | DEFINE_UNCORE_FORMAT_ATTR(mask1, mask1, "config2:32-63"); |
68 | 68 | ||
69 | static void uncore_pmu_start_hrtimer(struct intel_uncore_box *box); | ||
70 | static void uncore_pmu_cancel_hrtimer(struct intel_uncore_box *box); | ||
71 | static void uncore_perf_event_update(struct intel_uncore_box *box, struct perf_event *event); | ||
72 | static void uncore_pmu_event_read(struct perf_event *event); | ||
73 | |||
69 | static struct intel_uncore_pmu *uncore_event_to_pmu(struct perf_event *event) | 74 | static struct intel_uncore_pmu *uncore_event_to_pmu(struct perf_event *event) |
70 | { | 75 | { |
71 | return container_of(event->pmu, struct intel_uncore_pmu, pmu); | 76 | return container_of(event->pmu, struct intel_uncore_pmu, pmu); |
@@ -1667,6 +1672,344 @@ static struct intel_uncore_type *snb_msr_uncores[] = { | |||
1667 | &snb_uncore_cbox, | 1672 | &snb_uncore_cbox, |
1668 | NULL, | 1673 | NULL, |
1669 | }; | 1674 | }; |
1675 | |||
1676 | enum { | ||
1677 | SNB_PCI_UNCORE_IMC, | ||
1678 | }; | ||
1679 | |||
1680 | static struct uncore_event_desc snb_uncore_imc_events[] = { | ||
1681 | INTEL_UNCORE_EVENT_DESC(data_reads, "event=0x01"), | ||
1682 | INTEL_UNCORE_EVENT_DESC(data_reads.scale, "64"), | ||
1683 | INTEL_UNCORE_EVENT_DESC(data_reads.unit, "bytes"), | ||
1684 | |||
1685 | INTEL_UNCORE_EVENT_DESC(data_writes, "event=0x02"), | ||
1686 | INTEL_UNCORE_EVENT_DESC(data_writes.scale, "64"), | ||
1687 | INTEL_UNCORE_EVENT_DESC(data_writes.unit, "bytes"), | ||
1688 | |||
1689 | { /* end: all zeroes */ }, | ||
1690 | }; | ||
1691 | |||
1692 | #define SNB_UNCORE_PCI_IMC_EVENT_MASK 0xff | ||
1693 | #define SNB_UNCORE_PCI_IMC_BAR_OFFSET 0x48 | ||
1694 | |||
1695 | /* page size multiple covering all config regs */ | ||
1696 | #define SNB_UNCORE_PCI_IMC_MAP_SIZE 0x6000 | ||
1697 | |||
1698 | #define SNB_UNCORE_PCI_IMC_DATA_READS 0x1 | ||
1699 | #define SNB_UNCORE_PCI_IMC_DATA_READS_BASE 0x5050 | ||
1700 | #define SNB_UNCORE_PCI_IMC_DATA_WRITES 0x2 | ||
1701 | #define SNB_UNCORE_PCI_IMC_DATA_WRITES_BASE 0x5054 | ||
1702 | #define SNB_UNCORE_PCI_IMC_CTR_BASE SNB_UNCORE_PCI_IMC_DATA_READS_BASE | ||
1703 | |||
1704 | static struct attribute *snb_uncore_imc_formats_attr[] = { | ||
1705 | &format_attr_event.attr, | ||
1706 | NULL, | ||
1707 | }; | ||
1708 | |||
1709 | static struct attribute_group snb_uncore_imc_format_group = { | ||
1710 | .name = "format", | ||
1711 | .attrs = snb_uncore_imc_formats_attr, | ||
1712 | }; | ||
1713 | |||
1714 | static void snb_uncore_imc_init_box(struct intel_uncore_box *box) | ||
1715 | { | ||
1716 | struct pci_dev *pdev = box->pci_dev; | ||
1717 | u32 addr_lo, addr_hi; | ||
1718 | resource_size_t addr; | ||
1719 | |||
1720 | pci_read_config_dword(pdev, SNB_UNCORE_PCI_IMC_BAR_OFFSET, &addr_lo); | ||
1721 | addr = addr_lo; | ||
1722 | |||
1723 | #ifdef CONFIG_PHYS_ADDR_T_64BIT | ||
1724 | pci_read_config_dword(pdev, SNB_UNCORE_PCI_IMC_BAR_OFFSET+4, &addr_hi); | ||
1725 | addr = ((resource_size_t)addr_hi << 32) | addr_lo; | ||
1726 | #endif | ||
1727 | |||
1728 | addr &= ~(PAGE_SIZE - 1); | ||
1729 | |||
1730 | box->io_addr = ioremap(addr, SNB_UNCORE_PCI_IMC_MAP_SIZE); | ||
1731 | } | ||
1732 | |||
1733 | static void snb_uncore_imc_enable_box(struct intel_uncore_box *box) | ||
1734 | {} | ||
1735 | |||
1736 | static void snb_uncore_imc_disable_box(struct intel_uncore_box *box) | ||
1737 | {} | ||
1738 | |||
1739 | static void snb_uncore_imc_enable_event(struct intel_uncore_box *box, struct perf_event *event) | ||
1740 | {} | ||
1741 | |||
1742 | static void snb_uncore_imc_disable_event(struct intel_uncore_box *box, struct perf_event *event) | ||
1743 | {} | ||
1744 | |||
1745 | static u64 snb_uncore_imc_read_counter(struct intel_uncore_box *box, struct perf_event *event) | ||
1746 | { | ||
1747 | struct hw_perf_event *hwc = &event->hw; | ||
1748 | |||
1749 | return (u64)*(unsigned int *)(box->io_addr + hwc->event_base); | ||
1750 | } | ||
1751 | |||
1752 | /* | ||
1753 | * custom event_init() function because we define our own fixed, free | ||
1754 | * running counters, so we do not want to conflict with generic uncore | ||
1755 | * logic. Also simplifies processing | ||
1756 | */ | ||
1757 | static int snb_uncore_imc_event_init(struct perf_event *event) | ||
1758 | { | ||
1759 | struct intel_uncore_pmu *pmu; | ||
1760 | struct intel_uncore_box *box; | ||
1761 | struct hw_perf_event *hwc = &event->hw; | ||
1762 | u64 cfg = event->attr.config & SNB_UNCORE_PCI_IMC_EVENT_MASK; | ||
1763 | int idx, base; | ||
1764 | |||
1765 | if (event->attr.type != event->pmu->type) | ||
1766 | return -ENOENT; | ||
1767 | |||
1768 | pmu = uncore_event_to_pmu(event); | ||
1769 | /* no device found for this pmu */ | ||
1770 | if (pmu->func_id < 0) | ||
1771 | return -ENOENT; | ||
1772 | |||
1773 | /* Sampling not supported yet */ | ||
1774 | if (hwc->sample_period) | ||
1775 | return -EINVAL; | ||
1776 | |||
1777 | /* unsupported modes and filters */ | ||
1778 | if (event->attr.exclude_user || | ||
1779 | event->attr.exclude_kernel || | ||
1780 | event->attr.exclude_hv || | ||
1781 | event->attr.exclude_idle || | ||
1782 | event->attr.exclude_host || | ||
1783 | event->attr.exclude_guest || | ||
1784 | event->attr.sample_period) /* no sampling */ | ||
1785 | return -EINVAL; | ||
1786 | |||
1787 | /* | ||
1788 | * Place all uncore events for a particular physical package | ||
1789 | * onto a single cpu | ||
1790 | */ | ||
1791 | if (event->cpu < 0) | ||
1792 | return -EINVAL; | ||
1793 | |||
1794 | /* check only supported bits are set */ | ||
1795 | if (event->attr.config & ~SNB_UNCORE_PCI_IMC_EVENT_MASK) | ||
1796 | return -EINVAL; | ||
1797 | |||
1798 | box = uncore_pmu_to_box(pmu, event->cpu); | ||
1799 | if (!box || box->cpu < 0) | ||
1800 | return -EINVAL; | ||
1801 | |||
1802 | event->cpu = box->cpu; | ||
1803 | |||
1804 | event->hw.idx = -1; | ||
1805 | event->hw.last_tag = ~0ULL; | ||
1806 | event->hw.extra_reg.idx = EXTRA_REG_NONE; | ||
1807 | event->hw.branch_reg.idx = EXTRA_REG_NONE; | ||
1808 | /* | ||
1809 | * check event is known (whitelist, determines counter) | ||
1810 | */ | ||
1811 | switch (cfg) { | ||
1812 | case SNB_UNCORE_PCI_IMC_DATA_READS: | ||
1813 | base = SNB_UNCORE_PCI_IMC_DATA_READS_BASE; | ||
1814 | idx = UNCORE_PMC_IDX_FIXED; | ||
1815 | break; | ||
1816 | case SNB_UNCORE_PCI_IMC_DATA_WRITES: | ||
1817 | base = SNB_UNCORE_PCI_IMC_DATA_WRITES_BASE; | ||
1818 | idx = UNCORE_PMC_IDX_FIXED + 1; | ||
1819 | break; | ||
1820 | default: | ||
1821 | return -EINVAL; | ||
1822 | } | ||
1823 | |||
1824 | /* must be done before validate_group */ | ||
1825 | event->hw.event_base = base; | ||
1826 | event->hw.config = cfg; | ||
1827 | event->hw.idx = idx; | ||
1828 | |||
1829 | /* no group validation needed, we have free running counters */ | ||
1830 | |||
1831 | return 0; | ||
1832 | } | ||
1833 | |||
1834 | static int snb_uncore_imc_hw_config(struct intel_uncore_box *box, struct perf_event *event) | ||
1835 | { | ||
1836 | return 0; | ||
1837 | } | ||
1838 | |||
1839 | static void snb_uncore_imc_event_start(struct perf_event *event, int flags) | ||
1840 | { | ||
1841 | struct intel_uncore_box *box = uncore_event_to_box(event); | ||
1842 | u64 count; | ||
1843 | |||
1844 | if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) | ||
1845 | return; | ||
1846 | |||
1847 | event->hw.state = 0; | ||
1848 | box->n_active++; | ||
1849 | |||
1850 | list_add_tail(&event->active_entry, &box->active_list); | ||
1851 | |||
1852 | count = snb_uncore_imc_read_counter(box, event); | ||
1853 | local64_set(&event->hw.prev_count, count); | ||
1854 | |||
1855 | if (box->n_active == 1) | ||
1856 | uncore_pmu_start_hrtimer(box); | ||
1857 | } | ||
1858 | |||
1859 | static void snb_uncore_imc_event_stop(struct perf_event *event, int flags) | ||
1860 | { | ||
1861 | struct intel_uncore_box *box = uncore_event_to_box(event); | ||
1862 | struct hw_perf_event *hwc = &event->hw; | ||
1863 | |||
1864 | if (!(hwc->state & PERF_HES_STOPPED)) { | ||
1865 | box->n_active--; | ||
1866 | |||
1867 | WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); | ||
1868 | hwc->state |= PERF_HES_STOPPED; | ||
1869 | |||
1870 | list_del(&event->active_entry); | ||
1871 | |||
1872 | if (box->n_active == 0) | ||
1873 | uncore_pmu_cancel_hrtimer(box); | ||
1874 | } | ||
1875 | |||
1876 | if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) { | ||
1877 | /* | ||
1878 | * Drain the remaining delta count out of a event | ||
1879 | * that we are disabling: | ||
1880 | */ | ||
1881 | uncore_perf_event_update(box, event); | ||
1882 | hwc->state |= PERF_HES_UPTODATE; | ||
1883 | } | ||
1884 | } | ||
1885 | |||
1886 | static int snb_uncore_imc_event_add(struct perf_event *event, int flags) | ||
1887 | { | ||
1888 | struct intel_uncore_box *box = uncore_event_to_box(event); | ||
1889 | struct hw_perf_event *hwc = &event->hw; | ||
1890 | |||
1891 | if (!box) | ||
1892 | return -ENODEV; | ||
1893 | |||
1894 | hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; | ||
1895 | if (!(flags & PERF_EF_START)) | ||
1896 | hwc->state |= PERF_HES_ARCH; | ||
1897 | |||
1898 | snb_uncore_imc_event_start(event, 0); | ||
1899 | |||
1900 | box->n_events++; | ||
1901 | |||
1902 | return 0; | ||
1903 | } | ||
1904 | |||
1905 | static void snb_uncore_imc_event_del(struct perf_event *event, int flags) | ||
1906 | { | ||
1907 | struct intel_uncore_box *box = uncore_event_to_box(event); | ||
1908 | int i; | ||
1909 | |||
1910 | snb_uncore_imc_event_stop(event, PERF_EF_UPDATE); | ||
1911 | |||
1912 | for (i = 0; i < box->n_events; i++) { | ||
1913 | if (event == box->event_list[i]) { | ||
1914 | --box->n_events; | ||
1915 | break; | ||
1916 | } | ||
1917 | } | ||
1918 | } | ||
1919 | |||
1920 | static int snb_pci2phy_map_init(int devid) | ||
1921 | { | ||
1922 | struct pci_dev *dev = NULL; | ||
1923 | int bus; | ||
1924 | |||
1925 | dev = pci_get_device(PCI_VENDOR_ID_INTEL, devid, dev); | ||
1926 | if (!dev) | ||
1927 | return -ENOTTY; | ||
1928 | |||
1929 | bus = dev->bus->number; | ||
1930 | |||
1931 | pcibus_to_physid[bus] = 0; | ||
1932 | |||
1933 | pci_dev_put(dev); | ||
1934 | |||
1935 | return 0; | ||
1936 | } | ||
1937 | |||
1938 | static struct pmu snb_uncore_imc_pmu = { | ||
1939 | .task_ctx_nr = perf_invalid_context, | ||
1940 | .event_init = snb_uncore_imc_event_init, | ||
1941 | .add = snb_uncore_imc_event_add, | ||
1942 | .del = snb_uncore_imc_event_del, | ||
1943 | .start = snb_uncore_imc_event_start, | ||
1944 | .stop = snb_uncore_imc_event_stop, | ||
1945 | .read = uncore_pmu_event_read, | ||
1946 | }; | ||
1947 | |||
1948 | static struct intel_uncore_ops snb_uncore_imc_ops = { | ||
1949 | .init_box = snb_uncore_imc_init_box, | ||
1950 | .enable_box = snb_uncore_imc_enable_box, | ||
1951 | .disable_box = snb_uncore_imc_disable_box, | ||
1952 | .disable_event = snb_uncore_imc_disable_event, | ||
1953 | .enable_event = snb_uncore_imc_enable_event, | ||
1954 | .hw_config = snb_uncore_imc_hw_config, | ||
1955 | .read_counter = snb_uncore_imc_read_counter, | ||
1956 | }; | ||
1957 | |||
1958 | static struct intel_uncore_type snb_uncore_imc = { | ||
1959 | .name = "imc", | ||
1960 | .num_counters = 2, | ||
1961 | .num_boxes = 1, | ||
1962 | .fixed_ctr_bits = 32, | ||
1963 | .fixed_ctr = SNB_UNCORE_PCI_IMC_CTR_BASE, | ||
1964 | .event_descs = snb_uncore_imc_events, | ||
1965 | .format_group = &snb_uncore_imc_format_group, | ||
1966 | .perf_ctr = SNB_UNCORE_PCI_IMC_DATA_READS_BASE, | ||
1967 | .event_mask = SNB_UNCORE_PCI_IMC_EVENT_MASK, | ||
1968 | .ops = &snb_uncore_imc_ops, | ||
1969 | .pmu = &snb_uncore_imc_pmu, | ||
1970 | }; | ||
1971 | |||
1972 | static struct intel_uncore_type *snb_pci_uncores[] = { | ||
1973 | [SNB_PCI_UNCORE_IMC] = &snb_uncore_imc, | ||
1974 | NULL, | ||
1975 | }; | ||
1976 | |||
1977 | static DEFINE_PCI_DEVICE_TABLE(snb_uncore_pci_ids) = { | ||
1978 | { /* IMC */ | ||
1979 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SNB_IMC), | ||
1980 | .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), | ||
1981 | }, | ||
1982 | }; | ||
1983 | |||
1984 | static DEFINE_PCI_DEVICE_TABLE(ivb_uncore_pci_ids) = { | ||
1985 | { /* IMC */ | ||
1986 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IVB_IMC), | ||
1987 | .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), | ||
1988 | }, | ||
1989 | }; | ||
1990 | |||
1991 | static DEFINE_PCI_DEVICE_TABLE(hsw_uncore_pci_ids) = { | ||
1992 | { /* IMC */ | ||
1993 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HSW_IMC), | ||
1994 | .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), | ||
1995 | }, | ||
1996 | }; | ||
1997 | |||
1998 | static struct pci_driver snb_uncore_pci_driver = { | ||
1999 | .name = "snb_uncore", | ||
2000 | .id_table = snb_uncore_pci_ids, | ||
2001 | }; | ||
2002 | |||
2003 | static struct pci_driver ivb_uncore_pci_driver = { | ||
2004 | .name = "ivb_uncore", | ||
2005 | .id_table = ivb_uncore_pci_ids, | ||
2006 | }; | ||
2007 | |||
2008 | static struct pci_driver hsw_uncore_pci_driver = { | ||
2009 | .name = "hsw_uncore", | ||
2010 | .id_table = hsw_uncore_pci_ids, | ||
2011 | }; | ||
2012 | |||
1670 | /* end of Sandy Bridge uncore support */ | 2013 | /* end of Sandy Bridge uncore support */ |
1671 | 2014 | ||
1672 | /* Nehalem uncore support */ | 2015 | /* Nehalem uncore support */ |
@@ -3501,6 +3844,28 @@ static int __init uncore_pci_init(void) | |||
3501 | pci_uncores = ivt_pci_uncores; | 3844 | pci_uncores = ivt_pci_uncores; |
3502 | uncore_pci_driver = &ivt_uncore_pci_driver; | 3845 | uncore_pci_driver = &ivt_uncore_pci_driver; |
3503 | break; | 3846 | break; |
3847 | case 42: /* Sandy Bridge */ | ||
3848 | ret = snb_pci2phy_map_init(PCI_DEVICE_ID_INTEL_SNB_IMC); | ||
3849 | if (ret) | ||
3850 | return ret; | ||
3851 | pci_uncores = snb_pci_uncores; | ||
3852 | uncore_pci_driver = &snb_uncore_pci_driver; | ||
3853 | break; | ||
3854 | case 58: /* Ivy Bridge */ | ||
3855 | ret = snb_pci2phy_map_init(PCI_DEVICE_ID_INTEL_IVB_IMC); | ||
3856 | if (ret) | ||
3857 | return ret; | ||
3858 | pci_uncores = snb_pci_uncores; | ||
3859 | uncore_pci_driver = &ivb_uncore_pci_driver; | ||
3860 | break; | ||
3861 | case 60: /* Haswell */ | ||
3862 | case 69: /* Haswell Celeron */ | ||
3863 | ret = snb_pci2phy_map_init(PCI_DEVICE_ID_INTEL_HSW_IMC); | ||
3864 | if (ret) | ||
3865 | return ret; | ||
3866 | pci_uncores = snb_pci_uncores; | ||
3867 | uncore_pci_driver = &hsw_uncore_pci_driver; | ||
3868 | break; | ||
3504 | default: | 3869 | default: |
3505 | return 0; | 3870 | return 0; |
3506 | } | 3871 | } |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index 7efd298f6c6b..fbf45a0ebf92 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h | |||
@@ -492,6 +492,7 @@ struct intel_uncore_box { | |||
492 | u64 hrtimer_duration; /* hrtimer timeout for this box */ | 492 | u64 hrtimer_duration; /* hrtimer timeout for this box */ |
493 | struct hrtimer hrtimer; | 493 | struct hrtimer hrtimer; |
494 | struct list_head list; | 494 | struct list_head list; |
495 | void *io_addr; | ||
495 | struct intel_uncore_extra_reg shared_regs[0]; | 496 | struct intel_uncore_extra_reg shared_regs[0]; |
496 | }; | 497 | }; |
497 | 498 | ||