Hi Serge,
I've done a quick review on your new driver:
+ * WARNING! IDT PCIe-switch registers are all Little endian. So corresponding
+ * writel operations must have embedded endiannes conversion. If local
+ * platform doesn't have it, the driver won't properly work.
This warning seems unnecessary. As I understand it, PCI is by convention little
endian and writel and friends all swap to little endian (at least their generic version do in include/asm-generic/io.h).
So the driver should already be correct on BE platforms.
+ /*
+ * It's obvious bug to request a register exceeding the maximum possible
+ * value as well as to have it unaligned.
+ */
+ if (WARN_ON(reg > IDT_REG_PCI_MAX || !IS_ALIGNED(reg, IDT_REG_ALIGN)))
+ return;
IMO, This feels like a bit over kill to check this every time you access hardware. It's a static function where most of the calls are constants. In cases when
they're not constant the value should be checked ahead of time and sane things done instead of printing a stack trace.
+ /* Just write the value to the specified register */
+ writel(data, ndev->cfgspc + (ptrdiff_t)reg);
writel and friends are discouraged for new code. Use iowrite32, et al. See
http://www.makelinux.net/ldd3/chp-9-sect-4
+ dev_dbg_pci(ndev, "IDT NT local port: %hhu, num of peers: %hhu\n",
+ ndev->port, ndev->peer_cnt);
Don't do this. Open code it with: dev_dbg(&ndev->ntp.dev...
+ spin_lock_irqsave(&ndev->mtbl_lock, irqflags);
+ idt_nt_write(ndev, IDT_NT_NTMTBLADDR, ndev->part);
+ idt_nt_write(ndev, IDT_NT_NTMTBLDATA, mtbldata);
+ spin_unlock_irqrestore(&ndev->mtbl_lock, irqflags);
Why do writes like these need a spin lock? What is that protecting against? What happens if another thread does the same two writes immediately after the unlock?
+ /* Enable MSI interrupts */
+ ret = pci_enable_msi(pdev);
+ if (ret != 0) {
+ dev_err_pci(ndev, "IDT failed to enable MSI interrupt");
+ goto err_try_intx;
+ }
pci_alloc_irq_vectors could be used to simplify your interrupt enabling. Instead of trying, testing and failing and retrying with INTX, this function does that all for you.
+ /* Request corresponding IRQ number */
+ ret = request_threaded_irq(pdev->irq, NULL, idt_thread_isr,
+ IRQF_ONESHOT, NTB_IRQNAME, ndev);
It would probably be simpler to use devm_request_threaded_irq then your cleanup path will be simpler.
+ /* Request all BARs resources */
+ ret = pci_request_regions(pdev, NTB_NAME);
pcim_iomap_regions_request_all can be used to combine this and the pcim_iompa below it.
+static void idt_pci_remove(struct pci_dev *pdev)
+{
+ struct idt_ntb_dev *ndev = pci_get_drvdata(pdev);
+
+ /* Deinit the DebugFS node */
+ idt_deinit_dbgfs(ndev);
+
+ /* Unregister NTB device */
+ idt_unregister_device(ndev);
+
+ /* Stop the interrupts handling */
+ idt_deinit_isr(ndev);
+
+ /* Deinitialize link event subsystem */
+ idt_deinit_link(ndev);
+
+ /* Deinit basic PCI subsystem */
+ idt_deinit_pci(ndev);
+
+ /* IDT PCIe-switch NTB driver is finally initialized */
+ dev_info(&pdev->dev, "IDT NTB device is removed");
+
+ /* Sayonara... */
I don't see where you kfree ndev the structure... (and it's not done by the ntb device's release code...) so this looks like a memory leak.
Logan