Commit db11f75b authored by Yeoreum Yun's avatar Yeoreum Yun Committed by Suzuki K Poulose
Browse files

coresight-tmc: change tmc_drvdata spinlock's type to raw_spinlock_t



In coresight-tmc drivers, tmc_drvdata->spinlock can be held
during __schedule() by perf_event_task_sched_out()/in().

Since tmc_drvdata->spinlock type is spinlock_t and
perf_event_task_sched_out()/in() is called after acquiring rq_lock,
which is raw_spinlock_t (an unsleepable lock),
this poses an issue in PREEMPT_RT kernel where spinlock_t is sleepable.

To address this, change type tmc_drvdata->spinlock in coresight-tmc drivers,
which can be called by perf_event_task_sched_out()/in(),
from spinlock_t to raw_spinlock_t.

Reviewed-by: default avatarJames Clark <james.clark@linaro.org>
Reviewed-by: default avatarMike Leach <mike.leach@linaro.org>
Signed-off-by: default avatarYeoreum Yun <yeoreum.yun@arm.com>
Signed-off-by: default avatarSuzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20250306121110.1647948-9-yeoreum.yun@arm.com
parent 982d0a0e
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -358,12 +358,12 @@ static int tmc_crashdata_open(struct inode *inode, struct file *file)
	mdata = drvdata->crash_mdata.vaddr;
	rbuf = &drvdata->resrv_buf;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (mdata->valid)
		rbuf->reading = true;
	else
		err = -ENOENT;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
	if (err)
		goto exit;

@@ -408,9 +408,9 @@ static int tmc_crashdata_release(struct inode *inode, struct file *file)
						   crashdev);

	rbuf = &drvdata->resrv_buf;
	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	rbuf->reading = false;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__);
	return ret;
@@ -801,7 +801,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
	drvdata->base = base;
	desc.access = CSDEV_ACCESS_IOMEM(base);

	spin_lock_init(&drvdata->spinlock);
	raw_spin_lock_init(&drvdata->spinlock);

	devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
	drvdata->config_type = BMVAL(devid, 6, 7);
@@ -913,7 +913,7 @@ static void tmc_shutdown(struct amba_device *adev)
	unsigned long flags;
	struct tmc_drvdata *drvdata = amba_get_drvdata(adev);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
		goto out;
@@ -927,7 +927,7 @@ static void tmc_shutdown(struct amba_device *adev)
	 * the system is going down after this.
	 */
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
}

static void __tmc_remove(struct device *dev)
+24 −24
Original line number Diff line number Diff line
@@ -185,9 +185,9 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
	 * If we don't have a buffer release the lock and allocate memory.
	 * Otherwise keep the lock and move along.
	 */
	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (!drvdata->buf) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

		/* Allocating the memory here while outside of the spinlock */
		buf = kzalloc(drvdata->size, GFP_KERNEL);
@@ -195,7 +195,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
			return -ENOMEM;

		/* Let's try again */
		spin_lock_irqsave(&drvdata->spinlock, flags);
		raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	}

	if (drvdata->reading) {
@@ -237,7 +237,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
		used = false;
	}
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	/* Free memory outside the spinlock if need be */
	if (!used)
@@ -255,7 +255,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
	struct perf_output_handle *handle = data;
	struct cs_buffers *buf = etm_perf_sink_config(handle);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	do {
		ret = -EINVAL;
		if (drvdata->reading)
@@ -298,7 +298,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
			csdev->refcnt++;
		}
	} while (0);
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	return ret;
}
@@ -333,16 +333,16 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
	unsigned long flags;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	if (drvdata->reading) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return -EBUSY;
	}

	csdev->refcnt--;
	if (csdev->refcnt) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return -EBUSY;
	}

@@ -353,7 +353,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
	drvdata->pid = -1;
	coresight_set_mode(csdev, CS_MODE_DISABLED);

	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	dev_dbg(&csdev->dev, "TMC-ETB/ETF disabled\n");
	return 0;
@@ -368,9 +368,9 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
	bool first_enable = false;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (drvdata->reading) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return -EBUSY;
	}

@@ -383,7 +383,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
	}
	if (!ret)
		csdev->refcnt++;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	if (first_enable)
		dev_dbg(&csdev->dev, "TMC-ETF enabled\n");
@@ -398,9 +398,9 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
	bool last_disable = false;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (drvdata->reading) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return;
	}

@@ -410,7 +410,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
		coresight_set_mode(csdev, CS_MODE_DISABLED);
		last_disable = true;
	}
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	if (last_disable)
		dev_dbg(&csdev->dev, "TMC-ETF disabled\n");
@@ -490,7 +490,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
	if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF))
		return 0;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	/* Don't do anything if another tracer is using this sink */
	if (csdev->refcnt != 1)
