Fix the two issues listed below and code cleanup a) Fixed the BUG of atomic-sleep b) Fixed uninitialized before use buf_hw_base
Junhao He (3): coresight: ultrasoc-smb: fix sleep while close preempt in enable_smb coresight: ultrasoc-smb: simplify the code for check to_copy valid coresight: ultrasoc-smb: fix uninitialized before use buf_hw_base
drivers/hwtracing/coresight/ultrasoc-smb.c | 49 +++++++++------------- drivers/hwtracing/coresight/ultrasoc-smb.h | 6 +-- 2 files changed, 23 insertions(+), 32 deletions(-)
When we to enable the SMB by perf, the perf sched will call perf_ctx_lock() to close system preempt in event_function_call(). But SMB::enable_smb() use mutex to lock the critical section, which may sleep.
BUG: sleeping function called from invalid context at kernel/locking/mutex.c:580 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 153023, name: perf preempt_count: 2, expected: 0 RCU nest depth: 0, expected: 0 INFO: lockdep is turned off. irq event stamp: 0 hardirqs last enabled at (0): [<0000000000000000>] 0x0 hardirqs last disabled at (0): [<ffffa2983f5c5f40>] copy_process+0xae8/0x2b48 softirqs last enabled at (0): [<ffffa2983f5c5f40>] copy_process+0xae8/0x2b48 softirqs last disabled at (0): [<0000000000000000>] 0x0 CPU: 2 PID: 153023 Comm: perf Kdump: loaded Tainted: G W O 6.5.0-rc4+ #1
Call trace: ... __mutex_lock+0xbc/0xa70 mutex_lock_nested+0x34/0x48 smb_update_buffer+0x58/0x360 [ultrasoc_smb] etm_event_stop+0x204/0x2d8 [coresight] etm_event_del+0x1c/0x30 [coresight] event_sched_out+0x17c/0x3b8 group_sched_out.part.0+0x5c/0x208 __perf_event_disable+0x15c/0x210 event_function+0xe0/0x230 remote_function+0xb4/0xe8 generic_exec_single+0x160/0x268 smp_call_function_single+0x20c/0x2a0 event_function_call+0x20c/0x220 _perf_event_disable+0x5c/0x90 perf_event_for_each_child+0x58/0xc0 _perf_ioctl+0x34c/0x1250 perf_ioctl+0x64/0x98 ...
Use spinlock replace mutex to control driver data access to one at a time. But the function copy_to_user() may sleep so spinlock do not to lock it.
Fixes: 06f5c2926aaa ("drivers/coresight: Add UltraSoc System Memory Buffer driver") Signed-off-by: Junhao He hejunhao3@huawei.com --- drivers/hwtracing/coresight/ultrasoc-smb.c | 36 ++++++++++------------ drivers/hwtracing/coresight/ultrasoc-smb.h | 6 ++-- 2 files changed, 19 insertions(+), 23 deletions(-)
diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index e9a32a97fbee..b08a619d1116 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -99,7 +99,7 @@ static int smb_open(struct inode *inode, struct file *file) struct smb_drv_data, miscdev); int ret = 0;
- mutex_lock(&drvdata->mutex); + spin_lock(&drvdata->spinlock);
if (drvdata->reading) { ret = -EBUSY; @@ -115,7 +115,7 @@ static int smb_open(struct inode *inode, struct file *file)
drvdata->reading = true; out: - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
return ret; } @@ -132,10 +132,8 @@ static ssize_t smb_read(struct file *file, char __user *data, size_t len, if (!len) return 0;
- mutex_lock(&drvdata->mutex); - if (!sdb->data_size) - goto out; + return 0;
to_copy = min(sdb->data_size, len);
@@ -145,20 +143,18 @@ static ssize_t smb_read(struct file *file, char __user *data, size_t len,
if (copy_to_user(data, sdb->buf_base + sdb->buf_rdptr, to_copy)) { dev_dbg(dev, "Failed to copy data to user\n"); - to_copy = -EFAULT; - goto out; + return -EFAULT; }
+ spin_lock(&drvdata->spinlock); *ppos += to_copy; - smb_update_read_ptr(drvdata, to_copy);
- dev_dbg(dev, "%zu bytes copied\n", to_copy); -out: if (!sdb->data_size) smb_reset_buffer(drvdata); - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
+ dev_dbg(dev, "%zu bytes copied\n", to_copy); return to_copy; }
@@ -167,9 +163,9 @@ 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);
- mutex_lock(&drvdata->mutex); + spin_lock(&drvdata->spinlock); drvdata->reading = false; - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
return 0; } @@ -262,7 +258,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); int ret = 0;
- mutex_lock(&drvdata->mutex); + spin_lock(&drvdata->spinlock);
/* Do nothing, the trace data is reading by other interface now */ if (drvdata->reading) { @@ -294,7 +290,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n"); out: - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
return ret; } @@ -304,7 +300,7 @@ static int smb_disable(struct coresight_device *csdev) struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); int ret = 0;
- mutex_lock(&drvdata->mutex); + spin_lock(&drvdata->spinlock);
if (drvdata->reading) { ret = -EBUSY; @@ -327,7 +323,7 @@ static int smb_disable(struct coresight_device *csdev)
dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); out: - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
return ret; } @@ -408,7 +404,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, if (!buf) return 0;
- mutex_lock(&drvdata->mutex); + spin_lock(&drvdata->spinlock);
/* Don't do anything if another tracer is using this sink. */ if (atomic_read(&csdev->refcnt) != 1) @@ -432,7 +428,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, if (!buf->snapshot && lost) perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); out: - mutex_unlock(&drvdata->mutex); + spin_unlock(&drvdata->spinlock);
return data_size; } @@ -590,7 +586,7 @@ static int smb_probe(struct platform_device *pdev) return ret; }
- mutex_init(&drvdata->mutex); + spin_lock_init(&drvdata->spinlock); drvdata->pid = -1;
ret = smb_register_sink(pdev, drvdata); diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.h b/drivers/hwtracing/coresight/ultrasoc-smb.h index d2e14e8d2c8a..82a44c14a882 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.h +++ b/drivers/hwtracing/coresight/ultrasoc-smb.h @@ -8,7 +8,7 @@ #define _ULTRASOC_SMB_H
#include <linux/miscdevice.h> -#include <linux/mutex.h> +#include <linux/spinlock.h>
/* Offset of SMB global registers */ #define SMB_GLB_CFG_REG 0x00 @@ -105,7 +105,7 @@ struct smb_data_buffer { * @csdev: Component vitals needed by the framework. * @sdb: Data buffer for SMB. * @miscdev: Specifics to handle "/dev/xyz.smb" entry. - * @mutex: Control data access to one at a time. + * @spinlock: Control data access to one at a time. * @reading: Synchronise user space access to SMB buffer. * @pid: Process ID of the process being monitored by the * session that is using this component. @@ -116,7 +116,7 @@ struct smb_drv_data { struct coresight_device *csdev; struct smb_data_buffer sdb; struct miscdevice miscdev; - struct mutex mutex; + spinlock_t spinlock; bool reading; pid_t pid; enum cs_mode mode;
We only need to check once when before using the to_copy variable to simplify the code.
Signed-off-by: Junhao He hejunhao3@huawei.com --- drivers/hwtracing/coresight/ultrasoc-smb.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index b08a619d1116..e78edc3480ce 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -127,20 +127,15 @@ static ssize_t smb_read(struct file *file, char __user *data, size_t len, struct smb_drv_data, miscdev); struct smb_data_buffer *sdb = &drvdata->sdb; struct device *dev = &drvdata->csdev->dev; - ssize_t to_copy = 0; - - if (!len) - return 0; - - if (!sdb->data_size) - return 0; - - to_copy = min(sdb->data_size, len); + ssize_t to_copy = min(sdb->data_size, len);
/* Copy parts of trace data when read pointer wrap around SMB buffer */ if (sdb->buf_rdptr + to_copy > sdb->buf_size) to_copy = sdb->buf_size - sdb->buf_rdptr;
+ if (!to_copy) + return 0; + if (copy_to_user(data, sdb->buf_base + sdb->buf_rdptr, to_copy)) { dev_dbg(dev, "Failed to copy data to user\n"); return -EFAULT;
In smb_reset_buffer, the sdb->buf_hw_base variable is uninitialized before use, which initializes it in smb_init_data_buffer. And the SMB regiester are set in smb_config_inport. So move the call after smb_config_inport.
Fixes: 06f5c2926aaa ("drivers/coresight: Add UltraSoc System Memory Buffer driver")
Signed-off-by: Junhao He hejunhao3@huawei.com --- drivers/hwtracing/coresight/ultrasoc-smb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index e78edc3480ce..21ba473786e5 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -475,7 +475,6 @@ static int smb_init_data_buffer(struct platform_device *pdev, static void smb_init_hw(struct smb_drv_data *drvdata) { smb_disable_hw(drvdata); - smb_reset_buffer(drvdata);
writel(SMB_LB_CFG_LO_DEFAULT, drvdata->base + SMB_LB_CFG_LO_REG); writel(SMB_LB_CFG_HI_DEFAULT, drvdata->base + SMB_LB_CFG_HI_REG); @@ -597,6 +596,7 @@ static int smb_probe(struct platform_device *pdev) }
platform_set_drvdata(pdev, drvdata); + smb_reset_buffer(drvdata);
return 0; }