#include "op_x86_model.h"
 #include "op_counter.h"
+#include "../../../drivers/oprofile/cpu_buffer.h"
 
 #define NUM_COUNTERS 4
 #define NUM_CONTROLS 4
 #define IBS_OP_LOW_VALID_BIT           (1ULL<<18)      /* bit 18 */
 #define IBS_OP_LOW_ENABLE              (1ULL<<17)      /* bit 17 */
 
-/* Codes used in cpu_buffer.c */
-/* This produces duplicate code, need to be fixed */
-#define IBS_FETCH_BEGIN         (1UL << 4)
-#define IBS_OP_BEGIN            (1UL << 5)
-
 /*
  * The function interface needs to be fixed, something like add
  * data. Should then be added to linux/oprofile.h.
  */
-extern void
-oprofile_add_ibs_sample(struct pt_regs * const regs,
-                       unsigned int * const ibs_sample, int ibs_code);
-
-struct ibs_fetch_sample {
-       /* MSRC001_1031 IBS Fetch Linear Address Register */
-       unsigned int ibs_fetch_lin_addr_low;
-       unsigned int ibs_fetch_lin_addr_high;
-       /* MSRC001_1030 IBS Fetch Control Register */
-       unsigned int ibs_fetch_ctl_low;
-       unsigned int ibs_fetch_ctl_high;
-       /* MSRC001_1032 IBS Fetch Physical Address Register */
-       unsigned int ibs_fetch_phys_addr_low;
-       unsigned int ibs_fetch_phys_addr_high;
-};
+extern
+void oprofile_add_data(struct op_entry *entry, struct pt_regs * const regs,
+                      unsigned long pc, int code, int size);
 
-struct ibs_op_sample {
-       /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */
-       unsigned int ibs_op_rip_low;
-       unsigned int ibs_op_rip_high;
-       /* MSRC001_1035 IBS Op Data Register */
-       unsigned int ibs_op_data1_low;
-       unsigned int ibs_op_data1_high;
-       /* MSRC001_1036 IBS Op Data 2 Register */
-       unsigned int ibs_op_data2_low;
-       unsigned int ibs_op_data2_high;
-       /* MSRC001_1037 IBS Op Data 3 Register */
-       unsigned int ibs_op_data3_low;
-       unsigned int ibs_op_data3_high;
-       /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */
-       unsigned int ibs_dc_linear_low;
-       unsigned int ibs_dc_linear_high;
-       /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */
-       unsigned int ibs_dc_phys_low;
-       unsigned int ibs_dc_phys_high;
-};
+#define IBS_FETCH_SIZE 6
+#define IBS_OP_SIZE    12
 
 static int has_ibs;    /* AMD Family10h and later */
 
 op_amd_handle_ibs(struct pt_regs * const regs,
                  struct op_msrs const * const msrs)
 {
-       unsigned int low, high;
-       struct ibs_fetch_sample ibs_fetch;
-       struct ibs_op_sample ibs_op;
+       u32 low, high;
+       u64 msr;
+       struct op_entry entry;
 
        if (!has_ibs)
                return 1;
        if (ibs_config.fetch_enabled) {
                rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
                if (high & IBS_FETCH_HIGH_VALID_BIT) {
-                       ibs_fetch.ibs_fetch_ctl_high = high;
-                       ibs_fetch.ibs_fetch_ctl_low = low;
-                       rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high);
-                       ibs_fetch.ibs_fetch_lin_addr_high = high;
-                       ibs_fetch.ibs_fetch_lin_addr_low = low;
-                       rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high);
-                       ibs_fetch.ibs_fetch_phys_addr_high = high;
-                       ibs_fetch.ibs_fetch_phys_addr_low = low;
-
-                       oprofile_add_ibs_sample(regs,
-                                               (unsigned int *)&ibs_fetch,
-                                               IBS_FETCH_BEGIN);
+                       rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr);
+                       oprofile_add_data(&entry, regs, msr, IBS_FETCH_CODE,
+                                         IBS_FETCH_SIZE);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       op_cpu_buffer_add_data(&entry, low);
+                       op_cpu_buffer_add_data(&entry, high);
+                       rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       op_cpu_buffer_write_commit(&entry);
 
                        /* reenable the IRQ */
