]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'x86/pebs' into x86-v28-for-linus-phase1
authorIngo Molnar <mingo@elte.hu>
Mon, 6 Oct 2008 14:17:23 +0000 (16:17 +0200)
committerIngo Molnar <mingo@elte.hu>
Mon, 6 Oct 2008 14:17:23 +0000 (16:17 +0200)
Conflicts:
include/asm-x86/ds.h

Signed-off-by: Ingo Molnar <mingo@elte.hu>
1  2 
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
include/asm-x86/ds.h
include/asm-x86/processor.h
include/asm-x86/ptrace-abi.h
include/asm-x86/ptrace.h

index 31f40b24bf5d72f4d62c204307a1bd826c726c32,ae584f87d06aa66de16347e72c1a789c1ef63c4c..491eb1a7e0739f2b500c323308e4e7487fa04814
@@@ -55,7 -55,6 +55,7 @@@
  #include <asm/tlbflush.h>
  #include <asm/cpu.h>
  #include <asm/kdebug.h>
 +#include <asm/idle.h>
  
  asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
  
@@@ -89,7 -88,6 +89,7 @@@ static void cpu_exit_clear(void
        cpu_clear(cpu, cpu_callin_map);
  
        numa_remove_cpu(cpu);
 +      c1e_remove_cpu(cpu);
  }
  
  /* We don't actually take CPU down, just spin without interrupts. */
@@@ -277,6 -275,14 +277,14 @@@ void exit_thread(void
                tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
                put_cpu();
        }
+ #ifdef CONFIG_X86_DS
+       /* Free any DS contexts that have not been properly released. */
+       if (unlikely(current->thread.ds_ctx)) {
+               /* we clear debugctl to make sure DS is not used. */
+               update_debugctlmsr(0);
+               ds_free(current->thread.ds_ctx);
+       }
+ #endif /* CONFIG_X86_DS */
  }
  
  void flush_thread(void)
@@@ -438,6 -444,35 +446,35 @@@ int set_tsc_mode(unsigned int val
        return 0;
  }
  
+ #ifdef CONFIG_X86_DS
+ static int update_debugctl(struct thread_struct *prev,
+                       struct thread_struct *next, unsigned long debugctl)
+ {
+       unsigned long ds_prev = 0;
+       unsigned long ds_next = 0;
+       if (prev->ds_ctx)
+               ds_prev = (unsigned long)prev->ds_ctx->ds;
+       if (next->ds_ctx)
+               ds_next = (unsigned long)next->ds_ctx->ds;
+       if (ds_next != ds_prev) {
+               /* we clear debugctl to make sure DS
+                * is not in use when we change it */
+               debugctl = 0;
+               update_debugctlmsr(0);
+               wrmsr(MSR_IA32_DS_AREA, ds_next, 0);
+       }
+       return debugctl;
+ }
+ #else
+ static int update_debugctl(struct thread_struct *prev,
+                       struct thread_struct *next, unsigned long debugctl)
+ {
+       return debugctl;
+ }
+ #endif /* CONFIG_X86_DS */
  static noinline void
  __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
                 struct tss_struct *tss)
        prev = &prev_p->thread;
        next = &next_p->thread;
  
-       debugctl = prev->debugctlmsr;
-       if (next->ds_area_msr != prev->ds_area_msr) {
-               /* we clear debugctl to make sure DS
-                * is not in use when we change it */
-               debugctl = 0;
-               update_debugctlmsr(0);
-               wrmsr(MSR_IA32_DS_AREA, next->ds_area_msr, 0);
-       }
+       debugctl = update_debugctl(prev, next, prev->debugctlmsr);
  
        if (next->debugctlmsr != debugctl)
                update_debugctlmsr(next->debugctlmsr);
                        hard_enable_TSC();
        }
  
- #ifdef X86_BTS
+ #ifdef CONFIG_X86_PTRACE_BTS
        if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS))
                ptrace_bts_take_timestamp(prev_p, BTS_TASK_DEPARTS);
  
        if (test_tsk_thread_flag(next_p, TIF_BTS_TRACE_TS))
                ptrace_bts_take_timestamp(next_p, BTS_TASK_ARRIVES);
