Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
On Fri, 13 Jul 2018 at 05:23, Suzuki K Poulose wrote: > > On 12/07/18 21:57, Mathieu Poirier wrote: > > Hi Suzuki, > > > > On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: > >> Add support for using TMC-ETR as backend for ETM perf tracing. > >> We use software double buffering at the moment. i.e, the TMC-ETR > >> uses a separate buffer than the perf ring buffer. The data is > >> copied to the perf ring buffer once a session completes. > >> > >> The TMC-ETR would try to match the larger of perf ring buffer > >> or the ETR buffer size configured via sysfs, scaling down to > >> a minimum limit of 1MB. > >> > >> Cc: Mathieu Poirier > >> Signed-off-by: Suzuki K Poulose > >> --- > >> drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 > >> +++- > >> drivers/hwtracing/coresight/coresight-tmc.h | 2 + > >> 2 files changed, 265 insertions(+), 2 deletions(-) > > >> +/* > >> + * 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) > >> +{ > >> +long bytes, to_copy; > >> +long pg_idx, pg_offset, src_offset; > >> +unsigned long head = etr_perf->head; > >> +char **dst_pages, *src_buf; > >> +struct etr_buf *etr_buf = etr_perf->etr_buf; > >> + > >> +head = PERF_IDX2OFF(etr_perf->head, etr_perf); > > > > I'm puzzled here. Since etr_perf->head is set in tmc_etr_set_perf_buffer() > > to > > the value of handle->head, its value is always within the range of the perf > > ring > > IIUC, the handle->head is not necessarily guaranteed to be within the > aux-ring buffer. I just looked at the ETB code and you are correct. > > > buffer. In tmc_etr_alloc_perf_buffer() etr_perf->nr_pages is set to the > > number > > of pages present in that ring buffer. As such I'm not sure as to why we > > need > > the PERF_IDX2OFF() macro. > > > > It seems to me that "head = etr_perf->head;" above is sufficient. > > > > We need to do the step at some point, which could be moved to set_perf_buffer. That's a good idea. > > >> +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; > >> + > >> +while (to_copy > 0) { > >> +/* > >> + * We can copy minimum of : > > > > s/We can copy minimum of :/In one iteration we can copy a minimum of:/ > > > > If I'm wrong about the PERF_IDX2OFF(), don't bother respinning just for > > that. > > I have some more fixes for handling the different modes (sysfs vs perf), > which I can include in the v2. Very well. > > Suzuki
Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
On Fri, 13 Jul 2018 at 05:23, Suzuki K Poulose wrote: > > On 12/07/18 21:57, Mathieu Poirier wrote: > > Hi Suzuki, > > > > On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: > >> Add support for using TMC-ETR as backend for ETM perf tracing. > >> We use software double buffering at the moment. i.e, the TMC-ETR > >> uses a separate buffer than the perf ring buffer. The data is > >> copied to the perf ring buffer once a session completes. > >> > >> The TMC-ETR would try to match the larger of perf ring buffer > >> or the ETR buffer size configured via sysfs, scaling down to > >> a minimum limit of 1MB. > >> > >> Cc: Mathieu Poirier > >> Signed-off-by: Suzuki K Poulose > >> --- > >> drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 > >> +++- > >> drivers/hwtracing/coresight/coresight-tmc.h | 2 + > >> 2 files changed, 265 insertions(+), 2 deletions(-) > > >> +/* > >> + * 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) > >> +{ > >> +long bytes, to_copy; > >> +long pg_idx, pg_offset, src_offset; > >> +unsigned long head = etr_perf->head; > >> +char **dst_pages, *src_buf; > >> +struct etr_buf *etr_buf = etr_perf->etr_buf; > >> + > >> +head = PERF_IDX2OFF(etr_perf->head, etr_perf); > > > > I'm puzzled here. Since etr_perf->head is set in tmc_etr_set_perf_buffer() > > to > > the value of handle->head, its value is always within the range of the perf > > ring > > IIUC, the handle->head is not necessarily guaranteed to be within the > aux-ring buffer. I just looked at the ETB code and you are correct. > > > buffer. In tmc_etr_alloc_perf_buffer() etr_perf->nr_pages is set to the > > number > > of pages present in that ring buffer. As such I'm not sure as to why we > > need > > the PERF_IDX2OFF() macro. > > > > It seems to me that "head = etr_perf->head;" above is sufficient. > > > > We need to do the step at some point, which could be moved to set_perf_buffer. That's a good idea. > > >> +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; > >> + > >> +while (to_copy > 0) { > >> +/* > >> + * We can copy minimum of : > > > > s/We can copy minimum of :/In one iteration we can copy a minimum of:/ > > > > If I'm wrong about the PERF_IDX2OFF(), don't bother respinning just for > > that. > > I have some more fixes for handling the different modes (sysfs vs perf), > which I can include in the v2. Very well. > > Suzuki
Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
On 12/07/18 21:57, Mathieu Poirier wrote: Hi Suzuki, On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: Add support for using TMC-ETR as backend for ETM perf tracing. We use software double buffering at the moment. i.e, the TMC-ETR uses a separate buffer than the perf ring buffer. The data is copied to the perf ring buffer once a session completes. The TMC-ETR would try to match the larger of perf ring buffer or the ETR buffer size configured via sysfs, scaling down to a minimum limit of 1MB. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 +++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 265 insertions(+), 2 deletions(-) +/* + * 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) +{ + long bytes, to_copy; + long pg_idx, pg_offset, src_offset; + unsigned long head = etr_perf->head; + char **dst_pages, *src_buf; + struct etr_buf *etr_buf = etr_perf->etr_buf; + + head = PERF_IDX2OFF(etr_perf->head, etr_perf); I'm puzzled here. Since etr_perf->head is set in tmc_etr_set_perf_buffer() to the value of handle->head, its value is always within the range of the perf ring IIUC, the handle->head is not necessarily guaranteed to be within the aux-ring buffer. buffer. In tmc_etr_alloc_perf_buffer() etr_perf->nr_pages is set to the number of pages present in that ring buffer. As such I'm not sure as to why we need the PERF_IDX2OFF() macro. It seems to me that "head = etr_perf->head;" above is sufficient. We need to do the step at some point, which could be moved to set_perf_buffer. + 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; + + while (to_copy > 0) { + /* +* We can copy minimum of : s/We can copy minimum of :/In one iteration we can copy a minimum of:/ If I'm wrong about the PERF_IDX2OFF(), don't bother respinning just for that. I have some more fixes for handling the different modes (sysfs vs perf), which I can include in the v2. Suzuki
Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
On 12/07/18 21:57, Mathieu Poirier wrote: Hi Suzuki, On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: Add support for using TMC-ETR as backend for ETM perf tracing. We use software double buffering at the moment. i.e, the TMC-ETR uses a separate buffer than the perf ring buffer. The data is copied to the perf ring buffer once a session completes. The TMC-ETR would try to match the larger of perf ring buffer or the ETR buffer size configured via sysfs, scaling down to a minimum limit of 1MB. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 +++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 265 insertions(+), 2 deletions(-) +/* + * 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) +{ + long bytes, to_copy; + long pg_idx, pg_offset, src_offset; + unsigned long head = etr_perf->head; + char **dst_pages, *src_buf; + struct etr_buf *etr_buf = etr_perf->etr_buf; + + head = PERF_IDX2OFF(etr_perf->head, etr_perf); I'm puzzled here. Since etr_perf->head is set in tmc_etr_set_perf_buffer() to the value of handle->head, its value is always within the range of the perf ring IIUC, the handle->head is not necessarily guaranteed to be within the aux-ring buffer. buffer. In tmc_etr_alloc_perf_buffer() etr_perf->nr_pages is set to the number of pages present in that ring buffer. As such I'm not sure as to why we need the PERF_IDX2OFF() macro. It seems to me that "head = etr_perf->head;" above is sufficient. We need to do the step at some point, which could be moved to set_perf_buffer. + 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; + + while (to_copy > 0) { + /* +* We can copy minimum of : s/We can copy minimum of :/In one iteration we can copy a minimum of:/ If I'm wrong about the PERF_IDX2OFF(), don't bother respinning just for that. I have some more fixes for handling the different modes (sysfs vs perf), which I can include in the v2. Suzuki
Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
Hi Suzuki, On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: > Add support for using TMC-ETR as backend for ETM perf tracing. > We use software double buffering at the moment. i.e, the TMC-ETR > uses a separate buffer than the perf ring buffer. The data is > copied to the perf ring buffer once a session completes. > > The TMC-ETR would try to match the larger of perf ring buffer > or the ETR buffer size configured via sysfs, scaling down to > a minimum limit of 1MB. > > Cc: Mathieu Poirier > Signed-off-by: Suzuki K Poulose > --- > drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 > +++- > drivers/hwtracing/coresight/coresight-tmc.h | 2 + > 2 files changed, 265 insertions(+), 2 deletions(-) > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c > b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index 20a0beb..af921da 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -21,6 +21,28 @@ struct etr_flat_buf { > }; > > /* > + * etr_perf_buffer - Perf buffer used for ETR > + * @etr_buf - Actual buffer used by the ETR > + * @snaphost - Perf session mode > + * @head - handle->head at the beginning of the session. > + * @nr_pages - Number of pages in the ring buffer. > + * @pages- Array of Pages in the ring buffer. > + */ > +struct etr_perf_buffer { > + struct etr_buf *etr_buf; > + boolsnapshot; > + unsigned long head; > + int nr_pages; > + void**pages; > +}; > + > +/* Convert the perf index to an offset within the ETR buffer */ > +#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << > PAGE_SHIFT)) > + > +/* Lower limit for ETR hardware buffer */ > +#define TMC_ETR_PERF_MIN_BUF_SIZESZ_1M > + > +/* > * The TMC ETR SG has a page size of 4K. The SG table contains pointers > * to 4KB buffers. However, the OS may use a PAGE_SIZE different from > * 4K (i.e, 16KB or 64KB). This implies that a single OS page could > @@ -1103,10 +1125,245 @@ static int tmc_enable_etr_sink_sysfs(struct > coresight_device *csdev) > return ret; > } > > +/* > + * tmc_etr_setup_perf_buf: Allocate ETR buffer for use by perf. > + * The size of the hardware buffer is dependent on the size configured > + * via sysfs and the perf ring buffer size. We prefer to allocate the > + * largest possible size, scaling down the size by half until it > + * reaches a minimum limit (1M), beyond which we give up. > + */ > +static struct etr_perf_buffer * > +tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, > +void **pages, bool snapshot) > +{ > + struct etr_buf *etr_buf; > + struct etr_perf_buffer *etr_perf; > + unsigned long size; > + > + etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); > + if (!etr_perf) > + return ERR_PTR(-ENOMEM); > + > + /* > + * Try to match the perf ring buffer size if it is larger > + * than the size requested via sysfs. > + */ > + if ((nr_pages << PAGE_SHIFT) > drvdata->size) { > + etr_buf = tmc_alloc_etr_buf(drvdata, (nr_pages << PAGE_SHIFT), > + 0, node, NULL); > + if (!IS_ERR(etr_buf)) > + goto done; > + } > + > + /* > + * Else switch to configured size for this ETR > + * and scale down until we hit the minimum limit. > + */ > + size = drvdata->size; > + do { > + etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL); > + if (!IS_ERR(etr_buf)) > + goto done; > + size /= 2; > + } while (size >= TMC_ETR_PERF_MIN_BUF_SIZE); > + > + kfree(etr_perf); > + return ERR_PTR(-ENOMEM); > + > +done: > + etr_perf->etr_buf = etr_buf; > + return etr_perf; > +} > + > + > +static void *tmc_etr_alloc_perf_buffer(struct coresight_device *csdev, > +int cpu, void **pages, int nr_pages, > +bool snapshot) > +{ > + struct etr_perf_buffer *etr_perf; > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + if (cpu == -1) > + cpu = smp_processor_id(); > + > + etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), > + nr_pages, pages, snapshot); > + if (IS_ERR(etr_perf)) { > + dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); > + return NULL; > + } > + > + etr_perf->snapshot = snapshot; > + etr_perf->nr_pages = nr_pages; > + etr_perf->pages = pages; > + > + return etr_perf; > +} > + > +static void tmc_etr_free_perf_buffer(void *config) > +{ > + struct etr_perf_buffer *etr_perf = config; > + > + if (etr_perf->etr_buf) > +
Re: [PATCH 6/6] coresight: etm-perf: Add support for ETR backend
Hi Suzuki, On Wed, Jul 11, 2018 at 03:16:39PM +0100, Suzuki K Poulose wrote: > Add support for using TMC-ETR as backend for ETM perf tracing. > We use software double buffering at the moment. i.e, the TMC-ETR > uses a separate buffer than the perf ring buffer. The data is > copied to the perf ring buffer once a session completes. > > The TMC-ETR would try to match the larger of perf ring buffer > or the ETR buffer size configured via sysfs, scaling down to > a minimum limit of 1MB. > > Cc: Mathieu Poirier > Signed-off-by: Suzuki K Poulose > --- > drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 > +++- > drivers/hwtracing/coresight/coresight-tmc.h | 2 + > 2 files changed, 265 insertions(+), 2 deletions(-) > > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c > b/drivers/hwtracing/coresight/coresight-tmc-etr.c > index 20a0beb..af921da 100644 > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c > @@ -21,6 +21,28 @@ struct etr_flat_buf { > }; > > /* > + * etr_perf_buffer - Perf buffer used for ETR > + * @etr_buf - Actual buffer used by the ETR > + * @snaphost - Perf session mode > + * @head - handle->head at the beginning of the session. > + * @nr_pages - Number of pages in the ring buffer. > + * @pages- Array of Pages in the ring buffer. > + */ > +struct etr_perf_buffer { > + struct etr_buf *etr_buf; > + boolsnapshot; > + unsigned long head; > + int nr_pages; > + void**pages; > +}; > + > +/* Convert the perf index to an offset within the ETR buffer */ > +#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << > PAGE_SHIFT)) > + > +/* Lower limit for ETR hardware buffer */ > +#define TMC_ETR_PERF_MIN_BUF_SIZESZ_1M > + > +/* > * The TMC ETR SG has a page size of 4K. The SG table contains pointers > * to 4KB buffers. However, the OS may use a PAGE_SIZE different from > * 4K (i.e, 16KB or 64KB). This implies that a single OS page could > @@ -1103,10 +1125,245 @@ static int tmc_enable_etr_sink_sysfs(struct > coresight_device *csdev) > return ret; > } > > +/* > + * tmc_etr_setup_perf_buf: Allocate ETR buffer for use by perf. > + * The size of the hardware buffer is dependent on the size configured > + * via sysfs and the perf ring buffer size. We prefer to allocate the > + * largest possible size, scaling down the size by half until it > + * reaches a minimum limit (1M), beyond which we give up. > + */ > +static struct etr_perf_buffer * > +tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, > +void **pages, bool snapshot) > +{ > + struct etr_buf *etr_buf; > + struct etr_perf_buffer *etr_perf; > + unsigned long size; > + > + etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); > + if (!etr_perf) > + return ERR_PTR(-ENOMEM); > + > + /* > + * Try to match the perf ring buffer size if it is larger > + * than the size requested via sysfs. > + */ > + if ((nr_pages << PAGE_SHIFT) > drvdata->size) { > + etr_buf = tmc_alloc_etr_buf(drvdata, (nr_pages << PAGE_SHIFT), > + 0, node, NULL); > + if (!IS_ERR(etr_buf)) > + goto done; > + } > + > + /* > + * Else switch to configured size for this ETR > + * and scale down until we hit the minimum limit. > + */ > + size = drvdata->size; > + do { > + etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL); > + if (!IS_ERR(etr_buf)) > + goto done; > + size /= 2; > + } while (size >= TMC_ETR_PERF_MIN_BUF_SIZE); > + > + kfree(etr_perf); > + return ERR_PTR(-ENOMEM); > + > +done: > + etr_perf->etr_buf = etr_buf; > + return etr_perf; > +} > + > + > +static void *tmc_etr_alloc_perf_buffer(struct coresight_device *csdev, > +int cpu, void **pages, int nr_pages, > +bool snapshot) > +{ > + struct etr_perf_buffer *etr_perf; > + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); > + > + if (cpu == -1) > + cpu = smp_processor_id(); > + > + etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), > + nr_pages, pages, snapshot); > + if (IS_ERR(etr_perf)) { > + dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); > + return NULL; > + } > + > + etr_perf->snapshot = snapshot; > + etr_perf->nr_pages = nr_pages; > + etr_perf->pages = pages; > + > + return etr_perf; > +} > + > +static void tmc_etr_free_perf_buffer(void *config) > +{ > + struct etr_perf_buffer *etr_perf = config; > + > + if (etr_perf->etr_buf) > +
[PATCH 6/6] coresight: etm-perf: Add support for ETR backend
Add support for using TMC-ETR as backend for ETM perf tracing. We use software double buffering at the moment. i.e, the TMC-ETR uses a separate buffer than the perf ring buffer. The data is copied to the perf ring buffer once a session completes. The TMC-ETR would try to match the larger of perf ring buffer or the ETR buffer size configured via sysfs, scaling down to a minimum limit of 1MB. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 +++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 265 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 20a0beb..af921da 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -21,6 +21,28 @@ struct etr_flat_buf { }; /* + * etr_perf_buffer - Perf buffer used for ETR + * @etr_buf- Actual buffer used by the ETR + * @snaphost - Perf session mode + * @head - handle->head at the beginning of the session. + * @nr_pages - Number of pages in the ring buffer. + * @pages - Array of Pages in the ring buffer. + */ +struct etr_perf_buffer { + struct etr_buf *etr_buf; + boolsnapshot; + unsigned long head; + int nr_pages; + void**pages; +}; + +/* Convert the perf index to an offset within the ETR buffer */ +#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) + +/* Lower limit for ETR hardware buffer */ +#define TMC_ETR_PERF_MIN_BUF_SIZE SZ_1M + +/* * The TMC ETR SG has a page size of 4K. The SG table contains pointers * to 4KB buffers. However, the OS may use a PAGE_SIZE different from * 4K (i.e, 16KB or 64KB). This implies that a single OS page could @@ -1103,10 +1125,245 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) return ret; } +/* + * tmc_etr_setup_perf_buf: Allocate ETR buffer for use by perf. + * The size of the hardware buffer is dependent on the size configured + * via sysfs and the perf ring buffer size. We prefer to allocate the + * largest possible size, scaling down the size by half until it + * reaches a minimum limit (1M), beyond which we give up. + */ +static struct etr_perf_buffer * +tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, + void **pages, bool snapshot) +{ + struct etr_buf *etr_buf; + struct etr_perf_buffer *etr_perf; + unsigned long size; + + etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); + if (!etr_perf) + return ERR_PTR(-ENOMEM); + + /* +* Try to match the perf ring buffer size if it is larger +* than the size requested via sysfs. +*/ + if ((nr_pages << PAGE_SHIFT) > drvdata->size) { + etr_buf = tmc_alloc_etr_buf(drvdata, (nr_pages << PAGE_SHIFT), + 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + } + + /* +* Else switch to configured size for this ETR +* and scale down until we hit the minimum limit. +*/ + size = drvdata->size; + do { + etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + size /= 2; + } while (size >= TMC_ETR_PERF_MIN_BUF_SIZE); + + kfree(etr_perf); + return ERR_PTR(-ENOMEM); + +done: + etr_perf->etr_buf = etr_buf; + return etr_perf; +} + + +static void *tmc_etr_alloc_perf_buffer(struct coresight_device *csdev, + int cpu, void **pages, int nr_pages, + bool snapshot) +{ + struct etr_perf_buffer *etr_perf; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (cpu == -1) + cpu = smp_processor_id(); + + etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), + nr_pages, pages, snapshot); + if (IS_ERR(etr_perf)) { + dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); + return NULL; + } + + etr_perf->snapshot = snapshot; + etr_perf->nr_pages = nr_pages; + etr_perf->pages = pages; + + return etr_perf; +} + +static void tmc_etr_free_perf_buffer(void *config) +{ + struct etr_perf_buffer *etr_perf = config; + + if (etr_perf->etr_buf) + tmc_free_etr_buf(etr_perf->etr_buf); + kfree(etr_perf); +} + +static int tmc_etr_set_perf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, +
[PATCH 6/6] coresight: etm-perf: Add support for ETR backend
Add support for using TMC-ETR as backend for ETM perf tracing. We use software double buffering at the moment. i.e, the TMC-ETR uses a separate buffer than the perf ring buffer. The data is copied to the perf ring buffer once a session completes. The TMC-ETR would try to match the larger of perf ring buffer or the ETR buffer size configured via sysfs, scaling down to a minimum limit of 1MB. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 265 +++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 265 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 20a0beb..af921da 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -21,6 +21,28 @@ struct etr_flat_buf { }; /* + * etr_perf_buffer - Perf buffer used for ETR + * @etr_buf- Actual buffer used by the ETR + * @snaphost - Perf session mode + * @head - handle->head at the beginning of the session. + * @nr_pages - Number of pages in the ring buffer. + * @pages - Array of Pages in the ring buffer. + */ +struct etr_perf_buffer { + struct etr_buf *etr_buf; + boolsnapshot; + unsigned long head; + int nr_pages; + void**pages; +}; + +/* Convert the perf index to an offset within the ETR buffer */ +#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) + +/* Lower limit for ETR hardware buffer */ +#define TMC_ETR_PERF_MIN_BUF_SIZE SZ_1M + +/* * The TMC ETR SG has a page size of 4K. The SG table contains pointers * to 4KB buffers. However, the OS may use a PAGE_SIZE different from * 4K (i.e, 16KB or 64KB). This implies that a single OS page could @@ -1103,10 +1125,245 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) return ret; } +/* + * tmc_etr_setup_perf_buf: Allocate ETR buffer for use by perf. + * The size of the hardware buffer is dependent on the size configured + * via sysfs and the perf ring buffer size. We prefer to allocate the + * largest possible size, scaling down the size by half until it + * reaches a minimum limit (1M), beyond which we give up. + */ +static struct etr_perf_buffer * +tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, + void **pages, bool snapshot) +{ + struct etr_buf *etr_buf; + struct etr_perf_buffer *etr_perf; + unsigned long size; + + etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); + if (!etr_perf) + return ERR_PTR(-ENOMEM); + + /* +* Try to match the perf ring buffer size if it is larger +* than the size requested via sysfs. +*/ + if ((nr_pages << PAGE_SHIFT) > drvdata->size) { + etr_buf = tmc_alloc_etr_buf(drvdata, (nr_pages << PAGE_SHIFT), + 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + } + + /* +* Else switch to configured size for this ETR +* and scale down until we hit the minimum limit. +*/ + size = drvdata->size; + do { + etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + size /= 2; + } while (size >= TMC_ETR_PERF_MIN_BUF_SIZE); + + kfree(etr_perf); + return ERR_PTR(-ENOMEM); + +done: + etr_perf->etr_buf = etr_buf; + return etr_perf; +} + + +static void *tmc_etr_alloc_perf_buffer(struct coresight_device *csdev, + int cpu, void **pages, int nr_pages, + bool snapshot) +{ + struct etr_perf_buffer *etr_perf; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (cpu == -1) + cpu = smp_processor_id(); + + etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), + nr_pages, pages, snapshot); + if (IS_ERR(etr_perf)) { + dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); + return NULL; + } + + etr_perf->snapshot = snapshot; + etr_perf->nr_pages = nr_pages; + etr_perf->pages = pages; + + return etr_perf; +} + +static void tmc_etr_free_perf_buffer(void *config) +{ + struct etr_perf_buffer *etr_perf = config; + + if (etr_perf->etr_buf) + tmc_free_etr_buf(etr_perf->etr_buf); + kfree(etr_perf); +} + +static int tmc_etr_set_perf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, +