@@ -587,7 +587,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
	 */
	CS_LOCK(drvdata->base);
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	return to_read;
}
@@ -705,7 +705,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
			 drvdata->config_type != TMC_CONFIG_TYPE_ETF))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	if (drvdata->reading) {
		ret = -EBUSY;
@@ -737,7 +737,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)

	drvdata->reading = true;
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	return ret;
}
@@ -754,14 +754,14 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
			 drvdata->config_type != TMC_CONFIG_TYPE_ETF))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	/* Re-enable the TMC if need be */
	if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
		/* There is no point in reading a TMC in HW FIFO mode */
		mode = readl_relaxed(drvdata->base + TMC_MODE);
		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
			spin_unlock_irqrestore(&drvdata->spinlock, flags);
			raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
			return -EINVAL;
		}
		/*
@@ -775,7 +775,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
		memset(drvdata->buf, 0, drvdata->size);
		rc = __tmc_etb_enable_hw(drvdata);
		if (rc) {
			spin_unlock_irqrestore(&drvdata->spinlock, flags);
			raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
			return rc;
		}
	} else {
@@ -788,7 +788,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
	}

	drvdata->reading = false;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	/*
	 * Free allocated memory outside of the spinlock.  There is no need
+22 −22
Original line number Diff line number Diff line
@@ -1251,10 +1251,10 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
	 * buffer, provided the size matches. Any allocation has to be done
	 * with the lock released.
	 */
	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
	if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

		/* Allocate memory with the locks released */
		free_buf = new_buf = tmc_etr_setup_sysfs_buf(drvdata);
@@ -1262,7 +1262,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
			return new_buf;

		/* Let's try again */
		spin_lock_irqsave(&drvdata->spinlock, flags);
		raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	}

	if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
@@ -1281,7 +1281,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
	}

out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	/* Free memory outside the spinlock if need be */
	if (free_buf)
@@ -1299,7 +1299,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
	if (IS_ERR(sysfs_buf))
		return PTR_ERR(sysfs_buf);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	/*
	 * In sysFS mode we can have multiple writers per sink.  Since this
@@ -1318,7 +1318,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
	}

out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	if (!ret)
		dev_dbg(&csdev->dev, "TMC-ETR enabled\n");
@@ -1637,17 +1637,17 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
	struct etr_perf_buffer *etr_perf = config;
	struct etr_buf *etr_buf = etr_perf->etr_buf;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	/* Don't do anything if another tracer is using this sink */
	if (csdev->refcnt != 1) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		goto out;
	}

	if (WARN_ON(drvdata->perf_buf != etr_buf)) {
		lost = true;
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		goto out;
	}

@@ -1657,7 +1657,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
	tmc_sync_etr_buf(drvdata);

	CS_LOCK(drvdata->base);
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	lost = etr_buf->full;
	offset = etr_buf->offset;
@@ -1726,7 +1726,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
	struct perf_output_handle *handle = data;
	struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	 /* Don't use this sink if it is already claimed by sysFS */
	if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
		rc = -EBUSY;
@@ -1766,7 +1766,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
	}

unlock_out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
	return rc;
}

@@ -1788,16 +1788,16 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
	unsigned long flags;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	if (drvdata->reading) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return -EBUSY;
	}

	csdev->refcnt--;
	if (csdev->refcnt) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return -EBUSY;
	}

@@ -1810,7 +1810,7 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
	/* Reset perf specific data */
	drvdata->perf_buf = NULL;

	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	dev_dbg(&csdev->dev, "TMC-ETR disabled\n");
	return 0;
@@ -1910,7 +1910,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
	if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (drvdata->reading) {
		ret = -EBUSY;
		goto out;
@@ -1932,7 +1932,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)

	drvdata->reading = true;
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	return ret;
}
@@ -1946,7 +1946,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
	if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);

	/* RE-enable the TMC if need be */
	if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
@@ -1966,7 +1966,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
	}

	drvdata->reading = false;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

	/* Free allocated memory out side of the spinlock */
	if (sysfs_buf)
@@ -2023,14 +2023,14 @@ static int buf_mode_set_resrv(struct tmc_drvdata *drvdata)
	rbuf = &drvdata->resrv_buf;

	/* Ensure there are no active crashdata read sessions */
	spin_lock_irqsave(&drvdata->spinlock, flags);
	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
	if (!rbuf->reading) {
		tmc_crashdata_set_invalid(drvdata);
		rbuf->len = 0;
		drvdata->etr_mode = ETR_MODE_RESRV;
		err = 0;
	}
	spin_unlock_irqrestore(&drvdata->spinlock, flags);
	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
	return err;
}

+1 −1
Original line number Diff line number Diff line
@@ -249,7 +249,7 @@ struct tmc_drvdata {
	struct coresight_device	*csdev;
	struct miscdevice	miscdev;
	struct miscdevice	crashdev;
	spinlock_t		spinlock;
	raw_spinlock_t		spinlock;
	pid_t			pid;
	bool			reading;
	bool			stop_on_flush;