All of lore.kernel.org
 help / color / mirror / Atom feed
From: Logan Gunthorpe <logang@deltatee.com>
To: dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
	Vinod Koul <vkoul@kernel.org>
Cc: "Frank Li" <Frank.li@nxp.com>,
	"Christoph Hellwig" <hch@infradead.org>,
	"Christophe Jaillet" <christophe.jaillet@wanadoo.fr>,
	"Dave Jiang" <dave.jiang@intel.com>,
	"Thomas Weißschuh" <linux@weissschuh.net>,
	"Kelvin Cao" <kelvin.cao@microchip.com>,
	"Logan Gunthorpe" <logang@deltatee.com>
Subject: [PATCH v4 3/4] dmaengine: switchtec-dma: add config sysfs attributes
Date: Tue, 10 Nov 2026 11:31:35 -0600	[thread overview]
Message-ID: <20261110173137.623764-4-logang@deltatee.com> (raw)
In-Reply-To: <20261110173137.623764-1-logang@deltatee.com>

Add sysfs configuration options for switchtec-dma channels, using
dma_device.chan_groups the same way the ioatdma conversion does:
attach a "switchtec-config" attribute group directly to the channel's
existing struct device, with each show()/store() recovering the
struct dma_chan via the dmaengine_chan_from_dev CLASS.

The read-modify-write of the shared perf_cfg register is protected by
hw_ctrl_lock, and writes are rejected with -EBUSY while the channel is
allocated to a client (hw_cfg_locked), instead of a TOCTOU-prone
chan->client_count check.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
---
 drivers/dma/switchtec_dma.c | 179 ++++++++++++++++++++++++++++++++++++
 1 file changed, 179 insertions(+)

diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
index a386c3a284e6..1ebe17095c61 100644
--- a/drivers/dma/switchtec_dma.c
+++ b/drivers/dma/switchtec_dma.c
@@ -132,6 +132,11 @@ struct switchtec_dma_chan {
 
 	/* Serialize hardware control register access */
 	spinlock_t hw_ctrl_lock;
+	/*
+	 * Set while the channel is allocated to a client; blocks sysfs
+	 * hw config changes
+	 */
+	bool hw_cfg_locked;
 
 	struct tasklet_struct desc_task;
 
@@ -1029,6 +1034,10 @@ static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan)
 	dev_dbg(dmaengine_chan_dev(chan), "MRRS:        0x%x\n",
 		FIELD_GET(PERF_MRRS_MASK, perf_cfg));
 
+	spin_lock(&swdma_chan->hw_ctrl_lock);
+	swdma_chan->hw_cfg_locked = true;
+	spin_unlock(&swdma_chan->hw_ctrl_lock);
+
 	return SWITCHTEC_DMA_SQ_SIZE;
 
 err_ring_inactive:
@@ -1046,11 +1055,180 @@ static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan)
 	return rc;
 }
 