- #endif
+ #endif /* CONFIG_X86_PTRACE_BTS */
  
  
        if (!test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) {
index e12e0e4dd2566c2959623d4475053c5fcbcd65d3,30ce4734edaa0b672926649f20455ed8895a85a9..4e168b250affb1dc708d5a1f3ab5400a9220f68b
@@@ -93,8 -93,6 +93,8 @@@ DECLARE_PER_CPU(int, cpu_state)
  static inline void play_dead(void)
  {
        idle_task_exit();
 +      c1e_remove_cpu(raw_smp_processor_id());
 +
        mb();
        /* Ack it */
        __get_cpu_var(cpu_state) = CPU_DEAD;
@@@ -240,6 -238,14 +240,14 @@@ void exit_thread(void
                t->io_bitmap_max = 0;
                put_cpu();
        }
+ #ifdef CONFIG_X86_DS
+       /* Free any DS contexts that have not been properly released. */
+       if (unlikely(t->ds_ctx)) {
+               /* we clear debugctl to make sure DS is not used. */
+               update_debugctlmsr(0);
+               ds_free(t->ds_ctx);
+       }
+ #endif /* CONFIG_X86_DS */
  }
  
  void flush_thread(void)
@@@ -473,13 -479,27 +481,27 @@@ static inline void __switch_to_xtra(str
        next = &next_p->thread;
  
        debugctl = prev->debugctlmsr;
-       if (next->ds_area_msr != prev->ds_area_msr) {
-               /* we clear debugctl to make sure DS
-                * is not in use when we change it */
-               debugctl = 0;
-               update_debugctlmsr(0);
-               wrmsrl(MSR_IA32_DS_AREA, next->ds_area_msr);
+ #ifdef CONFIG_X86_DS
+       {
+               unsigned long ds_prev = 0, ds_next = 0;
+               if (prev->ds_ctx)
+                       ds_prev = (unsigned long)prev->ds_ctx->ds;
+               if (next->ds_ctx)
+                       ds_next = (unsigned long)next->ds_ctx->ds;
+               if (ds_next != ds_prev) {
+                       /*
+                        * We clear debugctl to make sure DS
+                        * is not in use when we change it:
+                        */
+                       debugctl = 0;
+                       update_debugctlmsr(0);
+                       wrmsrl(MSR_IA32_DS_AREA, ds_next);
+               }
        }
+ #endif /* CONFIG_X86_DS */
  
        if (next->debugctlmsr != debugctl)
                update_debugctlmsr(next->debugctlmsr);
                memset(tss->io_bitmap, 0xff, prev->io_bitmap_max);
        }
  
- #ifdef X86_BTS
+ #ifdef CONFIG_X86_PTRACE_BTS
        if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS))
                ptrace_bts_take_timestamp(prev_p, BTS_TASK_DEPARTS);
  
        if (test_tsk_thread_flag(next_p, TIF_BTS_TRACE_TS))
                ptrace_bts_take_timestamp(next_p, BTS_TASK_ARRIVES);
- #endif
+ #endif /* CONFIG_X86_PTRACE_BTS */
  }
  
  /*
diff --combined include/asm-x86/ds.h
index 6b27c686fa10f43046cd3fa123a365f408941080,72c5a190bf48076d8b99b9f42f8a6f7a16a0e009..c3c953a45b215ba24b3c4216f4ee9def2e1f7c08
   * Debug Store (DS) support
   *
   * This provides a low-level interface to the hardware's Debug Store
-  * feature that is used for last branch recording (LBR) and
+  * feature that is used for branch trace store (BTS) and
   * precise-event based sampling (PEBS).
   *
-  * Different architectures use a different DS layout/pointer size.
-  * The below functions therefore work on a void*.
+  * It manages:
+  * - per-thread and per-cpu allocation of BTS and PEBS
+  * - buffer memory allocation (optional)
+  * - buffer overflow handling
+  * - buffer access
   *
+  * It assumes:
+  * - get_task_struct on all parameter tasks
+  * - current is allowed to trace parameter tasks
   *
-  * Since there is no user for PEBS, yet, only LBR (or branch
-  * trace store, BTS) is supported.
   *
-  *
-  * Copyright (C) 2007 Intel Corporation.
-  * Markus Metzger <markus.t.metzger@intel.com>, Dec 2007
+  * Copyright (C) 2007-2008 Intel Corporation.
+  * Markus Metzger <markus.t.metzger@intel.com>, 2007-2008
   */
  
 -#ifndef _ASM_X86_DS_H
 -#define _ASM_X86_DS_H
 +#ifndef ASM_X86__DS_H
 +#define ASM_X86__DS_H
  
