static void bts_event_stop(struct perf_event *event, int flags) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); struct bts_buffer *buf = NULL; int state = READ_ONCE(bts->state); if (state == BTS_STATE_ACTIVE) __bts_event_stop(event, BTS_STATE_STOPPED); if (state != BTS_STATE_STOPPED) buf = perf_get_aux(&bts->handle); event->hw.state |= PERF_HES_STOPPED; if (flags & PERF_EF_UPDATE) { bts_update(bts); if (buf) { if (buf->snapshot) bts->handle.head = local_xchg(&buf->data_size, buf->nr_pages << PAGE_SHIFT); perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), !!local_xchg(&buf->lost, 0)); } cpuc->ds->bts_index = bts->ds_back.bts_buffer_base; cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base; cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum; cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold; } }
static unsigned long etb_reset_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { unsigned long size = 0; struct cs_buffers *buf = sink_config; if (buf) { /* * In snapshot mode ->data_size holds the new address of the * ring buffer's head. The size itself is the whole address * range since we want the latest information. */ if (buf->snapshot) handle->head = local_xchg(&buf->data_size, buf->nr_pages << PAGE_SHIFT); /* * Tell the tracer PMU how much we got in this run and if * something went wrong along the way. Nobody else can use * this cs_buffers instance until we are done. As such * resetting parameters here and squaring off with the ring * buffer API in the tracer PMU is fine. */ size = local_xchg(&buf->data_size, 0); } return size; }
static void bts_update(struct bts_ctx *bts) { int cpu = raw_smp_processor_id(); struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; struct bts_buffer *buf = perf_get_aux(&bts->handle); unsigned long index = ds->bts_index - ds->bts_buffer_base, old, head; if (!buf) return; head = index + bts_buffer_offset(buf, buf->cur_buf); old = local_xchg(&buf->head, head); if (!buf->snapshot) { if (old == head) return; if (ds->bts_index >= ds->bts_absolute_maximum) local_inc(&buf->lost); /* * old and head are always in the same physical buffer, so we * can subtract them to get the data size. */ local_add(head - old, &buf->data_size); } else { local_set(&buf->data_size, head); } }
static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode) { int ret = 0; long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* This shouldn't be happening */ if (WARN_ON(mode != CS_MODE_PERF)) return -EINVAL; spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EINVAL; goto out; } val = local_xchg(&drvdata->mode, mode); /* * In Perf mode there can be only one writer per sink. There * is also no need to continue if the ETB/ETR is already operated * from sysFS. */ if (val != CS_MODE_DISABLED) { ret = -EINVAL; goto out; } tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; }
int intel_bts_interrupt(void) { struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); struct perf_event *event = bts->handle.event; struct bts_buffer *buf; s64 old_head; int err; if (!event || !bts->started) return 0; buf = perf_get_aux(&bts->handle); /* * Skip snapshot counters: they don't use the interrupt, but * there's no other way of telling, because the pointer will * keep moving */ if (!buf || buf->snapshot) return 0; old_head = local_read(&buf->head); bts_update(bts); /* no new data */ if (old_head == local_read(&buf->head)) return 0; perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), !!local_xchg(&buf->lost, 0)); buf = perf_aux_output_begin(&bts->handle, event); if (!buf) return 1; err = bts_buffer_reset(buf, &bts->handle); if (err) perf_aux_output_end(&bts->handle, 0, false); return 1; }
static void bts_event_del(struct perf_event *event, int mode) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); struct bts_buffer *buf = perf_get_aux(&bts->handle); bts_event_stop(event, PERF_EF_UPDATE); if (buf) { if (buf->snapshot) bts->handle.head = local_xchg(&buf->data_size, buf->nr_pages << PAGE_SHIFT); perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), !!local_xchg(&buf->lost, 0)); } cpuc->ds->bts_index = bts->ds_back.bts_buffer_base; cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base; cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum; cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold; }
static void tmc_disable_etf_sink(struct coresight_device *csdev) { long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); return; } val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); /* Disable the TMC only if it needs to */ if (val != CS_MODE_DISABLED) tmc_etb_disable_hw(drvdata); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); }
int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size) { struct ring_buffer *rb; unsigned long tail, offset, head; int have_lost; struct perf_sample_data sample_data; struct { struct perf_event_header header; u64 id; u64 lost; } lost_event; rcu_read_lock(); /* * For inherited events we send all the output towards the parent. */ if (event->parent) event = event->parent; rb = rcu_dereference(event->rb); if (!rb) goto out; handle->rb = rb; handle->event = event; if (!rb->nr_pages) goto out; have_lost = local_read(&rb->lost); if (have_lost) { lost_event.header.size = sizeof(lost_event); perf_event_header__init_id(&lost_event.header, &sample_data, event); size += lost_event.header.size; } perf_output_get_handle(handle); do { /* * Userspace could choose to issue a mb() before updating the * tail pointer. So that all reads will be completed before the * write is issued. */ tail = ACCESS_ONCE(rb->user_page->data_tail); smp_rmb(); offset = head = local_read(&rb->head); head += size; if (unlikely(!perf_output_space(rb, tail, offset, head))) goto fail; } while (local_cmpxchg(&rb->head, offset, head) != offset); if (head - local_read(&rb->wakeup) > rb->watermark) local_add(rb->watermark, &rb->wakeup); handle->page = offset >> (PAGE_SHIFT + page_order(rb)); handle->page &= rb->nr_pages - 1; handle->size = offset & ((PAGE_SIZE << page_order(rb)) - 1); handle->addr = rb->data_pages[handle->page]; handle->addr += handle->size; handle->size = (PAGE_SIZE << page_order(rb)) - handle->size; if (have_lost) { lost_event.header.type = PERF_RECORD_LOST; lost_event.header.misc = 0; lost_event.id = event->id; lost_event.lost = local_xchg(&rb->lost, 0); perf_output_put(handle, lost_event); perf_event__output_id_sample(event, handle, &sample_data); } return 0; fail: local_inc(&rb->lost); perf_output_put_handle(handle); out: rcu_read_unlock(); return -ENOSPC; }
static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; char *buf = NULL; long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* This shouldn't be happening */ if (WARN_ON(mode != CS_MODE_SYSFS)) return -EINVAL; /* * If we don't have a buffer release the lock and allocate memory. * Otherwise keep the lock and move along. */ spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->buf) { spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Allocating the memory here while outside of the spinlock */ buf = kzalloc(drvdata->size, GFP_KERNEL); if (!buf) return -ENOMEM; /* Let's try again */ spin_lock_irqsave(&drvdata->spinlock, flags); } if (drvdata->reading) { ret = -EBUSY; goto out; } val = local_xchg(&drvdata->mode, mode); /* * In sysFS mode we can have multiple writers per sink. Since this * sink is already enabled no memory is needed and the HW need not be * touched. */ if (val == CS_MODE_SYSFS) goto out; /* * If drvdata::buf isn't NULL, memory was allocated for a previous * trace run but wasn't read. If so simply zero-out the memory. * Otherwise use the memory allocated above. * * The memory is freed when users read the buffer using the * /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for * details. */ if (drvdata->buf) { memset(drvdata->buf, 0, drvdata->size); } else { used = true; drvdata->buf = buf; } tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free memory outside the spinlock if need be */ if (!used && buf) kfree(buf); if (!ret) dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); return ret; }
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; long val; unsigned long flags; void __iomem *vaddr = NULL; dma_addr_t paddr; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* This shouldn't be happening */ if (WARN_ON(mode != CS_MODE_SYSFS)) return -EINVAL; /* * If we don't have a buffer release the lock and allocate memory. * Otherwise keep the lock and move along. */ spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->vaddr) { spin_unlock_irqrestore(&drvdata->spinlock, flags); /* * Contiguous memory can't be allocated while a spinlock is * held. As such allocate memory here and free it if a buffer * has already been allocated (from a previous session). */ vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size, &paddr, GFP_KERNEL); if (!vaddr) return -ENOMEM; /* Let's try again */ spin_lock_irqsave(&drvdata->spinlock, flags); } if (drvdata->reading) { ret = -EBUSY; goto out; } val = local_xchg(&drvdata->mode, mode); /* * In sysFS mode we can have multiple writers per sink. Since this * sink is already enabled no memory is needed and the HW need not be * touched. */ if (val == CS_MODE_SYSFS) goto out; /* * If drvdata::buf == NULL, use the memory allocated above. * Otherwise a buffer still exists from a previous session, so * simply use that. */ if (drvdata->buf == NULL) { used = true; drvdata->vaddr = vaddr; drvdata->paddr = paddr; drvdata->buf = drvdata->vaddr; } memset(drvdata->vaddr, 0, drvdata->size); tmc_etr_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free memory outside the spinlock if need be */ if (!used && vaddr) dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); if (!ret) dev_info(drvdata->dev, "TMC-ETR enabled\n"); return ret; }
int intel_bts_interrupt(void) { struct debug_store *ds = this_cpu_ptr(&cpu_hw_events)->ds; struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); struct perf_event *event = bts->handle.event; struct bts_buffer *buf; s64 old_head; int err = -ENOSPC, handled = 0; /* * The only surefire way of knowing if this NMI is ours is by checking * the write ptr against the PMI threshold. */ if (ds->bts_index >= ds->bts_interrupt_threshold) handled = 1; /* * this is wrapped in intel_bts_enable_local/intel_bts_disable_local, * so we can only be INACTIVE or STOPPED */ if (READ_ONCE(bts->state) == BTS_STATE_STOPPED) return handled; buf = perf_get_aux(&bts->handle); if (!buf) return handled; /* * Skip snapshot counters: they don't use the interrupt, but * there's no other way of telling, because the pointer will * keep moving */ if (buf->snapshot) return 0; old_head = local_read(&buf->head); bts_update(bts); /* no new data */ if (old_head == local_read(&buf->head)) return handled; perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), !!local_xchg(&buf->lost, 0)); buf = perf_aux_output_begin(&bts->handle, event); if (buf) err = bts_buffer_reset(buf, &bts->handle); if (err) { WRITE_ONCE(bts->state, BTS_STATE_STOPPED); if (buf) { /* * BTS_STATE_STOPPED should be visible before * cleared handle::event */ barrier(); perf_aux_output_end(&bts->handle, 0, false); } } return 1; }