[PATCH v5 1/2] drivers/coresight: Add UltraSoc System Memory Buffer driver
liuqi (BA)
liuqi115 at huawei.com
Thu May 5 19:35:57 PDT 2022
Hi Suzuki,
thanks for your review, some replies below.
On 2022/5/6 8:26, Suzuki K Poulose wrote:
> Hi Qi Liu
>
> Apologies for the delay. I have some more comments below.
> > On 16/04/2022 09:39, Qi Liu wrote:
>> This patch adds driver for UltraSoc SMB(System Memory Buffer)
>> device. SMB provides a way to buffer messages from ETM, and
>> store these CPU instructions in system memory.
>>
>> SMB is developed by UltraSoc technology, which is acquired by
>> Siemens, and we still use "UltraSoc" to name driver.
>>
>> Signed-off-by: Qi Liu <liuqi115 at huawei.com>
>> Tested-by: JunHao He <hejunhao2 at hisilicon.com>
>> ---
>> drivers/hwtracing/coresight/Kconfig | 10 +
>> drivers/hwtracing/coresight/Makefile | 1 +
>> drivers/hwtracing/coresight/ultrasoc-smb.c | 643 +++++++++++++++++++++
>> drivers/hwtracing/coresight/ultrasoc-smb.h | 106 ++++
>> 4 files changed, 760 insertions(+)
>> create mode 100644 drivers/hwtracing/coresight/ultrasoc-smb.c
>> create mode 100644 drivers/hwtracing/coresight/ultrasoc-smb.h
>>
>> diff --git a/drivers/hwtracing/coresight/Kconfig
>> b/drivers/hwtracing/coresight/Kconfig
>> index 514a9b8086e3..4380eb1a0a73 100644
>> --- a/drivers/hwtracing/coresight/Kconfig
>> +++ b/drivers/hwtracing/coresight/Kconfig
>> @@ -201,4 +201,14 @@ config CORESIGHT_TRBE
>> To compile this driver as a module, choose M here: the module
>> will be
>> called coresight-trbe.
>> +config ULTRASOC_SMB
>> + tristate "Ultrasoc system memory buffer drivers"
>> + depends on ACPI && ARM64 && CORESIGHT_LINKS_AND_SINKS
>> + help
>> + This driver provides support for the Ultrasoc system memory
>> buffer (SMB).
>> + SMB is responsible for receiving the trace data from Coresight
>> ETM devices
>> + and storing them to a system buffer.
>> +
>> + To compile this driver as a module, choose M here: the module
>> will be
>> + called ultrasoc-smb.
>> endif
>> diff --git a/drivers/hwtracing/coresight/Makefile
>> b/drivers/hwtracing/coresight/Makefile
>> index b6c4a48140ec..344dba8d6ff8 100644
>> --- a/drivers/hwtracing/coresight/Makefile
>> +++ b/drivers/hwtracing/coresight/Makefile
>> @@ -27,3 +27,4 @@ obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
>> obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
>> coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
>> coresight-cti-sysfs.o
>> +obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
>> diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c
>> b/drivers/hwtracing/coresight/ultrasoc-smb.c
>> new file mode 100644
>> index 000000000000..9a93b7fc7bda
>> --- /dev/null
>> +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c
>> @@ -0,0 +1,643 @@
>> +// SPDX-License-Identifier: MIT/GPL
>> +/*
>> + * Siemens System Memory Buffer driver.
>> + * Copyright(c) 2021, HiSilicon Limited.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/circ_buf.h>
>> +#include <linux/err.h>
>> +#include <linux/module.h>
>> +#include <linux/mod_devicetable.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "ultrasoc-smb.h"
>> +
>> +DEFINE_CORESIGHT_DEVLIST(sink_devs, "ultra_smb");
>> +
>> +#define ULTRASOC_SMB_DSM_UUID "82ae1283-7f6a-4cbe-aa06-53e8fb24db18"
>> +
>> +static bool smb_buffer_is_empty(struct smb_drv_data *drvdata)
>> +{
>> + u32 buf_status = readl(drvdata->base + SMB_LB_INT_STS);
>> +
>> + return buf_status & BIT(0) ? false : true;
>> +}
>> +
>> +static bool smb_buffer_cmp_pointer(struct smb_drv_data *drvdata)
>> +{
>> + u32 wr_offset, rd_offset;
>> +
>> + wr_offset = readl(drvdata->base + SMB_LB_WR_ADDR);
>> + rd_offset = readl(drvdata->base + SMB_LB_RD_ADDR);
>> + return wr_offset == rd_offset;
>> +}
>> +
>> +static void smb_reset_buffer_status(struct smb_drv_data *drvdata)
>> +{
>> + writel(0xf, drvdata->base + SMB_LB_INT_STS);
>> +}
>> +
>> +/* Purge data remaining in hardware path in case them influence next
>> trace */
>> +static void smb_purge_data(struct smb_drv_data *drvdata)
>> +{
>> + writel(0x1, drvdata->base + SMB_LB_PURGE);
>> +}
>> +
>> +static void smb_update_data_size(struct smb_drv_data *drvdata)
>> +{
>> + struct smb_data_buffer *sdb = &drvdata->sdb;
>> + u32 write_offset;
>> +
>> + smb_purge_data(drvdata);
>> + if (smb_buffer_cmp_pointer(drvdata)) {
>> + if (smb_buffer_is_empty(drvdata))
>> + sdb->data_size = 0;
>> + else
>> + sdb->data_size = sdb->buf_size;
>
> What happens when the buffer is full ? Does the sink stop writing ?
> Or does it keep on overwriting in a circular buffer mode ?
> If it does keep overwriting, we would need to make sure to update the
> sdb->rd_offset so that we provide the latest data (in smb_read())?
> If it doesn't overwrite, I think this logic could be simpler (similar
> to the tmc-et* circular buffer mode calculation.
>
sink will stop writing when the buffer is full, and I'll simplify this
function like this:
static void smb_buffer_is_full(struct smb_drv_data *drvdata)
{
if (smb_buffer_cmp_pointer(drvdata) && !smb_buffer_is_empty(drvdata))
return true;
return false;
}
static void smb_update_data_size(struct smb_drv_data *drvdata)
{
struct smb_data_buffer *sdb = &drvdata->sdb;
u32 write_offset;
if (smb_buffer_is_full(drvdata)) {
sdb->data_size = sdb->buf_size;
return;
}
write_offset = readl(drvdata->base + SMB_LB_WR_ADDR) -
sdb->start_addr;
sdb->data_size = CIRC_CNT(write_offset, sdb->rd_offset,
sdb->buf_size);
}
>
>> + return;
>> + }
>> +
>> + write_offset = readl(drvdata->base + SMB_LB_WR_ADDR) -
>> sdb->start_addr;
>> + sdb->data_size = CIRC_CNT(write_offset, sdb->rd_offset,
>> sdb->buf_size);
>> +}
>> +
>> +static int smb_open(struct inode *inode, struct file *file)
>> +{
>> + struct smb_drv_data *drvdata = container_of(file->private_data,
>> + struct smb_drv_data, miscdev);
>> +
>> + if (local_cmpxchg(&drvdata->reading, 0, 1))
>
> I believe this must be done in the smb_read(). Please see my comment
> for smb_disable()
>
got it, I'll change this like:
static int smb_open(struct inode *inode, struct file *file)
{
struct smb_drv_data *drvdata = container_of(file->private_data, struct
smb_drv_data, miscdev);
ret = 0;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
if (drvdata->mode == CS_MODE_PERF) {
ret = -EINVAL;
goto out;
}
if (atomic_read(drvdata->csdev->refcnt))
ret = -EBUSY;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
}
>> + return -EBUSY;
>> +
>> + return 0;
>> +}
>> +
>> +static ssize_t smb_read(struct file *file, char __user *data, size_t
>> len,
>> + loff_t *ppos)
>> +{
>> + struct smb_drv_data *drvdata = container_of(file->private_data,
>> + struct smb_drv_data, miscdev);
>> + struct smb_data_buffer *sdb = &drvdata->sdb;
>> + struct device *dev = &drvdata->csdev->dev;
>> + unsigned long flags;
>> + int to_copy = 0;
>> +
>> + spin_lock_irqsave(&drvdata->spinlock, flags);
>> +
>> + if (atomic_read(drvdata->csdev->refcnt)) {
>> + spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> + return -EBUSY;
>> + }
>> +
>> + if (!sdb->data_size) {
>> + smb_update_data_size(drvdata);
>> + if (!sdb->data_size)
>> + goto out;
>> + }
>> +
>> + to_copy = min(sdb->data_size, len);
>> +
>> + /* Copy parts of trace data when read pointer wrap around SMB
>> buffer */
>> + if (sdb->rd_offset + to_copy > sdb->buf_size)
>> + to_copy = sdb->buf_size - sdb->rd_offset;
>> +
>> + if (copy_to_user(data, (void *)sdb->buf_base + sdb->rd_offset,
>> + to_copy)) {
>> + dev_dbg(dev, "Failed to copy data to user.\n");
>> + to_copy = -EFAULT;
>> + goto out;
>> + }
>> +
>> + *ppos += to_copy;
>> + sdb->data_size -= to_copy;
>> + sdb->rd_offset += to_copy;
>> + sdb->rd_offset %= sdb->buf_size;
>> + writel(sdb->start_addr + sdb->rd_offset,
>> + drvdata->base + SMB_LB_RD_ADDR);
>> + dev_dbg(dev, "%d bytes copied.\n", to_copy);
>> +out:
>> + if (!sdb->data_size)
>> + smb_reset_buffer_status(drvdata);
>> + spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> + return to_copy;
>> +}
>> +
>> +static int smb_release(struct inode *inode, struct file *file)
>> +{
>> + struct smb_drv_data *drvdata = container_of(file->private_data,
>> + struct smb_drv_data, miscdev);
>> + local_set(&drvdata->reading, 0);
>> + return 0;
>> +}
>> +
[...]
>> +
>> +static int smb_disable(struct coresight_device *csdev)
>> +{
>> + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&drvdata->spinlock, flags);
>> +
>> + if (local_read(&drvdata->reading)) {
>> + spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> + return -EBUSY;
>> + }
>> +
>
> This is a bit confusing. So, we don't allow "read" when a session
> is in progress (via refcnt check in smb_read()). But the flag,
> drvdata->reading is set as soon as we open(). And we fail the disable
> if the drvdata->reading is set. I guess, we should move the setting
> of the drvdata->reading to smb_read() and set it for the first case
> where we are able to read.
> Otherwise we could see, something like:
>
> CPU0: smb_enable() # success, SMB is on
> CPU1: open(/dev/usmb0) # -> set drvdata->reading
> ..
> CPU0: smb_disable() # returns EBUSY since drvdata->reading is on.
> CPU1: read(fd) # returns EBUSY since there is a refcnt.
>
>
got it, I'll change this next version, thanks.
>
>> + if (atomic_dec_return(csdev->refcnt)) {
>> + spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> + return -EBUSY;
>> + }
>> +
>> + WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
>> + smb_disable_hw(drvdata);
>> + smb_purge_data(drvdata);
>> +
>> + /*
>> + * In perf mode, sink->disable is called after
>> sink->update_buffer, so
+ buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node);
[...]
>> +
>> +static unsigned long smb_update_buffer(struct coresight_device *csdev,
>> + struct perf_output_handle *handle,
>> + void *sink_config)
>> +{
>> + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
>> + struct smb_data_buffer *sdb = &drvdata->sdb;
>> + struct cs_buffers *buf = sink_config;
>> + unsigned long data_size = 0;
>> + unsigned long flags;
>> + bool lost = false;
>> +
>> + if (!buf)
>> + return 0;
>> +
>> + spin_lock_irqsave(&drvdata->spinlock, flags);
>> +
>> + /* Don't do anything if another tracer is using this sink. */
>> + if (atomic_read(csdev->refcnt) != 1)
>> + goto out;
>> +
>> + smb_update_data_size(drvdata);
>
> Are we allowed to call this when the SMB is ON ? In perf mode, we call
> the update_buffer() before disabling the "sink". So, care must be taken
> to stop the SMB if needed to make sure the registers are read properly.
got it, will do disable smb before smb_update_data_size.
>
>
> Rest looks fine to me.
>
>
> Suzuki
> .
Thanks,
Qi
More information about the linux-arm-kernel
mailing list