+ #ifdef CONFIG_X86_DS
  #include <linux/types.h>
  #include <linux/init.h>
  
- struct cpuinfo_x86;
  
+ struct task_struct;
  
- /* a branch trace record entry
+ /*
+  * Request BTS or PEBS
+  *
+  * Due to alignement constraints, the actual buffer may be slightly
+  * smaller than the requested or provided buffer.
   *
-  * In order to unify the interface between various processor versions,
-  * we use the below data structure for all processors.
+  * Returns 0 on success; -Eerrno otherwise
+  *
+  * task: the task to request recording for;
+  *       NULL for per-cpu recording on the current cpu
+  * base: the base pointer for the (non-pageable) buffer;
+  *       NULL if buffer allocation requested
+  * size: the size of the requested or provided buffer
+  * ovfl: pointer to a function to be called on buffer overflow;
+  *       NULL if cyclic buffer requested
   */
- enum bts_qualifier {
-       BTS_INVALID = 0,
-       BTS_BRANCH,
-       BTS_TASK_ARRIVES,
-       BTS_TASK_DEPARTS
- };
+ typedef void (*ds_ovfl_callback_t)(struct task_struct *);
+ extern int ds_request_bts(struct task_struct *task, void *base, size_t size,
+                         ds_ovfl_callback_t ovfl);
+ extern int ds_request_pebs(struct task_struct *task, void *base, size_t size,
+                          ds_ovfl_callback_t ovfl);
+ /*
+  * Release BTS or PEBS resources
+  *
+  * Frees buffers allocated on ds_request.
+  *
+  * Returns 0 on success; -Eerrno otherwise
+  *
+  * task: the task to release resources for;
+  *       NULL to release resources for the current cpu
+  */
+ extern int ds_release_bts(struct task_struct *task);
+ extern int ds_release_pebs(struct task_struct *task);
+ /*
+  * Return the (array) index of the write pointer.
+  * (assuming an array of BTS/PEBS records)
+  *
+  * Returns -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * pos (out): if not NULL, will hold the result
+  */
+ extern int ds_get_bts_index(struct task_struct *task, size_t *pos);
+ extern int ds_get_pebs_index(struct task_struct *task, size_t *pos);
+ /*
+  * Return the (array) index one record beyond the end of the array.
+  * (assuming an array of BTS/PEBS records)
+  *
+  * Returns -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * pos (out): if not NULL, will hold the result
+  */
+ extern int ds_get_bts_end(struct task_struct *task, size_t *pos);
+ extern int ds_get_pebs_end(struct task_struct *task, size_t *pos);
+ /*
+  * Provide a pointer to the BTS/PEBS record at parameter index.
+  * (assuming an array of BTS/PEBS records)
+  *
+  * The pointer points directly into the buffer. The user is
+  * responsible for copying the record.
+  *
+  * Returns the size of a single record on success; -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * index: the index of the requested record
+  * record (out): pointer to the requested record
+  */
+ extern int ds_access_bts(struct task_struct *task,
+                        size_t index, const void **record);
+ extern int ds_access_pebs(struct task_struct *task,
+                         size_t index, const void **record);
+ /*
+  * Write one or more BTS/PEBS records at the write pointer index and
+  * advance the write pointer.
+  *
+  * If size is not a multiple of the record size, trailing bytes are
+  * zeroed out.
+  *
+  * May result in one or more overflow notifications.
+  *
+  * If called during overflow handling, that is, with index >=
+  * interrupt threshold, the write will wrap around.
+  *
+  * An overflow notification is given if and when the interrupt
+  * threshold is reached during or after the write.
+  *
+  * Returns the number of bytes written or -Eerrno.
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * buffer: the buffer to write
+  * size: the size of the buffer
+  */
+ extern int ds_write_bts(struct task_struct *task,
+                       const void *buffer, size_t size);
+ extern int ds_write_pebs(struct task_struct *task,
+                        const void *buffer, size_t size);
+ /*
+  * Same as ds_write_bts/pebs, but omit ownership checks.
+  *
+  * This is needed to have some other task than the owner of the
+  * BTS/PEBS buffer or the parameter task itself write into the
+  * respective buffer.
+  */
+ extern int ds_unchecked_write_bts(struct task_struct *task,
+                                 const void *buffer, size_t size);
+ extern int ds_unchecked_write_pebs(struct task_struct *task,
+                                  const void *buffer, size_t size);
+ /*
+  * Reset the write pointer of the BTS/PEBS buffer.
+  *
+  * Returns 0 on success; -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  */
+ extern int ds_reset_bts(struct task_struct *task);
+ extern int ds_reset_pebs(struct task_struct *task);
+ /*
+  * Clear the BTS/PEBS buffer and reset the write pointer.
+  * The entire buffer will be zeroed out.
+  *
+  * Returns 0 on success; -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  */
+ extern int ds_clear_bts(struct task_struct *task);
+ extern int ds_clear_pebs(struct task_struct *task);
+ /*
+  * Provide the PEBS counter reset value.
+  *
+  * Returns 0 on success; -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * value (out): the counter reset value
+  */
+ extern int ds_get_pebs_reset(struct task_struct *task, u64 *value);
+ /*
+  * Set the PEBS counter reset value.
+  *
+  * Returns 0 on success; -Eerrno on error
+  *
+  * task: the task to access;
+  *       NULL to access the current cpu
+  * value: the new counter reset value
+  */
+ extern int ds_set_pebs_reset(struct task_struct *task, u64 value);
+ /*
+  * Initialization
+  */
+ struct cpuinfo_x86;
+ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
  
