public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Christoph Hellwig <hch@infradead.org>
To: Kelvin Cao <kelvin.cao@microchip.com>
Cc: vkoul@kernel.org, dmaengine@vger.kernel.org,
	linux-kernel@vger.kernel.org, logang@deltatee.com,
	george.ge@microchip.com, christophe.jaillet@wanadoo.fr,
	hch@infradead.org
Subject: Re: [PATCH v4 1/1] dmaengine: switchtec-dma: Introduce Switchtec DMA engine PCI driver
Date: Tue, 2 May 2023 23:31:34 -0700	[thread overview]
Message-ID: <ZFH/xhyjm9VTZolE@infradead.org> (raw)
In-Reply-To: <20230423213717.318655-2-kelvin.cao@microchip.com>

On Sun, Apr 23, 2023 at 02:37:17PM -0700, Kelvin Cao wrote:
> Implement core PCI driver skeleton and register DMA engine callbacks.

I only noticed this now, but this sentence reads a bit odd.  What does
it try to say?

> +struct chan_fw_regs {
> +	u32 valid_en_se;

...

> +	u16 cq_phase;
> +} __packed;

Everything here seems nicely naturally aligned, what is the reason
for the __packed attribute?

> +struct switchtec_dma_hw_se_desc {
> +	u8 opc;
> +	u8 ctrl;
> +	__le16 tlp_setting;
> +	__le16 rsvd1;
> +	__le16 cid;
> +	__le32 byte_cnt;
> +	union {
> +		__le32 saddr_lo;
> +		__le32 widata_lo;
> +	};
> +	union {
> +		__le32 saddr_hi;
> +		__le32 widata_hi;
> +	};

What is the point for unions of identical data types?

> +			p = (int *)ce;
> +			for (i = 0; i < sizeof(*ce)/4; i++) {
> +				dev_err(chan_dev, "CE DW%d: 0x%08x\n", i,
> +					le32_to_cpu((__force __le32)*p));
> +				p++;
> +			}

Why is this casting to an int that is never used and the back to CE?
Maybe a function that actually dumps the registers with names and
is type safe might be a better idea?  If not just using
print_hex_dump would be a simpler, although that would always printk
in little endian representation (which might be easier to read anyway).

> +	struct pci_dev *pdev;
> +	struct switchtec_dma_chan *swdma_chan = to_switchtec_dma_chan(chan);
> +	int rc;
> +
> +	rcu_read_lock();
> +	pdev = rcu_dereference(swdma_chan->swdma_dev->pdev);
> +	rcu_read_unlock();
> +
> +	if (pdev)
> +		synchronize_irq(swdma_chan->irq);

At this point pdev might be freed as you're outside the RCU critical
section, and the irq number could have been reused.

> +	switch (type) {
> +	case MEMCPY:
> +		if (len > SWITCHTEC_DESC_MAX_SIZE)
> +			goto err_unlock;
> +		break;
> +	case WIMM:
> +		if (len != 8)
> +			break;
> +
> +		if (dma_dst & ((1 << DMAENGINE_ALIGN_8_BYTES) - 1)) {
> +			dev_err(chan_dev,
> +				"QW WIMM dst addr 0x%08x_%08x not QW aligned!\n",
> +				upper_32_bits(dma_dst), lower_32_bits(dma_dst));
> +			goto err_unlock;
> +		}
> +		break;
> +	default:
> +		goto err_unlock;
> +	}

IT looks like these checks could easily be done without the lock,
and in the respective callers.

> +	if (type == MEMCPY) {
> +		desc->hw->opc = SWITCHTEC_DMA_OPC_MEMCPY;
> +		desc->hw->saddr_lo = cpu_to_le32(lower_32_bits(dma_src));
> +		desc->hw->saddr_hi = cpu_to_le32(upper_32_bits(dma_src));
> +	} else {
> +		desc->hw->opc = SWITCHTEC_DMA_OPC_WRIMM;
> +		desc->hw->widata_lo = cpu_to_le32(lower_32_bits(data));
> +		desc->hw->widata_hi = cpu_to_le32(upper_32_bits(data));
> +	}

... and then instead of the type I'd just pass the opcode directly,
simplifying the logic quite a bit.

> +static irqreturn_t switchtec_dma_isr(int irq, void *chan)
> +{
> +	struct switchtec_dma_chan *swdma_chan = chan;
> +
> +	if (swdma_chan->comp_ring_active)
> +		tasklet_schedule(&swdma_chan->desc_task);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t switchtec_dma_chan_status_isr(int irq, void *dma)
> +{
> +	struct switchtec_dma_dev *swdma_dev = dma;
> +
> +	tasklet_schedule(&swdma_dev->chan_status_task);
> +
> +	return IRQ_HANDLED;
> +}

Same comment as last time - irq + tasklet seems quite hack and
inefficient over just using threaded interrupts.  I'd like to see
a really good rationale for using it, and a Cc to the interrupt
maintainers that would love to kill off tasklets

> +	addr = swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET;
> +	addr +=  i * SWITCHTEC_DMA_CHAN_FW_REGS_SIZE;
> +	chan_fw = (struct chan_fw_regs __iomem *)addr;
> +
> +	addr = swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CTRL_OFFSET;
> +	addr +=  i * SWITCHTEC_DMA_CHAN_HW_REGS_SIZE;
> +	chan_hw = (struct chan_hw_regs __iomem *)addr;
> +
> +	swdma_dev->swdma_chans[i] = swdma_chan;
> +	swdma_chan->mmio_chan_fw = chan_fw;
> +	swdma_chan->mmio_chan_hw = chan_hw;

No need for the casts above.  This could simply become:

	swdma_chan->mmio_chan_fw =
		swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET +
		i * SWITCHTEC_DMA_CHAN_FW_REGS_SIZE;
	swdma_chan->mmio_chan_hw =
		swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CTRL_OFFSET +
		i * SWITCHTEC_DMA_CHAN_HW_REGS_SIZE;

> +	rc = pause_reset_channel(swdma_chan);
> +	if (rc)
> +		goto free_and_exit;
> +
> +	rcu_read_lock();
> +	pdev = rcu_dereference(swdma_dev->pdev);
> +	if (!pdev) {
> +		rc = -ENODEV;
> +		goto unlock_and_free;
> +	}

The pdev can't go away while you're in ->probe as that is synchronized
vs ->remove and ->shutdown.

> +	irq = pci_irq_vector(pdev, irq);
> +	if (irq < 0) {
> +		rc = irq;
> +		goto unlock_and_free;
> +	}
> +
> +	rcu_read_unlock();
> +
> +	rc = request_irq(irq, switchtec_dma_isr, 0, KBUILD_MODNAME, swdma_chan);
> +	if (rc)
> +		goto free_and_exit;

I'd just use pci_request_irq here.

> +#define SWITCHTEC_DMA_DEVICE(device_id) \
> +	{ \
> +		.vendor     = PCI_VENDOR_ID_MICROSEMI, \
> +		.device     = device_id, \
> +		.subvendor  = PCI_ANY_ID, \
> +		.subdevice  = PCI_ANY_ID, \
> +		.class      = PCI_CLASS_SYSTEM_OTHER << 8, \
> +		.class_mask = 0xFFFFFFFF, \
> +	}
> +
> +static const struct pci_device_id switchtec_dma_pci_tbl[] = {
> +	SWITCHTEC_DMA_DEVICE(0x4000), /* PFX 100XG4 */

This should use the common PCI_DEVICE() macro instead, i.e.

	PCI_DEVICE(PCI_VENDOR_ID_MICROSEMI, 0x4000), /* PFX 100XG4 */
	...

  parent reply	other threads:[~2023-05-03  6:31 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-23 21:37 [PATCH v4 0/1] Switchtec Switch DMA Engine Driver Kelvin Cao
2023-04-23 21:37 ` [PATCH v4 1/1] dmaengine: switchtec-dma: Introduce Switchtec DMA engine PCI driver Kelvin Cao
2023-04-24  1:36   ` kernel test robot
2023-05-03  6:31   ` Christoph Hellwig [this message]
2023-05-05  0:31     ` Kelvin.Cao
2023-05-15 15:13       ` Christoph Hellwig
2023-05-15 18:18         ` Kelvin.Cao
2023-05-16  5:00           ` Christoph Hellwig
2023-05-16  5:20             ` Kelvin.Cao

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=ZFH/xhyjm9VTZolE@infradead.org \
    --to=hch@infradead.org \
    --cc=christophe.jaillet@wanadoo.fr \
    --cc=dmaengine@vger.kernel.org \
    --cc=george.ge@microchip.com \
    --cc=kelvin.cao@microchip.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=logang@deltatee.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox