diff options
| author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-09-21 10:08:49 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-10-06 08:21:50 -0400 |
| commit | 906010b2134e14a2e377decbadd357b3d0ab9c6a (patch) | |
| tree | 598b30d08f5ca8df1e00abc295b120fa1bd2c2e2 | |
| parent | e13dbd7d75d1ecc315c6e3071b3c4e8fba4f6bec (diff) | |
perf_event: Provide vmalloc() based mmap() backing
Some architectures such as Sparc, ARM and MIPS (basically
everything with flush_dcache_page()) need to deal with dcache
aliases by carefully placing pages in both kernel and user maps.
These architectures typically have to use vmalloc_user() for this.
However, on other architectures, vmalloc() is not needed and has
the downsides of being more restricted and slower than regular
allocations.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: David Miller <davem@davemloft.net>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Jens Axboe <jens.axboe@oracle.com>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1254830228.21044.272.camel@laptop>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/sparc/Kconfig | 2 | ||||
| -rw-r--r-- | include/linux/perf_event.h | 5 | ||||
| -rw-r--r-- | init/Kconfig | 18 | ||||
| -rw-r--r-- | kernel/perf_event.c | 248 | ||||
| -rw-r--r-- | tools/perf/design.txt | 3 |
5 files changed, 214 insertions, 62 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 97fca4695e0b..9b70a2f28dc7 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig | |||
| @@ -26,6 +26,7 @@ config SPARC | |||
| 26 | select RTC_CLASS | 26 | select RTC_CLASS |
| 27 | select RTC_DRV_M48T59 | 27 | select RTC_DRV_M48T59 |
| 28 | select HAVE_PERF_EVENTS | 28 | select HAVE_PERF_EVENTS |
| 29 | select PERF_USE_VMALLOC | ||
| 29 | select HAVE_DMA_ATTRS | 30 | select HAVE_DMA_ATTRS |
| 30 | select HAVE_DMA_API_DEBUG | 31 | select HAVE_DMA_API_DEBUG |
| 31 | 32 | ||
| @@ -48,6 +49,7 @@ config SPARC64 | |||
| 48 | select RTC_DRV_SUN4V | 49 | select RTC_DRV_SUN4V |
| 49 | select RTC_DRV_STARFIRE | 50 | select RTC_DRV_STARFIRE |
| 50 | select HAVE_PERF_EVENTS | 51 | select HAVE_PERF_EVENTS |
| 52 | select PERF_USE_VMALLOC | ||
| 51 | 53 | ||
| 52 | config ARCH_DEFCONFIG | 54 | config ARCH_DEFCONFIG |
| 53 | string | 55 | string |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3a9d36d1e92a..2e6d95f97419 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -442,6 +442,7 @@ enum perf_callchain_context { | |||
| 442 | #include <linux/hrtimer.h> | 442 | #include <linux/hrtimer.h> |
| 443 | #include <linux/fs.h> | 443 | #include <linux/fs.h> |
| 444 | #include <linux/pid_namespace.h> | 444 | #include <linux/pid_namespace.h> |
| 445 | #include <linux/workqueue.h> | ||
| 445 | #include <asm/atomic.h> | 446 | #include <asm/atomic.h> |
| 446 | 447 | ||
| 447 | #define PERF_MAX_STACK_DEPTH 255 | 448 | #define PERF_MAX_STACK_DEPTH 255 |
| @@ -513,6 +514,10 @@ struct file; | |||
| 513 | 514 | ||
| 514 | struct perf_mmap_data { | 515 | struct perf_mmap_data { |
| 515 | struct rcu_head rcu_head; | 516 | struct rcu_head rcu_head; |
| 517 | #ifdef CONFIG_PERF_USE_VMALLOC | ||
| 518 | struct work_struct work; | ||
| 519 | #endif | ||
| 520 | int data_order; | ||
| 516 | int nr_pages; /* nr of data pages */ | 521 | int nr_pages; /* nr of data pages */ |
| 517 | int writable; /* are we writable */ | 522 | int writable; /* are we writable */ |
| 518 | int nr_locked; /* nr pages mlocked */ | 523 | int nr_locked; /* nr pages mlocked */ |
diff --git a/init/Kconfig b/init/Kconfig index c7bac39d6c61..09c5c6431f42 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
| @@ -921,6 +921,11 @@ config HAVE_PERF_EVENTS | |||
| 921 | help | 921 | help |
| 922 | See tools/perf/design.txt for details. | 922 | See tools/perf/design.txt for details. |
| 923 | 923 | ||
| 924 | config PERF_USE_VMALLOC | ||
| 925 | bool | ||
| 926 | help | ||
| 927 | See tools/perf/design.txt for details | ||
| 928 | |||
| 924 | menu "Kernel Performance Events And Counters" | 929 | menu "Kernel Performance Events And Counters" |
| 925 | 930 | ||
| 926 | config PERF_EVENTS | 931 | config PERF_EVENTS |
| @@ -976,6 +981,19 @@ config PERF_COUNTERS | |||
| 976 | 981 | ||
| 977 | Say N if unsure. | 982 | Say N if unsure. |
| 978 | 983 | ||
| 984 | config DEBUG_PERF_USE_VMALLOC | ||
| 985 | default n | ||
| 986 | bool "Debug: use vmalloc to back perf mmap() buffers" | ||
| 987 | depends on PERF_EVENTS && DEBUG_KERNEL | ||
| 988 | select PERF_USE_VMALLOC | ||
| 989 | help | ||
| 990 | Use vmalloc memory to back perf mmap() buffers. | ||
| 991 | |||
| 992 | Mostly useful for debugging the vmalloc code on platforms | ||
| 993 | that don't require it. | ||
| 994 | |||
| 995 | Say N if unsure. | ||
| 996 | |||
| 979 | endmenu | 997 | endmenu |
| 980 | 998 | ||
| 981 | config VM_EVENT_COUNTERS | 999 | config VM_EVENT_COUNTERS |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index e491fb087939..9d0b5c665883 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/percpu.h> | 20 | #include <linux/percpu.h> |
| 21 | #include <linux/ptrace.h> | 21 | #include <linux/ptrace.h> |
| 22 | #include <linux/vmstat.h> | 22 | #include <linux/vmstat.h> |
| 23 | #include <linux/vmalloc.h> | ||
| 23 | #include <linux/hardirq.h> | 24 | #include <linux/hardirq.h> |
| 24 | #include <linux/rculist.h> | 25 | #include <linux/rculist.h> |
| 25 | #include <linux/uaccess.h> | 26 | #include <linux/uaccess.h> |
| @@ -2091,49 +2092,31 @@ unlock: | |||
| 2091 | rcu_read_unlock(); | 2092 | rcu_read_unlock(); |
| 2092 | } | 2093 | } |
| 2093 | 2094 | ||
| 2094 | static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | 2095 | static unsigned long perf_data_size(struct perf_mmap_data *data) |
| 2095 | { | 2096 | { |
| 2096 | struct perf_event *event = vma->vm_file->private_data; | 2097 | return data->nr_pages << (PAGE_SHIFT + data->data_order); |
| 2097 | struct perf_mmap_data *data; | 2098 | } |
| 2098 | int ret = VM_FAULT_SIGBUS; | ||
| 2099 | |||
| 2100 | if (vmf->flags & FAULT_FLAG_MKWRITE) { | ||
| 2101 | if (vmf->pgoff == 0) | ||
| 2102 | ret = 0; | ||
| 2103 | return ret; | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | rcu_read_lock(); | ||
| 2107 | data = rcu_dereference(event->data); | ||
| 2108 | if (!data) | ||
| 2109 | goto unlock; | ||
| 2110 | |||
| 2111 | if (vmf->pgoff == 0) { | ||
| 2112 | vmf->page = virt_to_page(data->user_page); | ||
| 2113 | } else { | ||
| 2114 | int nr = vmf->pgoff - 1; | ||
| 2115 | |||
| 2116 | if ((unsigned)nr > data->nr_pages) | ||
| 2117 | goto unlock; | ||
| 2118 | 2099 | ||
| 2119 | if (vmf->flags & FAULT_FLAG_WRITE) | 2100 | #ifndef CONFIG_PERF_USE_VMALLOC |
| 2120 | goto unlock; | ||
| 2121 | 2101 | ||
| 2122 | vmf->page = virt_to_page(data->data_pages[nr]); | 2102 | /* |
| 2123 | } | 2103 | * Back perf_mmap() with regular GFP_KERNEL-0 pages. |
| 2104 | */ | ||
| 2124 | 2105 | ||
| 2125 | get_page(vmf->page); | 2106 | static struct page * |
| 2126 | vmf->page->mapping = vma->vm_file->f_mapping; | 2107 | perf_mmap_to_page(struct perf_mmap_data *data, unsigned long pgoff) |
| 2127 | vmf->page->index = vmf->pgoff; | 2108 | { |
| 2109 | if (pgoff > data->nr_pages) | ||
| 2110 | return NULL; | ||
| 2128 | 2111 | ||
| 2129 | ret = 0; | 2112 | if (pgoff == 0) |
| 2130 | unlock: | 2113 | return virt_to_page(data->user_page); |
| 2131 | rcu_read_unlock(); | ||
| 2132 | 2114 | ||
| 2133 | return ret; | 2115 | return virt_to_page(data->data_pages[pgoff - 1]); |
| 2134 | } | 2116 | } |
| 2135 | 2117 | ||
| 2136 | static int perf_mmap_data_alloc(struct perf_event *event, int nr_pages) | 2118 | static struct perf_mmap_data * |
| 2119 | perf_mmap_data_alloc(struct perf_event *event, int nr_pages) | ||
| 2137 | { | 2120 | { |
| 2138 | struct perf_mmap_data *data; | 2121 | struct perf_mmap_data *data; |
| 2139 | unsigned long size; | 2122 | unsigned long size; |
| @@ -2158,19 +2141,10 @@ static int perf_mmap_data_alloc(struct perf_event *event, int nr_pages) | |||
| 2158 | goto fail_data_pages; | 2141 | goto fail_data_pages; |
| 2159 | } | 2142 | } |
| 2160 | 2143 | ||
| 2144 | data->data_order = 0; | ||
| 2161 | data->nr_pages = nr_pages; | 2145 | data->nr_pages = nr_pages; |
| 2162 | atomic_set(&data->lock, -1); | ||
| 2163 | |||
| 2164 | if (event->attr.watermark) { | ||
| 2165 | data->watermark = min_t(long, PAGE_SIZE * nr_pages, | ||
| 2166 | event->attr.wakeup_watermark); | ||
| 2167 | } | ||
| 2168 | if (!data->watermark) | ||
| 2169 | data->watermark = max(PAGE_SIZE, PAGE_SIZE * nr_pages / 4); | ||
| 2170 | 2146 | ||
| 2171 | rcu_assign_pointer(event->data, data); | 2147 | return data; |
| 2172 | |||
| 2173 | return 0; | ||
| 2174 | 2148 | ||
| 2175 | fail_data_pages: | 2149 | fail_data_pages: |
| 2176 | for (i--; i >= 0; i--) | 2150 | for (i--; i >= 0; i--) |
| @@ -2182,7 +2156,7 @@ fail_user_page: | |||
| 2182 | kfree(data); | 2156 | kfree(data); |