- struct bts_struct {
-       u64 qualifier;
-       union {
-               /* BTS_BRANCH */
-               struct {
-                       u64 from_ip;
-                       u64 to_ip;
-               } lbr;
-               /* BTS_TASK_ARRIVES or
-                  BTS_TASK_DEPARTS */
-               u64 jiffies;
-       } variant;
+ /*
+  * The DS context - part of struct thread_struct.
+  */
+ struct ds_context {
+       /* pointer to the DS configuration; goes into MSR_IA32_DS_AREA */
+       unsigned char *ds;
+       /* the owner of the BTS and PEBS configuration, respectively */
+       struct task_struct *owner[2];
+       /* buffer overflow notification function for BTS and PEBS */
+       ds_ovfl_callback_t callback[2];
+       /* the original buffer address */
+       void *buffer[2];
+       /* the number of allocated pages for on-request allocated buffers */
+       unsigned int pages[2];
+       /* use count */
+       unsigned long count;
+       /* a pointer to the context location inside the thread_struct
+        * or the per_cpu context array */
+       struct ds_context **this;
+       /* a pointer to the task owning this context, or NULL, if the
+        * context is owned by a cpu */
+       struct task_struct *task;
  };
  
- /* Overflow handling mechanisms */
- #define DS_O_SIGNAL   1 /* send overflow signal */
- #define DS_O_WRAP     2 /* wrap around */
- extern int ds_allocate(void **, size_t);
- extern int ds_free(void **);
- extern int ds_get_bts_size(void *);
- extern int ds_get_bts_end(void *);
- extern int ds_get_bts_index(void *);
- extern int ds_set_overflow(void *, int);
- extern int ds_get_overflow(void *);
- extern int ds_clear(void *);
- extern int ds_read_bts(void *, int, struct bts_struct *);
- extern int ds_write_bts(void *, const struct bts_struct *);
- extern unsigned long ds_debugctl_mask(void);
- extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *c);
+ /* called by exit_thread() to free leftover contexts */
+ extern void ds_free(struct ds_context *context);
+ #else /* CONFIG_X86_DS */
+ #define ds_init_intel(config) do {} while (0)
  
 -#endif /* _ASM_X86_DS_H */
