From 615c164da0eb42cbfb1688cb429cc4d5039db5d8 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Jul 2019 17:14:21 +0300 Subject: intel_th: msu: Introduce buffer interface Introduces a concept of external buffers, which is a mechanism for creating trace sinks that would receive trace data from MSC buffers and transfer it elsewhere. A external buffer can implement its own window allocation/deallocation if it has to. It must provide a callback that's used to notify it when a window fills up, so that it can then start a DMA transaction from that window 'elsewhere'. This window remains in a 'locked' state and won't be used for storing new trace data until the buffer 'unlocks' it with a provided API call, at which point the window can be used again for storing trace data. This relies on a functional "last block" interrupt, so not all versions of Trace Hub can use this feature, which does not reflect on existing users. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190705141425.19894-2-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 388 +++++++++++++++++++++++++++++++++++++-- drivers/hwtracing/intel_th/msu.h | 20 +- 2 files changed, 379 insertions(+), 29 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 8ab28e5fb366..08413e6a075f 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -17,21 +17,48 @@ #include #include #include +#include #include #ifdef CONFIG_X86 #include #endif +#include #include "intel_th.h" #include "msu.h" #define msc_dev(x) (&(x)->thdev->dev) +/* + * Lockout state transitions: + * READY -> INUSE -+-> LOCKED -+-> READY -> etc. + * \-----------/ + * WIN_READY: window can be used by HW + * WIN_INUSE: window is in use + * WIN_LOCKED: window is filled up and is being processed by the buffer + * handling code + * + * All state transitions happen automatically, except for the LOCKED->READY, + * which needs to be signalled by the buffer code by calling + * intel_th_msc_window_unlock(). + * + * When the interrupt handler has to switch to the next window, it checks + * whether it's READY, and if it is, it performs the switch and tracing + * continues. If it's LOCKED, it stops the trace. + */ +enum lockout_state { + WIN_READY = 0, + WIN_INUSE, + WIN_LOCKED +}; + /** * struct msc_window - multiblock mode window descriptor * @entry: window list linkage (msc::win_list) * @pgoff: page offset into the buffer that this window starts at + * @lockout: lockout state, see comment below + * @lo_lock: lockout state serialization * @nr_blocks: number of blocks (pages) in this window * @nr_segs: number of segments in this window (<= @nr_blocks) * @_sgt: array of block descriptors @@ -40,6 +67,8 @@ struct msc_window { struct list_head entry; unsigned long pgoff; + enum lockout_state lockout; + spinlock_t lo_lock; unsigned int nr_blocks; unsigned int nr_segs; struct msc *msc; @@ -77,6 +106,8 @@ struct msc_iter { * struct msc - MSC device representation * @reg_base: register window base address * @thdev: intel_th_device pointer + * @mbuf: MSU buffer, if assigned + * @mbuf_priv MSU buffer's private data, if @mbuf * @win_list: list of windows in multiblock mode * @single_sgt: single mode buffer * @cur_win: current window @@ -100,6 +131,10 @@ struct msc { void __iomem *msu_base; struct intel_th_device *thdev; + const struct msu_buffer *mbuf; + void *mbuf_priv; + + struct work_struct work; struct list_head win_list; struct sg_table single_sgt; struct msc_window *cur_win; @@ -126,6 +161,101 @@ struct msc { unsigned int index; }; +static LIST_HEAD(msu_buffer_list); +static struct mutex msu_buffer_mutex; + +/** + * struct msu_buffer_entry - internal MSU buffer bookkeeping + * @entry: link to msu_buffer_list + * @mbuf: MSU buffer object + * @owner: module that provides this MSU buffer + */ +struct msu_buffer_entry { + struct list_head entry; + const struct msu_buffer *mbuf; + struct module *owner; +}; + +static struct msu_buffer_entry *__msu_buffer_entry_find(const char *name) +{ + struct msu_buffer_entry *mbe; + + lockdep_assert_held(&msu_buffer_mutex); + + list_for_each_entry(mbe, &msu_buffer_list, entry) { + if (!strcmp(mbe->mbuf->name, name)) + return mbe; + } + + return NULL; +} + +static const struct msu_buffer * +msu_buffer_get(const char *name) +{ + struct msu_buffer_entry *mbe; + + mutex_lock(&msu_buffer_mutex); + mbe = __msu_buffer_entry_find(name); + if (mbe && !try_module_get(mbe->owner)) + mbe = NULL; + mutex_unlock(&msu_buffer_mutex); + + return mbe ? mbe->mbuf : NULL; +} + +static void msu_buffer_put(const struct msu_buffer *mbuf) +{ + struct msu_buffer_entry *mbe; + + mutex_lock(&msu_buffer_mutex); + mbe = __msu_buffer_entry_find(mbuf->name); + if (mbe) + module_put(mbe->owner); + mutex_unlock(&msu_buffer_mutex); +} + +int intel_th_msu_buffer_register(const struct msu_buffer *mbuf, + struct module *owner) +{ + struct msu_buffer_entry *mbe; + int ret = 0; + + mbe = kzalloc(sizeof(*mbe), GFP_KERNEL); + if (!mbe) + return -ENOMEM; + + mutex_lock(&msu_buffer_mutex); + if (__msu_buffer_entry_find(mbuf->name)) { + ret = -EEXIST; + kfree(mbe); + goto unlock; + } + + mbe->mbuf = mbuf; + mbe->owner = owner; + list_add_tail(&mbe->entry, &msu_buffer_list); +unlock: + mutex_unlock(&msu_buffer_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(intel_th_msu_buffer_register); + +void intel_th_msu_buffer_unregister(const struct msu_buffer *mbuf) +{ + struct msu_buffer_entry *mbe; + + mutex_lock(&msu_buffer_mutex); + mbe = __msu_buffer_entry_find(mbuf->name); + if (mbe) { + list_del(&mbe->entry); + kfree(mbe); + } + mutex_unlock(&msu_buffer_mutex); +} +EXPORT_SYMBOL_GPL(intel_th_msu_buffer_unregister); + static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) { /* header hasn't been written */ @@ -188,6 +318,25 @@ static struct msc_window *msc_next_window(struct msc_window *win) return list_next_entry(win, entry); } +static size_t msc_win_total_sz(struct msc_window *win) +{ + unsigned int blk; + size_t size = 0; + + for (blk = 0; blk < win->nr_segs; blk++) { + struct msc_block_desc *bdesc = msc_win_block(win, blk); + + if (msc_block_wrapped(bdesc)) + return win->nr_blocks << PAGE_SHIFT; + + size += msc_total_sz(bdesc); + if (msc_block_last_written(bdesc)) + break; + } + + return size; +} + /** * msc_find_window() - find a window matching a given sg_table * @msc: MSC device @@ -527,6 +676,9 @@ static int intel_th_msu_init(struct msc *msc) if (!msc->do_irq) return 0; + if (!msc->mbuf) + return 0; + mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL); mintctl |= msc->index ? M1BLIE : M0BLIE; iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL); @@ -554,6 +706,44 @@ static void intel_th_msu_deinit(struct msc *msc) iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL); } +static int msc_win_set_lockout(struct msc_window *win, + enum lockout_state expect, + enum lockout_state new) +{ + enum lockout_state old; + unsigned long flags; + int ret = 0; + + if (!win->msc->mbuf) + return 0; + + spin_lock_irqsave(&win->lo_lock, flags); + old = win->lockout; + + if (old != expect) { + ret = -EINVAL; + dev_warn_ratelimited(msc_dev(win->msc), + "expected lockout state %d, got %d\n", + expect, old); + goto unlock; + } + + win->lockout = new; + +unlock: + spin_unlock_irqrestore(&win->lo_lock, flags); + + if (ret) { + if (expect == WIN_READY && old == WIN_LOCKED) + return -EBUSY; + + /* from intel_th_msc_window_unlock(), don't warn if not locked */ + if (expect == WIN_LOCKED && old == new) + return 0; + } + + return ret; +} /** * msc_configure() - set up MSC hardware * @msc: the MSC device to configure @@ -571,8 +761,12 @@ static int msc_configure(struct msc *msc) if (msc->mode > MSC_MODE_MULTI) return -ENOTSUPP; - if (msc->mode == MSC_MODE_MULTI) + if (msc->mode == MSC_MODE_MULTI) { + if (msc_win_set_lockout(msc->cur_win, WIN_READY, WIN_INUSE)) + return -EBUSY; + msc_buffer_clear_hw_header(msc); + } reg = msc->base_addr >> PAGE_SHIFT; iowrite32(reg, msc->reg_base + REG_MSU_MSC0BAR); @@ -594,10 +788,14 @@ static int msc_configure(struct msc *msc) iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); + intel_th_msu_init(msc); + msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI; intel_th_trace_enable(msc->thdev); msc->enabled = 1; + if (msc->mbuf && msc->mbuf->activate) + msc->mbuf->activate(msc->mbuf_priv); return 0; } @@ -611,10 +809,17 @@ static int msc_configure(struct msc *msc) */ static void msc_disable(struct msc *msc) { + struct msc_window *win = msc->cur_win; u32 reg; lockdep_assert_held(&msc->buf_mutex); + if (msc->mode == MSC_MODE_MULTI) + msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED); + + if (msc->mbuf && msc->mbuf->deactivate) + msc->mbuf->deactivate(msc->mbuf_priv); + intel_th_msu_deinit(msc); intel_th_trace_disable(msc->thdev); if (msc->mode == MSC_MODE_SINGLE) { @@ -630,6 +835,11 @@ static void msc_disable(struct msc *msc) reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); reg &= ~MSC_EN; iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); + + if (msc->mbuf && msc->mbuf->ready) + msc->mbuf->ready(msc->mbuf_priv, win->sgt, + msc_win_total_sz(win)); + msc->enabled = 0; iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR); @@ -640,6 +850,10 @@ static void msc_disable(struct msc *msc) reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); dev_dbg(msc_dev(msc), "MSCnSTS: %08x\n", reg); + + reg = ioread32(msc->reg_base + REG_MSU_MSUSTS); + reg &= msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST; + iowrite32(reg, msc->reg_base + REG_MSU_MSUSTS); } static int intel_th_msc_activate(struct intel_th_device *thdev) @@ -856,6 +1070,8 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) win->msc = msc; win->sgt = &win->_sgt; + win->lockout = WIN_READY; + spin_lock_init(&win->lo_lock); if (!list_empty(&msc->win_list)) { struct msc_window *prev = list_last_entry(&msc->win_list, @@ -865,8 +1081,13 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) win->pgoff = prev->pgoff + prev->nr_blocks; } - ret = __msc_buffer_win_alloc(win, nr_blocks); - if (ret < 0) + if (msc->mbuf && msc->mbuf->alloc_window) + ret = msc->mbuf->alloc_window(msc->mbuf_priv, &win->sgt, + nr_blocks << PAGE_SHIFT); + else + ret = __msc_buffer_win_alloc(win, nr_blocks); + + if (ret <= 0) goto err_nomem; msc_buffer_set_uc(win, ret); @@ -925,7 +1146,10 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win) msc_buffer_set_wb(win); - __msc_buffer_win_free(msc, win); + if (msc->mbuf && msc->mbuf->free_window) + msc->mbuf->free_window(msc->mbuf_priv, win->sgt); + else + __msc_buffer_win_free(msc, win); kfree(win); } @@ -1462,18 +1686,77 @@ static void msc_win_switch(struct msc *msc) intel_th_trace_switch(msc->thdev); } +/** + * intel_th_msc_window_unlock - put the window back in rotation + * @dev: MSC device to which this relates + * @sgt: buffer's sg_table for the window, does nothing if NULL + */ +void intel_th_msc_window_unlock(struct device *dev, struct sg_table *sgt) +{ + struct msc *msc = dev_get_drvdata(dev); + struct msc_window *win; + + if (!sgt) + return; + + win = msc_find_window(msc, sgt, false); + if (!win) + return; + + msc_win_set_lockout(win, WIN_LOCKED, WIN_READY); +} +EXPORT_SYMBOL_GPL(intel_th_msc_window_unlock); + +static void msc_work(struct work_struct *work) +{ + struct msc *msc = container_of(work, struct msc, work); + + intel_th_msc_deactivate(msc->thdev); +} + static irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev) { struct msc *msc = dev_get_drvdata(&thdev->dev); u32 msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS); u32 mask = msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST; + struct msc_window *win, *next_win; - if (!(msusts & mask)) { - if (msc->enabled) - return IRQ_HANDLED; + if (!msc->do_irq || !msc->mbuf) return IRQ_NONE; + + msusts &= mask; + + if (!msusts) + return msc->enabled ? IRQ_HANDLED : IRQ_NONE; + + iowrite32(msusts, msc->msu_base + REG_MSU_MSUSTS); + + if (!msc->enabled) + return IRQ_NONE; + + /* grab the window before we do the switch */ + win = msc->cur_win; + if (!win) + return IRQ_HANDLED; + next_win = msc_next_window(win); + if (!next_win) + return IRQ_HANDLED; + + /* next window: if READY, proceed, if LOCKED, stop the trace */ + if (msc_win_set_lockout(next_win, WIN_READY, WIN_INUSE)) { + schedule_work(&msc->work); + return IRQ_HANDLED; } + /* current window: INUSE -> LOCKED */ + msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED); + + msc_win_switch(msc); + + if (msc->mbuf && msc->mbuf->ready) + msc->mbuf->ready(msc->mbuf_priv, win->sgt, + msc_win_total_sz(win)); + return IRQ_HANDLED; } @@ -1511,21 +1794,43 @@ wrap_store(struct device *dev, struct device_attribute *attr, const char *buf, static DEVICE_ATTR_RW(wrap); +static void msc_buffer_unassign(struct msc *msc) +{ + lockdep_assert_held(&msc->buf_mutex); + + if (!msc->mbuf) + return; + + msc->mbuf->unassign(msc->mbuf_priv); + msu_buffer_put(msc->mbuf); + msc->mbuf_priv = NULL; + msc->mbuf = NULL; +} + static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct msc *msc = dev_get_drvdata(dev); + const char *mode = msc_mode[msc->mode]; + ssize_t ret; + + mutex_lock(&msc->buf_mutex); + if (msc->mbuf) + mode = msc->mbuf->name; + ret = scnprintf(buf, PAGE_SIZE, "%s\n", mode); + mutex_unlock(&msc->buf_mutex); - return scnprintf(buf, PAGE_SIZE, "%s\n", msc_mode[msc->mode]); + return ret; } static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + const struct msu_buffer *mbuf = NULL; struct msc *msc = dev_get_drvdata(dev); size_t len = size; - char *cp; + char *cp, *mode; int i, ret; if (!capable(CAP_SYS_RAWIO)) @@ -1535,17 +1840,59 @@ mode_store(struct device *dev, struct device_attribute *attr, const char *buf, if (cp) len = cp - buf; - for (i = 0; i < ARRAY_SIZE(msc_mode); i++) - if (!strncmp(msc_mode[i], buf, len)) - goto found; + mode = kstrndup(buf, len, GFP_KERNEL); + i = match_string(msc_mode, ARRAY_SIZE(msc_mode), mode); + if (i >= 0) + goto found; + + /* Buffer sinks only work with a usable IRQ */ + if (!msc->do_irq) { + kfree(mode); + return -EINVAL; + } + + mbuf = msu_buffer_get(mode); + kfree(mode); + if (mbuf) + goto found; return -EINVAL; found: mutex_lock(&msc->buf_mutex); + ret = 0; + + /* Same buffer: do nothing */ + if (mbuf && mbuf == msc->mbuf) { + /* put the extra reference we just got */ + msu_buffer_put(mbuf); + goto unlock; + } + ret = msc_buffer_unlocked_free_unless_used(msc); - if (!ret) - msc->mode = i; + if (ret) + goto unlock; + + if (mbuf) { + void *mbuf_priv = mbuf->assign(dev, &i); + + if (!mbuf_priv) { + ret = -ENOMEM; + goto unlock; + } + + msc_buffer_unassign(msc); + msc->mbuf_priv = mbuf_priv; + msc->mbuf = mbuf; + } else { + msc_buffer_unassign(msc); + } + + msc->mode = i; + +unlock: + if (ret && mbuf) + msu_buffer_put(mbuf); mutex_unlock(&msc->buf_mutex); return ret ? ret : size; @@ -1667,7 +2014,12 @@ win_switch_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&msc->buf_mutex); - if (msc->mode != MSC_MODE_MULTI) + /* + * Window switch can only happen in the "multi" mode. + * If a external buffer is engaged, they have the full + * control over window switching. + */ + if (msc->mode != MSC_MODE_MULTI || msc->mbuf) ret = -ENOTSUPP; else msc_win_switch(msc); @@ -1720,10 +2072,7 @@ static int intel_th_msc_probe(struct intel_th_device *thdev) msc->reg_base = base + msc->index * 0x100; msc->msu_base = base; - err = intel_th_msu_init(msc); - if (err) - return err; - + INIT_WORK(&msc->work, msc_work); err = intel_th_msc_init(msc); if (err) return err; @@ -1739,7 +2088,6 @@ static void intel_th_msc_remove(struct intel_th_device *thdev) int ret; intel_th_msc_deactivate(thdev); - intel_th_msu_deinit(msc); /* * Buffers should not be used at this point except if the diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h index 574c16004cb2..3f527dd4d727 100644 --- a/drivers/hwtracing/intel_th/msu.h +++ b/drivers/hwtracing/intel_th/msu.h @@ -44,14 +44,6 @@ enum { #define M0BLIE BIT(16) #define M1BLIE BIT(24) -/* MSC operating modes (MSC_MODE) */ -enum { - MSC_MODE_SINGLE = 0, - MSC_MODE_MULTI, - MSC_MODE_EXI, - MSC_MODE_DEBUG, -}; - /* MSCnSTS bits */ #define MSCSTS_WRAPSTAT BIT(1) /* Wrap occurred */ #define MSCSTS_PLE BIT(2) /* Pipeline Empty */ @@ -93,6 +85,16 @@ static inline unsigned long msc_data_sz(struct msc_block_desc *bdesc) return bdesc->valid_dw * 4 - MSC_BDESC; } +static inline unsigned long msc_total_sz(struct msc_block_desc *bdesc) +{ + return bdesc->valid_dw * 4; +} + +static inline unsigned long msc_block_sz(struct msc_block_desc *bdesc) +{ + return bdesc->block_sz * 64 - MSC_BDESC; +} + static inline bool msc_block_wrapped(struct msc_block_desc *bdesc) { if (bdesc->hw_tag & (MSC_HW_TAG_BLOCKWRAP | MSC_HW_TAG_WINWRAP)) @@ -104,7 +106,7 @@ static inline bool msc_block_wrapped(struct msc_block_desc *bdesc) static inline bool msc_block_last_written(struct msc_block_desc *bdesc) { if ((bdesc->hw_tag & MSC_HW_TAG_ENDBIT) || - (msc_data_sz(bdesc) != DATA_IN_PAGE)) + (msc_data_sz(bdesc) != msc_block_sz(bdesc))) return true; return false; -- cgit v1.2.3 From f220df66f67684246ae1bf4a4e479efc7c2f325a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Jul 2019 17:14:22 +0300 Subject: intel_th: msu-sink: An example msu buffer "sink" This patch adds an example MSU buffer "sink", which consumes trace data from MSC buffers. Functionally, it acts similarly to "multi" mode with automatic window switching. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190705141425.19894-3-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/Makefile | 3 + drivers/hwtracing/intel_th/msu-sink.c | 116 ++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 drivers/hwtracing/intel_th/msu-sink.c (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index d9252fa8d9ca..b63eb8f309ad 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile @@ -20,3 +20,6 @@ intel_th_msu-y := msu.o obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o intel_th_pti-y := pti.o + +obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu_sink.o +intel_th_msu_sink-y := msu-sink.o diff --git a/drivers/hwtracing/intel_th/msu-sink.c b/drivers/hwtracing/intel_th/msu-sink.c new file mode 100644 index 000000000000..2c7f5116be12 --- /dev/null +++ b/drivers/hwtracing/intel_th/msu-sink.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An example software sink buffer for Intel TH MSU. + * + * Copyright (C) 2019 Intel Corporation. + */ + +#include +#include +#include +#include +#include + +#define MAX_SGTS 16 + +struct msu_sink_private { + struct device *dev; + struct sg_table **sgts; + unsigned int nr_sgts; +}; + +static void *msu_sink_assign(struct device *dev, int *mode) +{ + struct msu_sink_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->sgts = kcalloc(MAX_SGTS, sizeof(void *), GFP_KERNEL); + if (!priv->sgts) { + kfree(priv); + return NULL; + } + + priv->dev = dev; + *mode = MSC_MODE_MULTI; + + return priv; +} + +static void msu_sink_unassign(void *data) +{ + struct msu_sink_private *priv = data; + + kfree(priv->sgts); + kfree(priv); +} + +/* See also: msc.c: __msc_buffer_win_alloc() */ +static int msu_sink_alloc_window(void *data, struct sg_table **sgt, size_t size) +{ + struct msu_sink_private *priv = data; + unsigned int nents; + struct scatterlist *sg_ptr; + void *block; + int ret, i; + + if (priv->nr_sgts == MAX_SGTS) + return -ENOMEM; + + nents = DIV_ROUND_UP(size, PAGE_SIZE); + + ret = sg_alloc_table(*sgt, nents, GFP_KERNEL); + if (ret) + return -ENOMEM; + + priv->sgts[priv->nr_sgts++] = *sgt; + + for_each_sg((*sgt)->sgl, sg_ptr, nents, i) { + block = dma_alloc_coherent(priv->dev->parent->parent, + PAGE_SIZE, &sg_dma_address(sg_ptr), + GFP_KERNEL); + sg_set_buf(sg_ptr, block, PAGE_SIZE); + } + + return nents; +} + +/* See also: msc.c: __msc_buffer_win_free() */ +static void msu_sink_free_window(void *data, struct sg_table *sgt) +{ + struct msu_sink_private *priv = data; + struct scatterlist *sg_ptr; + int i; + + for_each_sg(sgt->sgl, sg_ptr, sgt->nents, i) { + dma_free_coherent(priv->dev->parent->parent, PAGE_SIZE, + sg_virt(sg_ptr), sg_dma_address(sg_ptr)); + } + + sg_free_table(sgt); + priv->nr_sgts--; +} + +static int msu_sink_ready(void *data, struct sg_table *sgt, size_t bytes) +{ + struct msu_sink_private *priv = data; + + intel_th_msc_window_unlock(priv->dev, sgt); + + return 0; +} + +static const struct msu_buffer sink_mbuf = { + .name = "sink", + .assign = msu_sink_assign, + .unassign = msu_sink_unassign, + .alloc_window = msu_sink_alloc_window, + .free_window = msu_sink_free_window, + .ready = msu_sink_ready, +}; + +module_intel_th_msu_buffer(sink_mbuf); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 57b69a1f66486d632e188fb167950fb4bdf84221 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Jul 2019 17:14:23 +0300 Subject: intel_th: msu: Get rid of the window size limit Currently, the window size is limited to the maximum number of sg entries in one table. This is because the code addresses individual blocks within the window by their numeric index. In reality, though, the blocks most often are iterated through sequentially. By rewriting the logic to use sg pointers instead of block indices we loose the necessity to dereference them directly and gain the ability to use multiple chained tables if necessary. Get rid of the limitation by replacing index-based block accesses with sequential block accesses. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190705141425.19894-4-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 139 +++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 71 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 08413e6a075f..a6c0eb09c515 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -95,8 +95,8 @@ struct msc_iter { struct msc_window *start_win; struct msc_window *win; unsigned long offset; - int start_block; - int block; + struct scatterlist *start_block; + struct scatterlist *block; unsigned int block_off; unsigned int wrap_count; unsigned int eof; @@ -269,28 +269,25 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) return false; } -static inline struct msc_block_desc * -msc_win_block(struct msc_window *win, unsigned int block) +static inline struct scatterlist *msc_win_base_sg(struct msc_window *win) { - return sg_virt(&win->sgt->sgl[block]); + return win->sgt->sgl; } -static inline size_t -msc_win_actual_bsz(struct msc_window *win, unsigned int block) +static inline struct msc_block_desc *msc_win_base(struct msc_window *win) { - return win->sgt->sgl[block].length; + return sg_virt(msc_win_base_sg(win)); } -static inline dma_addr_t -msc_win_baddr(struct msc_window *win, unsigned int block) +static inline dma_addr_t msc_win_base_dma(struct msc_window *win) { - return sg_dma_address(&win->sgt->sgl[block]); + return sg_dma_address(msc_win_base_sg(win)); } static inline unsigned long -msc_win_bpfn(struct msc_window *win, unsigned int block) +msc_win_base_pfn(struct msc_window *win) { - return msc_win_baddr(win, block) >> PAGE_SHIFT; + return PFN_DOWN(msc_win_base_dma(win)); } /** @@ -320,11 +317,12 @@ static struct msc_window *msc_next_window(struct msc_window *win) static size_t msc_win_total_sz(struct msc_window *win) { + struct scatterlist *sg; unsigned int blk; size_t size = 0; - for (blk = 0; blk < win->nr_segs; blk++) { - struct msc_block_desc *bdesc = msc_win_block(win, blk); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { + struct msc_block_desc *bdesc = sg_virt(sg); if (msc_block_wrapped(bdesc)) return win->nr_blocks << PAGE_SHIFT; @@ -365,7 +363,7 @@ msc_find_window(struct msc *msc, struct sg_table *sgt, bool nonempty) found++; /* skip the empty ones */ - if (nonempty && msc_block_is_empty(msc_win_block(win, 0))) + if (nonempty && msc_block_is_empty(msc_win_base(win))) continue; if (found) @@ -399,44 +397,38 @@ static struct msc_window *msc_oldest_window(struct msc *msc) } /** - * msc_win_oldest_block() - locate the oldest block in a given window + * msc_win_oldest_sg() - locate the oldest block in a given window * @win: window to look at * * Return: index of the block with the oldest data */ -static unsigned int msc_win_oldest_block(struct msc_window *win) +static struct scatterlist *msc_win_oldest_sg(struct msc_window *win) { unsigned int blk; - struct msc_block_desc *bdesc = msc_win_block(win, 0); + struct scatterlist *sg; + struct msc_block_desc *bdesc = msc_win_base(win); /* without wrapping, first block is the oldest */ if (!msc_block_wrapped(bdesc)) - return 0; + return msc_win_base_sg(win); /* * with wrapping, last written block contains both the newest and the * oldest data for this window. */ - for (blk = 0; blk < win->nr_segs; blk++) { - bdesc = msc_win_block(win, blk); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { + struct msc_block_desc *bdesc = sg_virt(sg); if (msc_block_last_written(bdesc)) - return blk; + return sg; } - return 0; + return msc_win_base_sg(win); } static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter) { - return msc_win_block(iter->win, iter->block); -} - -static void msc_iter_init(struct msc_iter *iter) -{ - memset(iter, 0, sizeof(*iter)); - iter->start_block = -1; - iter->block = -1; + return sg_virt(iter->block); } static struct msc_iter *msc_iter_install(struct msc *msc) @@ -461,7 +453,6 @@ static struct msc_iter *msc_iter_install(struct msc *msc) goto unlock; } - msc_iter_init(iter); iter->msc = msc; list_add_tail(&iter->entry, &msc->iter_list); @@ -482,10 +473,10 @@ static void msc_iter_remove(struct msc_iter *iter, struct msc *msc) static void msc_iter_block_start(struct msc_iter *iter) { - if (iter->start_block != -1) + if (iter->start_block) return; - iter->start_block = msc_win_oldest_block(iter->win); + iter->start_block = msc_win_oldest_sg(iter->win); iter->block = iter->start_block; iter->wrap_count = 0; @@ -509,7 +500,7 @@ static int msc_iter_win_start(struct msc_iter *iter, struct msc *msc) return -EINVAL; iter->win = iter->start_win; - iter->start_block = -1; + iter->start_block = NULL; msc_iter_block_start(iter); @@ -519,7 +510,7 @@ static int msc_iter_win_start(struct msc_iter *iter, struct msc *msc) static int msc_iter_win_advance(struct msc_iter *iter) { iter->win = msc_next_window(iter->win); - iter->start_block = -1; + iter->start_block = NULL; if (iter->win == iter->start_win) { iter->eof++; @@ -549,8 +540,10 @@ static int msc_iter_block_advance(struct msc_iter *iter) return msc_iter_win_advance(iter); /* block advance */ - if (++iter->block == iter->win->nr_segs) - iter->block = 0; + if (sg_is_last(iter->block)) + iter->block = msc_win_base_sg(iter->win); + else + iter->block = sg_next(iter->block); /* no wrapping, sanity check in case there is no last written block */ if (!iter->wrap_count && iter->block == iter->start_block) @@ -655,14 +648,15 @@ next_block: static void msc_buffer_clear_hw_header(struct msc *msc) { struct msc_window *win; + struct scatterlist *sg; list_for_each_entry(win, &msc->win_list, entry) { unsigned int blk; size_t hw_sz = sizeof(struct msc_block_desc) - offsetof(struct msc_block_desc, hw_tag); - for (blk = 0; blk < win->nr_segs; blk++) { - struct msc_block_desc *bdesc = msc_win_block(win, blk); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { + struct msc_block_desc *bdesc = sg_virt(sg); memset(&bdesc->hw_tag, 0, hw_sz); } @@ -1005,10 +999,9 @@ static int __msc_buffer_win_alloc(struct msc_window *win, return nr_segs; err_nomem: - for (i--; i >= 0; i--) + for_each_sg(win->sgt->sgl, sg_ptr, i, ret) dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, - msc_win_block(win, i), - msc_win_baddr(win, i)); + sg_virt(sg_ptr), sg_dma_address(sg_ptr)); sg_free_table(win->sgt); @@ -1018,20 +1011,26 @@ err_nomem: #ifdef CONFIG_X86 static void msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) { + struct scatterlist *sg_ptr; int i; - for (i = 0; i < nr_segs; i++) + for_each_sg(win->sgt->sgl, sg_ptr, nr_segs, i) { /* Set the page as uncached */ - set_memory_uc((unsigned long)msc_win_block(win, i), 1); + set_memory_uc((unsigned long)sg_virt(sg_ptr), + PFN_DOWN(sg_ptr->length)); + } } static void msc_buffer_set_wb(struct msc_window *win) { + struct scatterlist *sg_ptr; int i; - for (i = 0; i < win->nr_segs; i++) + for_each_sg(win->sgt->sgl, sg_ptr, win->nr_segs, i) { /* Reset the page to write-back */ - set_memory_wb((unsigned long)msc_win_block(win, i), 1); + set_memory_wb((unsigned long)sg_virt(sg_ptr), + PFN_DOWN(sg_ptr->length)); + } } #else /* !X86 */ static inline void @@ -1057,13 +1056,6 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) if (!nr_blocks) return 0; - /* - * This limitation hold as long as we need random access to the - * block. When that changes, this can go away. - */ - if (nr_blocks > SG_MAX_SINGLE_ALLOC) - return -EINVAL; - win = kzalloc(sizeof(*win), GFP_KERNEL); if (!win) return -ENOMEM; @@ -1096,8 +1088,8 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) win->nr_blocks = nr_blocks; if (list_empty(&msc->win_list)) { - msc->base = msc_win_block(win, 0); - msc->base_addr = msc_win_baddr(win, 0); + msc->base = msc_win_base(win); + msc->base_addr = msc_win_base_dma(win); msc->cur_win = win; } @@ -1114,14 +1106,15 @@ err_nomem: static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win) { + struct scatterlist *sg; int i; - for (i = 0; i < win->nr_segs; i++) { - struct page *page = sg_page(&win->sgt->sgl[i]); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, i) { + struct page *page = sg_page(sg); page->mapping = NULL; dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, - msc_win_block(win, i), msc_win_baddr(win, i)); + sg_virt(sg), sg_dma_address(sg)); } sg_free_table(win->sgt); } @@ -1167,6 +1160,7 @@ static void msc_buffer_relink(struct msc *msc) /* call with msc::mutex locked */ list_for_each_entry(win, &msc->win_list, entry) { + struct scatterlist *sg; unsigned int blk; u32 sw_tag = 0; @@ -1182,12 +1176,12 @@ static void msc_buffer_relink(struct msc *msc) next_win = list_next_entry(win, entry); } - for (blk = 0; blk < win->nr_segs; blk++) { - struct msc_block_desc *bdesc = msc_win_block(win, blk); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { + struct msc_block_desc *bdesc = sg_virt(sg); memset(bdesc, 0, sizeof(*bdesc)); - bdesc->next_win = msc_win_bpfn(next_win, 0); + bdesc->next_win = msc_win_base_pfn(next_win); /* * Similarly to last window, last block should point @@ -1195,13 +1189,15 @@ static void msc_buffer_relink(struct msc *msc) */ if (blk == win->nr_segs - 1) { sw_tag |= MSC_SW_TAG_LASTBLK; - bdesc->next_blk = msc_win_bpfn(win, 0); + bdesc->next_blk = msc_win_base_pfn(win); } else { - bdesc->next_blk = msc_win_bpfn(win, blk + 1); + dma_addr_t addr = sg_dma_address(sg_next(sg)); + + bdesc->next_blk = PFN_DOWN(addr); } bdesc->sw_tag = sw_tag; - bdesc->block_sz = msc_win_actual_bsz(win, blk) / 64; + bdesc->block_sz = sg->length / 64; } } @@ -1360,6 +1356,7 @@ static int msc_buffer_free_unless_used(struct msc *msc) static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff) { struct msc_window *win; + struct scatterlist *sg; unsigned int blk; if (msc->mode == MSC_MODE_SINGLE) @@ -1374,9 +1371,9 @@ static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff) found: pgoff -= win->pgoff; - for (blk = 0; blk < win->nr_segs; blk++) { - struct page *page = sg_page(&win->sgt->sgl[blk]); - size_t pgsz = PFN_DOWN(msc_win_actual_bsz(win, blk)); + for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { + struct page *page = sg_page(sg); + size_t pgsz = PFN_DOWN(sg->length); if (pgoff < pgsz) return page + pgoff; @@ -1680,8 +1677,8 @@ static void msc_win_switch(struct msc *msc) else msc->cur_win = list_next_entry(msc->cur_win, entry); - msc->base = msc_win_block(msc->cur_win, 0); - msc->base_addr = msc_win_baddr(msc->cur_win, 0); + msc->base = msc_win_base(msc->cur_win); + msc->base_addr = msc_win_base_dma(msc->cur_win); intel_th_trace_switch(msc->thdev); } -- cgit v1.2.3 From ed5207afc580937cd6cc671357fe1119f1b5b0f5 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Jul 2019 17:14:24 +0300 Subject: intel_th: msu: Prevent freeing buffers while locked windows exist We already prevent freeing buffers via sysfs interface in case there are existing users or if trace is active. Treat the existence of locked windows similarly and return -EBUSY on attempts to free the buffer. When the last window is unlocked, the freeing will succeed. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190705141425.19894-5-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index a6c0eb09c515..b200d9d1c7a0 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -724,6 +724,11 @@ static int msc_win_set_lockout(struct msc_window *win, win->lockout = new; + if (old == expect && new == WIN_LOCKED) + atomic_inc(&win->msc->user_count); + else if (old == expect && old == WIN_LOCKED) + atomic_dec(&win->msc->user_count); + unlock: spin_unlock_irqrestore(&win->lo_lock, flags); -- cgit v1.2.3 From 092f47c383adbff4d873dcb7f0887954bd407a08 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Jul 2019 17:14:25 +0300 Subject: intel_th: msu: Preserve pre-existing buffer configuration The MSU configuration registers may contain buffer address/size set by the BIOS or an external hardware debugger, which may want to take over tracing from the driver when the driver is not actively tracing. Preserve these settings when not actively tracing. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20190705141425.19894-6-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index b200d9d1c7a0..fc9f15f36ad4 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -143,6 +143,8 @@ struct msc { unsigned int single_wrap : 1; void *base; dma_addr_t base_addr; + u32 orig_addr; + u32 orig_sz; /* <0: no buffer, 0: no users, >0: active users */ atomic_t user_count; @@ -767,6 +769,9 @@ static int msc_configure(struct msc *msc) msc_buffer_clear_hw_header(msc); } + msc->orig_addr = ioread32(msc->reg_base + REG_MSU_MSC0BAR); + msc->orig_sz = ioread32(msc->reg_base + REG_MSU_MSC0SIZE); + reg = msc->base_addr >> PAGE_SHIFT; iowrite32(reg, msc->reg_base + REG_MSU_MSC0BAR); @@ -841,8 +846,8 @@ static void msc_disable(struct msc *msc) msc->enabled = 0; - iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR); - iowrite32(0, msc->reg_base + REG_MSU_MSC0SIZE); + iowrite32(msc->orig_addr, msc->reg_base + REG_MSU_MSC0BAR); + iowrite32(msc->orig_sz, msc->reg_base + REG_MSU_MSC0SIZE); dev_dbg(msc_dev(msc), "MSCnNWSA: %08x\n", ioread32(msc->reg_base + REG_MSU_MSC0NWSA)); -- cgit v1.2.3 From 3bf8c48ca7272c0ffabfa54a4a8b06b1f8416dfd Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 29 Aug 2019 14:28:26 -0600 Subject: coresight: etm4x: Two function calls less Avoid an extra function call in two function implementations by using a ternary operator instead of a conditional statement. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-2-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a0365e23678e..219c10eb752c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -296,11 +296,8 @@ static ssize_t mode_store(struct device *dev, spin_lock(&drvdata->spinlock); config->mode = val & ETMv4_MODE_ALL; - - if (config->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); + etm4_set_mode_exclude(drvdata, + config->mode & ETM_MODE_EXCLUDE ? true : false); if (drvdata->instrp0 == true) { /* start by clearing instruction P0 field */ @@ -999,10 +996,8 @@ static ssize_t addr_range_store(struct device *dev, * Program include or exclude control bits for vinst or vdata * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE */ - if (config->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); + etm4_set_mode_exclude(drvdata, + config->mode & ETM_MODE_EXCLUDE ? true : false); spin_unlock(&drvdata->spinlock); return size; -- cgit v1.2.3 From 17b4add0d4e01ec1eeb1d0c5fc96d4624d02fe54 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 29 Aug 2019 14:28:27 -0600 Subject: coresight: etm4x: Add ETM PIDs for SDM845 and MSM8996 Instead of overriding the peripheral id(PID) check in AMBA by hardcoding them in DT, add the PIDs to the ETM4x driver. Here we use Unique Component Identifier(UCI) for MSM8996 since the ETM and CPU debug module shares the same PIDs. SDM845 does not support CPU debug module. Signed-off-by: Sai Prakash Ranjan Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-3-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 7bcac8896fc1..28bcc0e58d7a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1192,11 +1192,15 @@ static struct amba_cs_uci_id uci_id_etm4[] = { }; static const struct amba_id etm4_ids[] = { - CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */ - CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */ - CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ - CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ - CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4), /* Cortex-A35 */ + CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */ + CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */ + CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ + CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ + CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4),/* Cortex-A35 */ + CS_AMBA_UCI_ID(0x000f0205, uci_id_etm4),/* Qualcomm Kryo */ + CS_AMBA_UCI_ID(0x000f0211, uci_id_etm4),/* Qualcomm Kryo */ + CS_AMBA_ID(0x000bb802), /* Qualcomm Kryo 385 Cortex-A55 */ + CS_AMBA_ID(0x000bb803), /* Qualcomm Kryo 385 Cortex-A75 */ {}, }; -- cgit v1.2.3 From df4473fadcfe53fcab212b167f4df20bbeb175db Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 29 Aug 2019 14:28:28 -0600 Subject: coresight: cpu-debug: Add support for Qualcomm Kryo Add support for coresight CPU debug module on Qualcomm Kryo CPUs. This patch adds the UCI entries for Kryo CPUs found on MSM8996 which shares the same PIDs as ETMs. Without this, below error is observed on MSM8996: [ 5.429867] OF: graph: no port node found in /soc/debug@3810000 [ 5.429938] coresight-etm4x: probe of 3810000.debug failed with error -22 [ 5.435415] coresight-cpu-debug 3810000.debug: Coresight debug-CPU0 initialized [ 5.446474] OF: graph: no port node found in /soc/debug@3910000 [ 5.448927] coresight-etm4x: probe of 3910000.debug failed with error -22 [ 5.454681] coresight-cpu-debug 3910000.debug: Coresight debug-CPU1 initialized [ 5.487765] OF: graph: no port node found in /soc/debug@3a10000 [ 5.488007] coresight-etm4x: probe of 3a10000.debug failed with error -22 [ 5.493024] coresight-cpu-debug 3a10000.debug: Coresight debug-CPU2 initialized [ 5.501802] OF: graph: no port node found in /soc/debug@3b10000 [ 5.512901] coresight-etm4x: probe of 3b10000.debug failed with error -22 [ 5.513192] coresight-cpu-debug 3b10000.debug: Coresight debug-CPU3 initialized Signed-off-by: Sai Prakash Ranjan Tested-by: Leo Yan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-4-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-cpu-debug.c | 33 +++++++++++------------ drivers/hwtracing/coresight/coresight-priv.h | 10 +++---- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 2463aa7ab4f6..96544b348c27 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -646,24 +646,23 @@ static int debug_remove(struct amba_device *adev) return 0; } +static const struct amba_cs_uci_id uci_id_debug[] = { + { + /* CPU Debug UCI data */ + .devarch = 0x47706a15, + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000015, + } +}; + static const struct amba_id debug_ids[] = { - { /* Debug for Cortex-A53 */ - .id = 0x000bbd03, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A57 */ - .id = 0x000bbd07, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A72 */ - .id = 0x000bbd08, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A73 */ - .id = 0x000bbd09, - .mask = 0x000fffff, - }, - { 0, 0 }, + CS_AMBA_ID(0x000bbd03), /* Cortex-A53 */ + CS_AMBA_ID(0x000bbd07), /* Cortex-A57 */ + CS_AMBA_ID(0x000bbd08), /* Cortex-A72 */ + CS_AMBA_ID(0x000bbd09), /* Cortex-A73 */ + CS_AMBA_UCI_ID(0x000f0205, uci_id_debug), /* Qualcomm Kryo */ + CS_AMBA_UCI_ID(0x000f0211, uci_id_debug), /* Qualcomm Kryo */ + {}, }; static struct amba_driver debug_driver = { diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 7d401790dd7e..41ae5863104d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -185,11 +185,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } } /* coresight AMBA ID, full UCI structure: id table entry. */ -#define CS_AMBA_UCI_ID(pid, uci_ptr) \ - { \ - .id = pid, \ - .mask = 0x000fffff, \ - .data = uci_ptr \ +#define CS_AMBA_UCI_ID(pid, uci_ptr) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = (void *)uci_ptr \ } /* extract the data value from a UCI structure given amba_id pointer. */ -- cgit v1.2.3 From 5774a34fc802dbec0c1e7afd0c6737a0233e64ec Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Aug 2019 14:28:29 -0600 Subject: coresight: etr_buf: Consolidate refcount initialization We now use refcounts for the etr_buf users. Let us initialize it while we allocate it. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-5-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 17006705287a..3116d1f28e66 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -871,6 +871,7 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, return ERR_PTR(rc); } + refcount_set(&etr_buf->refcount, 1); dev_dbg(dev, "allocated buffer of size %ldKB in mode %d\n", (unsigned long)size >> 10, etr_buf->mode); return etr_buf; @@ -1263,8 +1264,6 @@ retry: if (IS_ERR(etr_buf)) return etr_buf; - refcount_set(&etr_buf->refcount, 1); - /* Now that we have a buffer, add it to the IDR. */ mutex_lock(&drvdata->idr_mutex); ret = idr_alloc(&drvdata->idr, etr_buf, pid, pid + 1, GFP_KERNEL); @@ -1291,19 +1290,11 @@ get_perf_etr_buf_per_thread(struct tmc_drvdata *drvdata, struct perf_event *event, int nr_pages, void **pages, bool snapshot) { - struct etr_buf *etr_buf; - /* * In per-thread mode the etr_buf isn't shared, so just go ahead * with memory allocation. */ - etr_buf = alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); - if (IS_ERR(etr_buf)) - goto out; - - refcount_set(&etr_buf->refcount, 1); -out: - return etr_buf; + return alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); } static struct etr_buf * -- cgit v1.2.3 From f52ff9b7d64953b9a1a03faaf797d23b5c1d10d0 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Aug 2019 14:28:30 -0600 Subject: coresight: tmc-etr: Handle memory errors We have so far ignored the memory errors, assuming that we have perfect hardware and driver. Let us handle the memory errors reported by the TMC ETR in status and truncate the buffer. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose [Removed ASCII smiley face from changelog] Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-6-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 1 + 2 files changed, 14 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 3116d1f28e66..2246c1e6744a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -928,6 +928,19 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) rrp = tmc_read_rrp(drvdata); rwp = tmc_read_rwp(drvdata); status = readl_relaxed(drvdata->base + TMC_STS); + + /* + * If there were memory errors in the session, truncate the + * buffer. + */ + if (WARN_ON_ONCE(status & TMC_STS_MEMERR)) { + dev_dbg(&drvdata->csdev->dev, + "tmc memory error detected, truncating buffer\n"); + etr_buf->len = 0; + etr_buf->full = 0; + return; + } + etr_buf->full = status & TMC_STS_FULL; WARN_ON(!etr_buf->ops || !etr_buf->ops->sync); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 1ed50411cc3c..95d2e2747970 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -47,6 +47,7 @@ #define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) +#define TMC_STS_MEMERR BIT(5) /* * TMC_AXICTL - 0x110 * -- cgit v1.2.3 From 8a4bc4f195044004520e38da7b8a52a76ccc9945 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Aug 2019 14:28:31 -0600 Subject: coresight: tmc-etr: Check if non-secure access is enabled CoreSight TMC-ETR must have the non-secure invasive debug access enabled for use by self-hosted tracing. Without it, there is no point in enabling the ETR. So, let us check it in the TMC_AUTHSTATUS register and fail the probe if it is disabled. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-7-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc.c | 12 ++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index be37aff573b4..3055bf8e2236 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -236,6 +236,7 @@ coresight_tmc_reg(ffcr, TMC_FFCR); coresight_tmc_reg(mode, TMC_MODE); coresight_tmc_reg(pscr, TMC_PSCR); coresight_tmc_reg(axictl, TMC_AXICTL); +coresight_tmc_reg(authstatus, TMC_AUTHSTATUS); coresight_tmc_reg(devid, CORESIGHT_DEVID); coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); @@ -255,6 +256,7 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_devid.attr, &dev_attr_dba.attr, &dev_attr_axictl.attr, + &dev_attr_authstatus.attr, NULL, }; @@ -342,6 +344,13 @@ static inline bool tmc_etr_can_use_sg(struct device *dev) return fwnode_property_present(dev->fwnode, "arm,scatter-gather"); } +static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata) +{ + u32 auth = readl_relaxed(drvdata->base + TMC_AUTHSTATUS); + + return (auth & TMC_AUTH_NSID_MASK) == 0x3; +} + /* Detect and initialise the capabilities of a TMC ETR */ static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) { @@ -349,6 +358,9 @@ static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) u32 dma_mask = 0; struct tmc_drvdata *drvdata = dev_get_drvdata(parent); + if (!tmc_etr_has_non_secure_access(drvdata)) + return -EACCES; + /* Set the unadvertised capabilities */ tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 95d2e2747970..4c59f2a4ad0e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -39,6 +39,7 @@ #define TMC_ITATBCTR2 0xef0 #define TMC_ITATBCTR1 0xef4 #define TMC_ITATBCTR0 0xef8 +#define TMC_AUTHSTATUS 0xfb8 /* register description */ /* TMC_CTL - 0x020 */ @@ -90,6 +91,8 @@ #define TMC_DEVID_AXIAW_SHIFT 17 #define TMC_DEVID_AXIAW_MASK 0x7f +#define TMC_AUTH_NSID_MASK GENMASK(1, 0) + enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, -- cgit v1.2.3 From 82451bbd540f5254b1f5b655d2183469f2b0a7a7 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Aug 2019 14:28:32 -0600 Subject: coresight: Convert pr_warn to dev_warn for obsolete bindings We warn the users of obsolete bindings in the DT for coresight replicator and funnel drivers. However we use pr_warn_once() which doesn't give a clue about which device it is bound to. Let us use dev_warn_once() to give the context. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-8-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-funnel.c | 2 +- drivers/hwtracing/coresight/coresight-replicator.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fa97cb9ab4f9..84ca30f4e5ec 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -192,7 +192,7 @@ static int funnel_probe(struct device *dev, struct resource *res) if (is_of_node(dev_fwnode(dev)) && of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) - pr_warn_once("Uses OBSOLETE CoreSight funnel binding\n"); + dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n"); desc.name = coresight_alloc_device_name(&funnel_devs, dev); if (!desc.name) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b7d6d59d56db..b29ba640eb25 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -184,7 +184,8 @@ static int replicator_probe(struct device *dev, struct resource *res) if (is_of_node(dev_fwnode(dev)) && of_device_is_compatible(dev->of_node, "arm,coresight-replicator")) - pr_warn_once("Uses OBSOLETE CoreSight replicator binding\n"); + dev_warn_once(dev, + "Uses OBSOLETE CoreSight replicator binding\n"); desc.name = coresight_alloc_device_name(&replicator_devs, dev); if (!desc.name) -- cgit v1.2.3 From 6c87bedc9e9c922093bec602687d991669eccc64 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Aug 2019 14:28:33 -0600 Subject: coresight: acpi: Static funnel support The ACPI bindings for CoreSight has been updated to add the device id for non-programmable CoreSight funnels (aka static funnels) as of v1.1 [0]. Add the ACPI id for static funnels in the driver. [0] https://static.docs.arm.com/den0067/a/DEN0067_CoreSight_ACPI_1.1.pdf Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-9-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-funnel.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 84ca30f4e5ec..05f7896c3a01 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -5,6 +5,7 @@ * Description: CoreSight Funnel driver */ +#include #include #include #include @@ -302,11 +303,19 @@ static const struct of_device_id static_funnel_match[] = { {} }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id static_funnel_ids[] = { + {"ARMHC9FE", 0}, + {}, +}; +#endif + static struct platform_driver static_funnel_driver = { .probe = static_funnel_probe, .driver = { .name = "coresight-static-funnel", .of_match_table = static_funnel_match, + .acpi_match_table = ACPI_PTR(static_funnel_ids), .pm = &funnel_dev_pm_ops, .suppress_bind_attrs = true, }, -- cgit v1.2.3 From 6d7651015c16e1ee3576edbb5e88ba24f0f1fe7a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Aug 2019 14:28:34 -0600 Subject: coresight: etm4x: Remove superfluous setting of os_unlock In addition to unlocking the OS lock, etm4_os_unlock will also set the os_unlock flag. Therefore let's avoid unnecessarily setting os_unlock flag outside of this function. Signed-off-by: Andrew Murray Reviewed-by: Suzuki K Poulose [Fixed capital letter for "remove" in the title] Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-10-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 28bcc0e58d7a..7ad15651e069 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1047,10 +1047,8 @@ static int etm4_starting_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) { + if (!etmdrvdata[cpu]->os_unlock) etm4_os_unlock(etmdrvdata[cpu]); - etmdrvdata[cpu]->os_unlock = true; - } if (local_read(&etmdrvdata[cpu]->mode)) etm4_enable_hw(etmdrvdata[cpu]); -- cgit v1.2.3 From 1004ce4c255fc3eb3ad9145ddd53547d1b7ce327 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Aug 2019 14:28:35 -0600 Subject: coresight: etm4x: Use explicit barriers on enable/disable Synchronization is recommended before disabling the trace registers to prevent any start or stop points being speculative at the point of disabling the unit (section 7.3.77 of ARM IHI 0064D). Synchronization is also recommended after programming the trace registers to ensure all updates are committed prior to normal code resuming (section 4.3.7 of ARM IHI 0064D). Let's ensure these syncronization points are present in the code and clearly commented. Note that we could rely on the barriers in CS_LOCK and coresight_disclaim_device_unlocked or the context switch to user space - however coresight may be of use in the kernel. On armv8 the mb macro is defined as dsb(sy) - Given that the etm4x is only used on armv8 let's directly use dsb(sy) instead of mb(). This removes some ambiguity and makes it easier to correlate the code with the TRM. Signed-off-by: Andrew Murray Reviewed-by: Suzuki K Poulose [Fixed capital letter for "use" in title] Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-11-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 7ad15651e069..ec9468880c71 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -188,6 +188,13 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); + /* + * As recommended by section 4.3.7 ("Synchronization when using the + * memory-mapped interface") of ARM IHI 0064D + */ + dsb(sy); + isb(); + done: CS_LOCK(drvdata->base); @@ -453,8 +460,12 @@ static void etm4_disable_hw(void *info) /* EN, bit[0] Trace unit enable bit */ control &= ~0x1; - /* make sure everything completes before disabling */ - mb(); + /* + * Make sure everything completes before disabling, as recommended + * by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register, + * SSTATUS") of ARM IHI 0064D + */ + dsb(sy); isb(); writel_relaxed(control, drvdata->base + TRCPRGCTLR); -- cgit v1.2.3 From 08d2ddaa585091d555a31d6ed538c6cedba060eb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Aug 2019 14:28:36 -0600 Subject: coresight: etm4x: use module_param instead of module_param_named Given that the user-exposed module parameter for 'boot_enable' matches the variable that it sets, let's use module_param instead of module_param_named. Let's also use octal permissions (checkpatch recommends this) and provide a module parameter description. Signed-off-by: Andrew Murray Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-12-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index ec9468880c71..615bdbf7c9b7 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,7 +34,8 @@ #include "coresight-etm-perf.h" static int boot_enable; -module_param_named(boot_enable, boot_enable, int, S_IRUGO); +module_param(boot_enable, int, 0444); +MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); /* The number of ETMv4 currently registered */ static int etm4_count; -- cgit v1.2.3 From 89e89b05ef5b58ab33070cc5b59d74ca8cff86fe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 29 Aug 2019 14:28:37 -0600 Subject: coresight: etm4x: improve clarity of etm4_os_unlock comment To improve clarity, let's update the comment for etm4_os_unlock to use the name of the register as per the ETM architecture specification. The existing comment is also misleading as it suggests any value written to TRCOSLAR unlocks the trace registers, however it must be '0' - let's also correct this. Signed-off-by: Andrew Murray Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-13-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 615bdbf7c9b7..a128b5063f46 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -48,7 +48,7 @@ static enum cpuhp_state hp_online; static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { - /* Writing any value to ETMOSLAR unlocks the trace registers */ + /* Writing 0 to TRCOSLAR unlocks the trace registers */ writel_relaxed(0x0, drvdata->base + TRCOSLAR); drvdata->os_unlock = true; isb(); -- cgit v1.2.3 From 13af88f312fc57becacfcbb1cc77f844281a30ec Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 29 Aug 2019 14:28:38 -0600 Subject: coresight: tmc-etr: Fix updating buffer in not-snapshot mode. TMC etr always copies all available data to perf aux buffer, which may exceed the available space in perf aux buffer. It isn't suitable for not-snapshot mode, because: 1) It may overwrite previously written data. 2) It may make the perf_event_mmap_page->aux_head report having more or less data than the reality. So change to only copy the latest data fitting the available space in perf aux buffer. Signed-off-by: Yabin Cui Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-14-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 2246c1e6744a..f835a3c78b37 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1414,9 +1414,10 @@ free_etr_perf_buffer: * tmc_etr_sync_perf_buffer: Copy the actual trace data from the hardware * buffer to the perf ring buffer. */ -static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) +static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf, + unsigned long to_copy) { - long bytes, to_copy; + long bytes; long pg_idx, pg_offset, src_offset; unsigned long head = etr_perf->head; char **dst_pages, *src_buf; @@ -1426,8 +1427,7 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); dst_pages = (char **)etr_perf->pages; - src_offset = etr_buf->offset; - to_copy = etr_buf->len; + src_offset = etr_buf->offset + etr_buf->len - to_copy; while (to_copy > 0) { /* @@ -1438,6 +1438,8 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) * 3) what is available in the destination page. * in one iteration. */ + if (src_offset >= etr_buf->size) + src_offset -= etr_buf->size; bytes = tmc_etr_buf_get_data(etr_buf, src_offset, to_copy, &src_buf); if (WARN_ON_ONCE(bytes <= 0)) @@ -1458,8 +1460,6 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) /* Move source pointers */ src_offset += bytes; - if (src_offset >= etr_buf->size) - src_offset -= etr_buf->size; } } @@ -1505,7 +1505,11 @@ tmc_update_etr_buffer(struct coresight_device *csdev, spin_unlock_irqrestore(&drvdata->spinlock, flags); size = etr_buf->len; - tmc_etr_sync_perf_buffer(etr_perf); + if (!etr_perf->snapshot && size > handle->size) { + size = handle->size; + lost = true; + } + tmc_etr_sync_perf_buffer(etr_perf, size); /* * In snapshot mode we simply increment the head by the number of byte -- cgit v1.2.3 From bbedcb91cc3bf252e6031e199ab3d1f07107f7c5 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Thu, 29 Aug 2019 14:28:39 -0600 Subject: coresight: tmc-etr: Fix perf_data check When tracing etm data of multiple threads on multiple cpus through perf interface, each cpu has a unique etr_perf_buffer while sharing the same etr device. There is no guarantee that the last cpu starts etm tracing also stops last. This makes perf_data check fail. Fix it by checking etr_buf instead of etr_perf_buffer. Also move the code setting and clearing perf_buf to more suitable places. Fixes: 3147da92a8a8 ("coresight: tmc-etr: Allocate and free ETR memory buffers for CPU-wide scenarios") Signed-off-by: Yabin Cui Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20190829202842.580-15-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 8 ++++---- drivers/hwtracing/coresight/coresight-tmc.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index f835a3c78b37..4f000a03152e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1488,7 +1488,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, goto out; } - if (WARN_ON(drvdata->perf_data != etr_perf)) { + if (WARN_ON(drvdata->perf_buf != etr_buf)) { lost = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; @@ -1500,8 +1500,6 @@ tmc_update_etr_buffer(struct coresight_device *csdev, tmc_sync_etr_buf(drvdata); CS_LOCK(drvdata->base); - /* Reset perf specific data */ - drvdata->perf_data = NULL; spin_unlock_irqrestore(&drvdata->spinlock, flags); size = etr_buf->len; @@ -1564,7 +1562,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) } etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf); - drvdata->perf_data = etr_perf; /* * No HW configuration is needed if the sink is already in @@ -1580,6 +1577,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) /* Associate with monitored process. */ drvdata->pid = pid; drvdata->mode = CS_MODE_PERF; + drvdata->perf_buf = etr_perf->etr_buf; atomic_inc(csdev->refcnt); } @@ -1625,6 +1623,8 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) /* Dissociate from monitored process. */ drvdata->pid = -1; drvdata->mode = CS_MODE_DISABLED; + /* Reset perf specific data */ + drvdata->perf_buf = NULL; spin_unlock_irqrestore(&drvdata->spinlock, flags); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 4c59f2a4ad0e..9dbcdf453e22 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -182,8 +182,8 @@ struct etr_buf { * device configuration register (DEVID) * @idr: Holds etr_bufs allocated for this ETR. * @idr_mutex: Access serialisation for idr. - * @perf_data: PERF buffer for ETR. - * @sysfs_data: SYSFS buffer for ETR. + * @sysfs_buf: SYSFS buffer for ETR. + * @perf_buf: PERF buffer for ETR. */ struct tmc_drvdata { void __iomem *base; @@ -206,7 +206,7 @@ struct tmc_drvdata { struct idr idr; struct mutex idr_mutex; struct etr_buf *sysfs_buf; - void *perf_data; + struct etr_buf *perf_buf; }; struct etr_buf_operations { -- cgit v1.2.3 From 00bb485ce7b8d1186a5166fc9e38822797fae7d4 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 29 Aug 2019 14:28:40 -0600 Subject: coresight: tmc: Make memory width mask computation into a function Make the computation of a memory mask representing the width of the memory bus into a function so that it can be re-used by the ETR driver. Signed-off-by: Mathieu Poirier Reviewed-by: Leo Yan Link: https://lore.kernel.org/r/20190829202842.580-16-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 23 ++------------------ drivers/hwtracing/coresight/coresight-tmc.c | 28 +++++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 1 + 3 files changed, 31 insertions(+), 21 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 23b7ff00af5c..807416b75ecc 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -479,30 +479,11 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, * traces. */ if (!buf->snapshot && to_read > handle->size) { - u32 mask = 0; - - /* - * The value written to RRP must be byte-address aligned to - * the width of the trace memory databus _and_ to a frame - * boundary (16 byte), whichever is the biggest. For example, - * for 32-bit, 64-bit and 128-bit wide trace memory, the four - * LSBs must be 0s. For 256-bit wide trace memory, the five - * LSBs must be 0s. - */ - switch (drvdata->memwidth) { - case TMC_MEM_INTF_WIDTH_32BITS: - case TMC_MEM_INTF_WIDTH_64BITS: - case TMC_MEM_INTF_WIDTH_128BITS: - mask = GENMASK(31, 4); - break; - case TMC_MEM_INTF_WIDTH_256BITS: - mask = GENMASK(31, 5); - break; - } + u32 mask = tmc_get_memwidth_mask(drvdata); /* * Make sure the new size is aligned in accordance with the - * requirement explained above. + * requirement explained in function tmc_get_memwidth_mask(). */ to_read = handle->size & mask; /* Move the RAM read pointer up */ diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3055bf8e2236..1cf82fa58289 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -70,6 +70,34 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata) writel_relaxed(0x0, drvdata->base + TMC_CTL); } +u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata) +{ + u32 mask = 0; + + /* + * When moving RRP or an offset address forward, the new values must + * be byte-address aligned to the width of the trace memory databus + * _and_ to a frame boundary (16 byte), whichever is the biggest. For + * example, for 32-bit, 64-bit and 128-bit wide trace memory, the four + * LSBs must be 0s. For 256-bit wide trace memory, the five LSBs must + * be 0s. + */ + switch (drvdata->memwidth) { + case TMC_MEM_INTF_WIDTH_32BITS: + /* fallthrough */ + case TMC_MEM_INTF_WIDTH_64BITS: + /* fallthrough */ + case TMC_MEM_INTF_WIDTH_128BITS: + mask = GENMASK(31, 4); + break; + case TMC_MEM_INTF_WIDTH_256BITS: + mask = GENMASK(31, 5); + break; + } + + return mask; +} + static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 9dbcdf453e22..71de978575f3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -255,6 +255,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); void tmc_flush_and_stop(struct tmc_drvdata *drvdata); void tmc_enable_hw(struct tmc_drvdata *drvdata); void tmc_disable_hw(struct tmc_drvdata *drvdata); +u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata); /* ETB/ETF functions */ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata); -- cgit v1.2.3 From 3507d231a499e27c4bac8a47169b74ec7ef87292 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 29 Aug 2019 14:28:41 -0600 Subject: coresight: tmc-etr: Decouple buffer sync and barrier packet insertion If less space is available in the perf ring buffer than the ETR buffer, barrier packets inserted in the trace stream by tmc_sync_etr_buf() are skipped over when the head of the buffer is moved forward, resulting in traces that can't be decoded. This patch decouples the process of syncing ETR buffers and the addition of barrier packets in order to perform the latter once the offset in the trace buffer has been properly computed. Signed-off-by: Mathieu Poirier Reviewed-by: Leo Yan Link: https://lore.kernel.org/r/20190829202842.580-17-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 4f000a03152e..bae47272de98 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -946,10 +946,6 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) WARN_ON(!etr_buf->ops || !etr_buf->ops->sync); etr_buf->ops->sync(etr_buf, rrp, rwp); - - /* Insert barrier packets at the beginning, if there was an overflow */ - if (etr_buf->full) - tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); } static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) @@ -1086,6 +1082,13 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) drvdata->sysfs_buf = NULL; } else { tmc_sync_etr_buf(drvdata); + /* + * Insert barrier packets at the beginning, if there was + * an overflow. + */ + if (etr_buf->full) + tmc_etr_buf_insert_barrier_packet(etr_buf, + etr_buf->offset); } } @@ -1502,11 +1505,16 @@ tmc_update_etr_buffer(struct coresight_device *csdev, CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); + lost = etr_buf->full; size = etr_buf->len; if (!etr_perf->snapshot && size > handle->size) { size = handle->size; lost = true; } + + /* Insert barrier packets at the beginning, if there was an overflow */ + if (lost) + tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); tmc_etr_sync_perf_buffer(etr_perf, size); /* @@ -1517,8 +1525,6 @@ tmc_update_etr_buffer(struct coresight_device *csdev, */ if (etr_perf->snapshot) handle->head += size; - - lost |= etr_buf->full; out: /* * Don't set the TRUNCATED flag in snapshot mode because 1) the -- cgit v1.2.3 From ec13c78d7b45851d21786127ae17193f9a1446fb Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 29 Aug 2019 14:28:42 -0600 Subject: coresight: tmc-etr: Add barrier packets when moving offset forward This patch adds barrier packets in the trace stream when the offset in the data buffer needs to be moved forward. Otherwise the decoder isn't aware of the break in the stream and can't synchronise itself with the trace data. Signed-off-by: Mathieu Poirier Tested-by: Yabin Cui Reviewed-by: Leo Yan Link: https://lore.kernel.org/r/20190829202842.580-18-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 29 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index bae47272de98..625882bc8b08 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1418,10 +1418,11 @@ free_etr_perf_buffer: * buffer to the perf ring buffer. */ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf, + unsigned long src_offset, unsigned long to_copy) { long bytes; - long pg_idx, pg_offset, src_offset; + long pg_idx, pg_offset; unsigned long head = etr_perf->head; char **dst_pages, *src_buf; struct etr_buf *etr_buf = etr_perf->etr_buf; @@ -1430,7 +1431,6 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf, pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); dst_pages = (char **)etr_perf->pages; - src_offset = etr_buf->offset + etr_buf->len - to_copy; while (to_copy > 0) { /* @@ -1478,7 +1478,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, void *config) { bool lost = false; - unsigned long flags, size = 0; + unsigned long flags, offset, size = 0; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etr_perf_buffer *etr_perf = config; struct etr_buf *etr_buf = etr_perf->etr_buf; @@ -1506,16 +1506,35 @@ tmc_update_etr_buffer(struct coresight_device *csdev, spin_unlock_irqrestore(&drvdata->spinlock, flags); lost = etr_buf->full; + offset = etr_buf->offset; size = etr_buf->len; + + /* + * The ETR buffer may be bigger than the space available in the + * perf ring buffer (handle->size). If so advance the offset 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 (!etr_perf->snapshot && size > handle->size) { - size = 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(). + */ + size = handle->size & mask; + offset = etr_buf->offset + etr_buf->len - size; + + if (offset >= etr_buf->size) + offset -= etr_buf->size; lost = true; } /* Insert barrier packets at the beginning, if there was an overflow */ if (lost) tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); - tmc_etr_sync_perf_buffer(etr_perf, size); + tmc_etr_sync_perf_buffer(etr_perf, offset, size); /* * In snapshot mode we simply increment the head by the number of byte -- cgit v1.2.3