-                       rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
                        high &= ~IBS_FETCH_HIGH_VALID_BIT;
                        high |= IBS_FETCH_HIGH_ENABLE;
                        low &= IBS_FETCH_LOW_MAX_CNT_MASK;
        if (ibs_config.op_enabled) {
                rdmsr(MSR_AMD64_IBSOPCTL, low, high);
                if (low & IBS_OP_LOW_VALID_BIT) {
-                       rdmsr(MSR_AMD64_IBSOPRIP, low, high);
-                       ibs_op.ibs_op_rip_low = low;
-                       ibs_op.ibs_op_rip_high = high;
-                       rdmsr(MSR_AMD64_IBSOPDATA, low, high);
-                       ibs_op.ibs_op_data1_low = low;
-                       ibs_op.ibs_op_data1_high = high;
-                       rdmsr(MSR_AMD64_IBSOPDATA2, low, high);
-                       ibs_op.ibs_op_data2_low = low;
-                       ibs_op.ibs_op_data2_high = high;
-                       rdmsr(MSR_AMD64_IBSOPDATA3, low, high);
-                       ibs_op.ibs_op_data3_low = low;
-                       ibs_op.ibs_op_data3_high = high;
-                       rdmsr(MSR_AMD64_IBSDCLINAD, low, high);
-                       ibs_op.ibs_dc_linear_low = low;
-                       ibs_op.ibs_dc_linear_high = high;
-                       rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high);
-                       ibs_op.ibs_dc_phys_low = low;
-                       ibs_op.ibs_dc_phys_high = high;
+                       rdmsrl(MSR_AMD64_IBSOPRIP, msr);
+                       oprofile_add_data(&entry, regs, msr, IBS_OP_CODE,
+                                         IBS_OP_SIZE);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       rdmsrl(MSR_AMD64_IBSOPDATA, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       rdmsrl(MSR_AMD64_IBSOPDATA2, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       rdmsrl(MSR_AMD64_IBSOPDATA3, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       rdmsrl(MSR_AMD64_IBSDCLINAD, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr);
+                       op_cpu_buffer_add_data(&entry, (u32)msr);
+                       op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
+                       op_cpu_buffer_write_commit(&entry);
 
                        /* reenable the IRQ */
-                       oprofile_add_ibs_sample(regs,
-                                               (unsigned int *)&ibs_op,
-                                               IBS_OP_BEGIN);
-                       rdmsr(MSR_AMD64_IBSOPCTL, low, high);
                        high = 0;
                        low &= ~IBS_OP_LOW_VALID_BIT;
                        low |= IBS_OP_LOW_ENABLE;
 
 
 #ifdef CONFIG_OPROFILE_IBS
 
-#define IBS_FETCH_CODE_SIZE    2
-#define IBS_OP_CODE_SIZE       5
-
-/*
- * Add IBS fetch and op entries to event buffer
- */
-static void add_ibs_begin(int cpu, int code, struct mm_struct *mm)
+static void add_data(struct op_entry *entry, struct mm_struct *mm)
 {
-       unsigned long pc;
-       int i, count;
-       unsigned long cookie = 0;
+       unsigned long code, pc, val;
+       unsigned long cookie;
        off_t offset;
-       struct op_entry entry;
-       struct op_sample *sample;
 
-       sample = op_cpu_buffer_read_entry(&entry, cpu);
-       if (!sample)
+       if (!op_cpu_buffer_get_data(entry, &code))
+               return;
+       if (!op_cpu_buffer_get_data(entry, &pc))
+               return;
+       if (!op_cpu_buffer_get_size(entry))
                return;
-       pc = sample->eip;
-
-#ifdef __LP64__
-       pc += sample->event << 32;
-#endif
 
        if (mm) {
                cookie = lookup_dcookie(mm, pc, &offset);
        add_event_entry(code);
        add_event_entry(offset);        /* Offset from Dcookie */
 
-       /* we send the Dcookie offset, but send the raw Linear Add also*/
-       add_event_entry(sample->eip);
-       add_event_entry(sample->event);
-
-       if (code == IBS_FETCH_CODE)
-               count = IBS_FETCH_CODE_SIZE;    /*IBS FETCH is 2 int64s*/
-       else
-               count = IBS_OP_CODE_SIZE;       /*IBS OP is 5 int64s*/
-
-       for (i = 0; i < count; i++) {
-               sample = op_cpu_buffer_read_entry(&entry, cpu);
-               if (!sample)
-                       return;
-               add_event_entry(sample->eip);
-               add_event_entry(sample->event);
-       }
-
-       return;
+       while (op_cpu_buffer_get_data(entry, &val))
+               add_event_entry(val);
 }
 
 #endif
                                add_user_ctx_switch(new, cookie);
                        }
 #ifdef CONFIG_OPROFILE_IBS
-                       if (flags & IBS_FETCH_BEGIN)
-                               add_ibs_begin(cpu, IBS_FETCH_CODE, mm);
-                       if (flags & IBS_OP_BEGIN)
-                               add_ibs_begin(cpu, IBS_OP_CODE, mm);
+                       if (op_cpu_buffer_get_size(&entry))
+                               add_data(&entry, mm);
 #endif
                        continue;
                }
 
 
 #ifdef CONFIG_OPROFILE_IBS
 
-void oprofile_add_ibs_sample(struct pt_regs * const regs,
-                            unsigned int * const ibs_sample, int ibs_code)
+/*
+ * Add samples with data to the ring buffer.
+ *
+ * Use op_cpu_buffer_add_data(&entry, val) to add data and
+ * op_cpu_buffer_write_commit(&entry) to commit the sample.
+ */
+void oprofile_add_data(struct op_entry *entry, struct pt_regs * const regs,
+                      unsigned long pc, int code, int size)
 {
+       struct op_sample *sample;
        int is_kernel = !user_mode(regs);
        struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
-       int fail = 0;
 
        cpu_buf->sample_received++;
 
-       /* backtraces disabled for ibs */
-       fail = fail || op_add_code(cpu_buf, 0, is_kernel, current);
+       /* no backtraces for samples with data */
+       if (op_add_code(cpu_buf, 0, is_kernel, current))
+               goto fail;
 
-       fail = fail || op_add_sample(cpu_buf, ESCAPE_CODE,   ibs_code);
-       fail = fail || op_add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
-       fail = fail || op_add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
-       fail = fail || op_add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
+       sample = op_cpu_buffer_write_reserve(entry, size + 2);
+       if (!sample)
+               goto fail;
+       sample->eip = ESCAPE_CODE;
+       sample->event = 0;              /* no flags */
 
-       if (ibs_code == IBS_OP_BEGIN) {
-               fail = fail || op_add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
-               fail = fail || op_add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
-               fail = fail || op_add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
-       }
+       op_cpu_buffer_add_data(entry, code);
+       op_cpu_buffer_add_data(entry, pc);
+
+       return;
 
-       if (fail)
-               cpu_buf->sample_lost_overflow++;
+fail:
+       cpu_buf->sample_lost_overflow++;
 }
 
 #endif