+ #endif /* CONFIG_X86_DS */
 +#endif /* ASM_X86__DS_H */
index 24cc5261af0c6654e211b5c49e8b000e8b8790e8,b9d6e51a6fe46ff0c256ea1b7a4815afe23740ae..51297d7f99c9f803fa2f9ab9d945e800a6d58bff
@@@ -1,5 -1,5 +1,5 @@@
 -#ifndef __ASM_X86_PROCESSOR_H
 -#define __ASM_X86_PROCESSOR_H
 +#ifndef ASM_X86__PROCESSOR_H
 +#define ASM_X86__PROCESSOR_H
  
  #include <asm/processor-flags.h>
  
@@@ -20,6 -20,7 +20,7 @@@ struct mm_struct
  #include <asm/msr.h>
  #include <asm/desc_defs.h>
  #include <asm/nops.h>
+ #include <asm/ds.h>
  
  #include <linux/personality.h>
  #include <linux/cpumask.h>
@@@ -411,9 -412,14 +412,14 @@@ struct thread_struct 
        unsigned                io_bitmap_max;
  /* MSR_IA32_DEBUGCTLMSR value to switch in if TIF_DEBUGCTLMSR is set.  */
        unsigned long   debugctlmsr;
- /* Debug Store - if not 0 points to a DS Save Area configuration;
-  *               goes into MSR_IA32_DS_AREA */
-       unsigned long   ds_area_msr;
+ #ifdef CONFIG_X86_DS
+ /* Debug Store context; see include/asm-x86/ds.h; goes into MSR_IA32_DS_AREA */
+       struct ds_context       *ds_ctx;
+ #endif /* CONFIG_X86_DS */
+ #ifdef CONFIG_X86_PTRACE_BTS
+ /* the signal to send on a bts buffer overflow */
+       unsigned int    bts_ovfl_signal;
+ #endif /* CONFIG_X86_PTRACE_BTS */
  };
  
  static inline unsigned long native_get_debugreg(int regno)
@@@ -943,4 -949,4 +949,4 @@@ extern void start_thread(struct pt_reg
  extern int get_tsc_mode(unsigned long adr);
  extern int set_tsc_mode(unsigned int val);
  
 -#endif
 +#endif /* ASM_X86__PROCESSOR_H */
index d0cf3344a586ccedc403472fa179039049b737c4,3397817eded997c054286ea00a2081f9051f26ba..4298b8882a782ab23ac101fcf48322eb78ea2cbb
@@@ -1,5 -1,5 +1,5 @@@
 -#ifndef _ASM_X86_PTRACE_ABI_H
 -#define _ASM_X86_PTRACE_ABI_H
 +#ifndef ASM_X86__PTRACE_ABI_H
 +#define ASM_X86__PTRACE_ABI_H
  
  #ifdef __i386__
  
@@@ -80,8 -80,9 +80,9 @@@
  
  #define PTRACE_SINGLEBLOCK    33      /* resume execution until next branch */
  
- #ifndef __ASSEMBLY__
+ #ifdef CONFIG_X86_PTRACE_BTS
  
+ #ifndef __ASSEMBLY__
  #include <asm/types.h>
  
  /* configuration/status structure used in PTRACE_BTS_CONFIG and
@@@ -97,20 -98,20 +98,20 @@@ struct ptrace_bts_config 
        /* actual size of bts_struct in bytes */
        __u32 bts_size;
  };
