/* Wait for TMCSReady bit to be set */
rc = tmc_wait_for_tmcready(drvdata); if (rc) {
dev_err(&drvdata->csdev->dev, "Failed to enable: TMC not ready\n");
CS_LOCK(drvdata->base); return rc;
}
tmc_flush_and_stop(drvdata); /* * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled.
*/ if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
staticint __tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
{ int rc = 0;
CS_UNLOCK(drvdata->base);
/* Wait for TMCSReady bit to be set */
rc = tmc_wait_for_tmcready(drvdata); if (rc) {
dev_err(&drvdata->csdev->dev, "Failed to enable : TMC is not ready\n");
CS_LOCK(drvdata->base); return rc;
}
/* * Return the available trace data in the buffer from @pos, with * a maximum limit of @len, updating the @bufpp on where to * find it.
*/
ssize_t tmc_etb_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
{
ssize_t actual = len;
/* Adjust the len to available size @pos */ if (pos + actual > drvdata->len)
actual = drvdata->len - pos; if (actual > 0)
*bufpp = drvdata->buf + pos; return actual;
}
staticint tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
{ int ret = 0; bool used = false; char *buf = NULL; unsignedlong flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/* * If we don't have a buffer release the lock and allocate memory. * Otherwise keep the lock and move along.
*/
raw_spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->buf) {
raw_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 */
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
}
if (drvdata->reading) {
ret = -EBUSY; goto out;
}
/* * 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 (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
csdev->refcnt++; 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;
}
ret = tmc_etb_enable_hw(drvdata); if (!ret) {
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
} else { /* Free up the buffer if we failed to enable */
used = false;
}
out:
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */ if (!used)
kfree(buf);
raw_spin_lock_irqsave(&drvdata->spinlock, flags); do {
ret = -EINVAL; if (drvdata->reading) break; /* * No need to continue if the ETB/ETF is already operated * from sysFS.
*/ if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
ret = -EBUSY; break;
}
/* Get a handle on the pid of the process to monitor */
pid = buf->pid;
if (drvdata->pid != -1 && drvdata->pid != pid) {
ret = -EBUSY; break;
}
ret = tmc_set_etf_buffer(csdev, handle); if (ret) break;
/* * No HW configuration is needed if the sink is already in * use for this session.
*/ if (drvdata->pid == pid) {
csdev->refcnt++; break;
}
ret = tmc_etb_enable_hw(drvdata); if (!ret) { /* Associate with monitored process. */
drvdata->pid = pid;
coresight_set_mode(csdev, CS_MODE_PERF);
csdev->refcnt++;
}
} while (0);
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
switch (mode) { case CS_MODE_SYSFS:
ret = tmc_enable_etf_sink_sysfs(csdev); break; case CS_MODE_PERF:
ret = tmc_enable_etf_sink_perf(csdev, data); break; /* We shouldn't be here */ default:
ret = -EINVAL; break;
}
if (drvdata->reading) {
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EBUSY;
}
csdev->refcnt--; if (csdev->refcnt) {
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
tmc_etb_disable_hw(drvdata); /* Dissociate from monitored process. */
drvdata->pid = -1;
coresight_set_mode(csdev, CS_MODE_DISABLED);
/* * Get a hold of the status register and see if a wrap around * has occurred. If so adjust things accordingly.
*/
status = readl_relaxed(drvdata->base + TMC_STS); if (status & TMC_STS_FULL) {
lost = true;
to_read = drvdata->size;
} else {
to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
}
/* * The TMC RAM buffer may be bigger than the space available in the * perf ring buffer (handle->size). If so advance the RRP so that we * get the latest trace data. In snapshot mode none of that matters * since we are expected to clobber stale data in favour of the latest * traces.
*/ if (!buf->snapshot && to_read > handle->size) {
u32 mask = tmc_get_memwidth_mask(drvdata);
/* * Make sure the new size is aligned in accordance with the * requirement explained in function tmc_get_memwidth_mask().
*/
to_read = handle->size & mask; /* Move the RAM read pointer up */
read_ptr = (write_ptr + drvdata->size) - to_read; /* Make sure we are still within our limits */ if (read_ptr > (drvdata->size - 1))
read_ptr -= drvdata->size; /* Tell the HW */
tmc_write_rrp(drvdata, read_ptr);
lost = true;
}
/* * Don't set the TRUNCATED flag in snapshot mode because 1) the * captured buffer is expected to be truncated and 2) a full buffer * prevents the event from being re-enabled by the perf core, * resulting in stale data being send to user space.
*/ if (!buf->snapshot && lost)
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
cur = buf->cur;
offset = buf->offset;
barrier = coresight_barrier_pkt;
/* for every byte to read */ for (i = 0; i < to_read; i += 4) {
buf_ptr = buf->data_pages[cur] + offset;
*buf_ptr = readl_relaxed(drvdata->base + TMC_RRD);
if (lost && i < CORESIGHT_BARRIER_PKT_SIZE) {
*buf_ptr = *barrier;
barrier++;
}
offset += 4; if (offset >= PAGE_SIZE) {
offset = 0;
cur++; /* wrap around at the end of the buffer */
cur &= buf->nr_pages - 1;
}
}
/* * In snapshot mode we simply increment the head by the number of byte * that were written. User space will figure out how many bytes to get * from the AUX buffer based on the position of the head.
*/ if (buf->snapshot)
handle->head += to_read;
/* * CS_LOCK() contains mb() so it can ensure visibility of the AUX trace * data before the aux_head is updated via perf_aux_output_end(), which * is expected by the perf ring buffer.
*/
CS_LOCK(drvdata->base);
/* * If the event is active, it is triggered during an AUX pause. * Re-enable the sink so that it is ready when AUX resume is invoked.
*/ if (!event->hw.state)
__tmc_etb_enable_hw(drvdata);
/* Make sure we have valid reserved memory */ if (!tmc_has_reserved_buffer(drvdata) ||
!tmc_has_crash_mdata_buffer(drvdata)) return 0;
tmc_crashdata_set_invalid(drvdata);
CS_UNLOCK(drvdata->base);
/* Proceed only if ETF is enabled or configured as sink */
val = readl(drvdata->base + TMC_CTL); if (!(val & TMC_CTL_CAPT_EN)) goto out;
val = readl(drvdata->base + TMC_MODE); if (val != TMC_MODE_CIRCULAR_BUFFER) goto out;
val = readl(drvdata->base + TMC_FFSR); /* Do manual flush and stop only if its not auto-stopped */ if (!(val & TMC_FFSR_FT_STOPPED)) {
dev_dbg(&csdev->dev, "%s: Triggering manual flush\n", __func__);
tmc_flush_and_stop(drvdata);
} else
tmc_wait_for_tmcready(drvdata);
/* Sync registers from hardware to metadata region */
mdata->tmc_sts = readl(drvdata->base + TMC_STS);
mdata->tmc_mode = readl(drvdata->base + TMC_MODE);
mdata->tmc_ffcr = readl(drvdata->base + TMC_FFCR);
mdata->tmc_ffsr = readl(drvdata->base + TMC_FFSR);
/* Sync Internal SRAM to reserved trace buffer region */
drvdata->buf = drvdata->resrv_buf.vaddr;
tmc_etb_dump_hw(drvdata); /* Store as per RSZ register convention */
mdata->tmc_ram_size = drvdata->len >> 2;
/* * Make sure all previous writes are ordered, * before we mark valid
*/
dmb(sy);
mdata->valid = true; /* * Below order need to maintained, since crc of metadata * is dependent on first
*/
mdata->crc32_tdata = find_crash_tracedata_crc(drvdata, mdata);
mdata->crc32_mdata = find_crash_metadata_crc(mdata);
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
{ enum tmc_mode mode; int ret = 0; unsignedlong flags;
/* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
drvdata->config_type != TMC_CONFIG_TYPE_ETF)) return -EINVAL;
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY; goto out;
}
/* Don't interfere if operated from Perf */ if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) {
ret = -EINVAL; goto out;
}
/* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) {
ret = -EINVAL; goto out;
}
/* Disable the TMC if need be */ if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) {
ret = -EINVAL; goto out;
}
__tmc_etb_disable_hw(drvdata);
}
/* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
drvdata->config_type != TMC_CONFIG_TYPE_ETF)) return -EINVAL;
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/* Re-enable the TMC if need be */ if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { /* There is no point in reading a TMC in HW FIFO mode */
mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) {
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EINVAL;
} /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end * up with stale data. * * Since the tracer is still enabled drvdata::buf * can't be NULL.
*/
memset(drvdata->buf, 0, drvdata->size); /* * Ignore failures to enable the TMC to make sure, we don't * leave the TMC in a "reading" state.
*/
__tmc_etb_enable_hw(drvdata);
} else { /* * The ETB/ETF is not tracing and the buffer was just read. * As such prepare to free the trace buffer.
*/
buf = drvdata->buf;
drvdata->buf = NULL;
}
/* * Free allocated memory outside of the spinlock. There is no need * to assert the validity of 'buf' since calling kfree(NULL) is safe.
*/
kfree(buf);
return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.