+static __always_inline ssize_t perf_cfg_show(struct device *dev, char *page,
+					     unsigned int mask)
+{
+	struct switchtec_dma_chan *swdma_chan;
+	struct chan_fw_regs __iomem *chan_fw;
+	u32 perf_cfg;
+	int value;
+
+	CLASS(dmaengine_chan_from_dev, c)(dev);
+
+	if (!c)
+		return -ENODEV;
+
+	swdma_chan = container_of(c, struct switchtec_dma_chan, dma_chan);
+	chan_fw = swdma_chan->mmio_chan_fw;
+
+	rcu_read_lock();
+	if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+
+	perf_cfg = readl(&chan_fw->perf_cfg);
+	value = field_get(mask, perf_cfg);
+
+	rcu_read_unlock();
+	return sysfs_emit(page, "0x%x\n", value);
+}
+
+static __always_inline ssize_t perf_cfg_store(struct device *dev,
+					      const char *page, size_t count,
+					      unsigned int mask)
+{
+	struct switchtec_dma_chan *swdma_chan;
+	struct chan_fw_regs __iomem *chan_fw;
+	ssize_t ret = count;
+	u32 perf_cfg;
+	int value;
+
+	CLASS(dmaengine_chan_from_dev, c)(dev);
+
+	if (!c)
+		return -ENODEV;
+
+	swdma_chan = container_of(c, struct switchtec_dma_chan, dma_chan);
+	chan_fw = swdma_chan->mmio_chan_fw;
+
+	if (kstrtoint(page, 0, &value) < 0)
+		return -EINVAL;
+
+	if (value < 0 || value > field_max(mask))
+		return -EINVAL;
+
+	rcu_read_lock();
+	if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	spin_lock(&swdma_chan->hw_ctrl_lock);
+	if (swdma_chan->hw_cfg_locked) {
+		spin_unlock(&swdma_chan->hw_ctrl_lock);
+		ret = -EBUSY;
+		goto err_unlock;
+	}
+
+	perf_cfg = readl(&chan_fw->perf_cfg);
+	perf_cfg = (perf_cfg & ~mask) | field_prep(mask, value);
+	writel(perf_cfg, &chan_fw->perf_cfg);
+	spin_unlock(&swdma_chan->hw_ctrl_lock);
+
+err_unlock:
+	rcu_read_unlock();
+	return ret;
+}
+
+static ssize_t burst_scale_show(struct device *dev,
+				struct device_attribute *attr, char *page)
+{
+	return perf_cfg_show(dev, page, PERF_BURST_SCALE_MASK);
+}
+
+static ssize_t burst_scale_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *page, size_t count)
+{
+	return perf_cfg_store(dev, page, count, PERF_BURST_SCALE_MASK);
+}
+static DEVICE_ATTR_RW(burst_scale);
+
+static ssize_t mrrs_show(struct device *dev,
+			 struct device_attribute *attr, char *page)
+{
+	return perf_cfg_show(dev, page, PERF_MRRS_MASK);
+}
+
+static ssize_t mrrs_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *page, size_t count)
+{
+	return perf_cfg_store(dev, page, count, PERF_MRRS_MASK);
+}
+static DEVICE_ATTR_RW(mrrs);
+
+static ssize_t interval_show(struct device *dev,
+			     struct device_attribute *attr, char *page)
+{
+	return perf_cfg_show(dev, page, PERF_INTERVAL_MASK);
+}
+
+static ssize_t interval_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *page, size_t count)
+{
+	return perf_cfg_store(dev, page, count, PERF_INTERVAL_MASK);
+}
+static DEVICE_ATTR_RW(interval);
+
+static ssize_t burst_size_show(struct device *dev,
+			       struct device_attribute *attr, char *page)
+{
+	return perf_cfg_show(dev, page, PERF_BURST_SIZE_MASK);
+}
+
+static ssize_t burst_size_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *page, size_t count)
+{
+	return perf_cfg_store(dev, page, count, PERF_BURST_SIZE_MASK);
+}
+static DEVICE_ATTR_RW(burst_size);
+
+static ssize_t arb_weight_show(struct device *dev,
+			       struct device_attribute *attr, char *page)
+{
+	return perf_cfg_show(dev, page, PERF_ARB_WEIGHT_MASK);
+}
+
+static ssize_t arb_weight_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *page, size_t count)
+{
+	return perf_cfg_store(dev, page, count, PERF_ARB_WEIGHT_MASK);
+}
+static DEVICE_ATTR_RW(arb_weight);
+
+static struct attribute *switchtec_config_attrs[] = {
+	&dev_attr_burst_scale.attr,
+	&dev_attr_mrrs.attr,
+	&dev_attr_interval.attr,
+	&dev_attr_burst_size.attr,
+	&dev_attr_arb_weight.attr,
+	NULL,
+};
+
+static const struct attribute_group switchtec_config_group = {
+	.name = "switchtec-config",
+	.attrs = switchtec_config_attrs,
+};
+
+static const struct attribute_group *switchtec_groups[] = {
+	&switchtec_config_group,
+	NULL,
+};
+
 static void switchtec_dma_free_chan_resources(struct dma_chan *chan)
 {
 	struct switchtec_dma_chan *swdma_chan =
 		container_of(chan, struct switchtec_dma_chan, dma_chan);
 
+	spin_lock(&swdma_chan->hw_ctrl_lock);
+	swdma_chan->hw_cfg_locked = false;
+	spin_unlock(&swdma_chan->hw_ctrl_lock);
+
 	spin_lock_bh(&swdma_chan->submit_lock);
 	swdma_chan->ring_active = false;
 	spin_unlock_bh(&swdma_chan->submit_lock);
@@ -1313,6 +1491,7 @@ static int switchtec_dma_create(struct pci_dev *pdev)
 	dma->device_terminate_all = switchtec_dma_terminate_all;
 	dma->device_synchronize = switchtec_dma_synchronize;
 	dma->device_release = switchtec_dma_release;
+	dma->chan_groups = switchtec_groups;
 
 	rc = dma_async_device_register(dma);
 	if (rc) {
-- 
2.47.3


  parent reply	other threads:[~2026-11-10 17:32 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10 17:31 [PATCH v4 0/4] Add sysfs interface to switchtec-dma Logan Gunthorpe
2026-11-10 17:31 ` [PATCH v4 1/4] dmaengine: add per-channel sysfs attribute groups via chan_groups Logan Gunthorpe
2026-11-10 17:31 ` [PATCH v4 2/4] dmaengine: ioatdma: convert per-channel sysfs to chan_groups Logan Gunthorpe
2026-11-10 17:31 ` Logan Gunthorpe [this message]
2026-11-10 17:31 ` [PATCH v4 4/4] dmaengine: switchtec-dma: add pmon sysfs attributes Logan Gunthorpe
2026-11-10 17:47   ` sashiko-bot
2026-11-10 17:50     ` Logan Gunthorpe

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261110173137.623764-4-logang@deltatee.com \
    --to=logang@deltatee.com \
    --cc=Frank.li@nxp.com \
    --cc=christophe.jaillet@wanadoo.fr \
    --cc=dave.jiang@intel.com \
    --cc=dmaengine@vger.kernel.org \
    --cc=hch@infradead.org \
    --cc=kelvin.cao@microchip.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@weissschuh.net \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.