- #endif
+ #endif /* __ASSEMBLY__ */
  
  #define PTRACE_BTS_O_TRACE    0x1 /* branch trace */
  #define PTRACE_BTS_O_SCHED    0x2 /* scheduling events w/ jiffies */
  #define PTRACE_BTS_O_SIGNAL     0x4 /* send SIG<signal> on buffer overflow
                                       instead of wrapping around */
- #define PTRACE_BTS_O_CUT_SIZE 0x8 /* cut requested size to max available
-                                      instead of failing */
+ #define PTRACE_BTS_O_ALLOC    0x8 /* (re)allocate buffer */
  
  #define PTRACE_BTS_CONFIG     40
  /* Configure branch trace recording.
     ADDR points to a struct ptrace_bts_config.
     DATA gives the size of that buffer.
-    A new buffer is allocated, iff the size changes.
+    A new buffer is allocated, if requested in the flags.
+    An overflow signal may only be requested for new buffers.
     Returns the number of bytes read.
  */
  #define PTRACE_BTS_STATUS     41
     Returns the number of bytes written.
  */
  #define PTRACE_BTS_SIZE               42
- /* Return the number of available BTS records.
+ /* Return the number of available BTS records for draining.
     DATA and ADDR are ignored.
  */
  #define PTRACE_BTS_GET                43
     BTS records are read from oldest to newest.
     Returns number of BTS records drained.
  */
+ #endif /* CONFIG_X86_PTRACE_BTS */
  
 -#endif
 +#endif /* ASM_X86__PTRACE_ABI_H */
diff --combined include/asm-x86/ptrace.h
index d464f252edc3cfef0f4c7936db6056d664cc84d0,5e0c48d24b1a61642806de0b5eaaf37a9e50aacd..45c75238cd08ac91cd9abc3961df5a4b6fb23755
@@@ -1,5 -1,5 +1,5 @@@
 -#ifndef _ASM_X86_PTRACE_H
 -#define _ASM_X86_PTRACE_H
 +#ifndef ASM_X86__PTRACE_H
 +#define ASM_X86__PTRACE_H
  
  #include <linux/compiler.h>   /* For __user */
  #include <asm/ptrace-abi.h>
@@@ -127,14 -127,48 +127,48 @@@ struct pt_regs 
  #endif /* __KERNEL__ */
  #endif /* !__i386__ */
  
+ #ifdef CONFIG_X86_PTRACE_BTS
+ /* a branch trace record entry
+  *
+  * In order to unify the interface between various processor versions,
+  * we use the below data structure for all processors.
+  */
+ enum bts_qualifier {
+       BTS_INVALID = 0,
+       BTS_BRANCH,
+       BTS_TASK_ARRIVES,
+       BTS_TASK_DEPARTS
+ };
+ struct bts_struct {
+       __u64 qualifier;
+       union {
+               /* BTS_BRANCH */
+               struct {
+                       __u64 from_ip;
+                       __u64 to_ip;
+               } lbr;
+               /* BTS_TASK_ARRIVES or
+                  BTS_TASK_DEPARTS */
+               __u64 jiffies;
+       } variant;
+ };
+ #endif /* CONFIG_X86_PTRACE_BTS */
  #ifdef __KERNEL__
  
- /* the DS BTS struct is used for ptrace as well */
- #include <asm/ds.h>
+ #include <linux/init.h>
  
+ struct cpuinfo_x86;
  struct task_struct;
  
+ #ifdef CONFIG_X86_PTRACE_BTS
+ extern void __cpuinit ptrace_bts_init_intel(struct cpuinfo_x86 *);
  extern void ptrace_bts_take_timestamp(struct task_struct *, enum bts_qualifier);
+ #else
+ #define ptrace_bts_init_intel(config) do {} while (0)
+ #endif /* CONFIG_X86_PTRACE_BTS */
  
  extern unsigned long profile_pc(struct pt_regs *regs);
  
@@@ -239,4 -273,4 +273,4 @@@ extern int do_set_thread_area(struct ta
  
  #endif /* !__ASSEMBLY__ */
  
 -#endif
 +#endif /* ASM_X86__PTRACE_H */