diff --git a/modules/mqnic/Makefile b/modules/mqnic/Makefile index 292e351d8..a0cc63de1 100644 --- a/modules/mqnic/Makefile +++ b/modules/mqnic/Makefile @@ -2,6 +2,7 @@ # object files to build obj-m += mqnic.o mqnic-y += mqnic_main.o +mqnic-y += mqnic_irq.o mqnic-y += mqnic_dev.o mqnic-y += mqnic_netdev.o mqnic-y += mqnic_port.o diff --git a/modules/mqnic/mqnic.h b/modules/mqnic/mqnic.h index a481c9606..1ec5b7df7 100644 --- a/modules/mqnic/mqnic.h +++ b/modules/mqnic/mqnic.h @@ -78,6 +78,12 @@ struct mqnic_i2c_bus { struct i2c_adapter adapter; }; +struct mqnic_irq { + int index; + int irqn; + struct atomic_notifier_head nh; +}; + struct mqnic_dev { struct device *dev; struct pci_dev *pdev; @@ -103,8 +109,7 @@ struct mqnic_dev { char name[16]; int irq_count; - int irq_map[32]; - struct atomic_notifier_head irq_nh[MQNIC_MAX_IRQ]; + struct mqnic_irq *irq[MQNIC_MAX_IRQ]; unsigned int id; struct list_head dev_list_node; @@ -255,7 +260,8 @@ struct mqnic_eq_ring { struct net_device *ndev; struct mqnic_priv *priv; int ring_index; - int int_index; + struct mqnic_irq *irq; + int irq_index; int active; struct notifier_block irq_nb; @@ -334,6 +340,10 @@ struct mqnic_priv { // mqnic_main.c +// mqnic_irq.c +int mqnic_irq_init_pcie(struct mqnic_dev *mdev); +void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev); + // mqnic_dev.c extern const struct file_operations mqnic_fops; @@ -380,7 +390,7 @@ int mqnic_create_eq_ring(struct mqnic_priv *priv, struct mqnic_eq_ring **ring_pt void mqnic_destroy_eq_ring(struct mqnic_eq_ring **ring_ptr); int mqnic_alloc_eq_ring(struct mqnic_eq_ring *ring, int size, int stride); void mqnic_free_eq_ring(struct mqnic_eq_ring *ring); -int mqnic_activate_eq_ring(struct mqnic_eq_ring *ring, int int_index); +int mqnic_activate_eq_ring(struct mqnic_eq_ring *ring, struct mqnic_irq *irq); void mqnic_deactivate_eq_ring(struct mqnic_eq_ring *ring); bool mqnic_is_eq_ring_empty(const struct mqnic_eq_ring *ring); bool mqnic_is_eq_ring_full(const struct mqnic_eq_ring *ring); diff --git a/modules/mqnic/mqnic_eq.c b/modules/mqnic/mqnic_eq.c index a81a16197..80a77016e 100644 --- a/modules/mqnic/mqnic_eq.c +++ b/modules/mqnic/mqnic_eq.c @@ -130,32 +130,30 @@ void mqnic_free_eq_ring(struct mqnic_eq_ring *ring) ring->buf_dma_addr = 0; } -int mqnic_activate_eq_ring(struct mqnic_eq_ring *ring, int int_index) +int mqnic_activate_eq_ring(struct mqnic_eq_ring *ring, struct mqnic_irq *irq) { int ret = 0; mqnic_deactivate_eq_ring(ring); - if (!ring->buf) - return -EINVAL; - - if (int_index < 0 || int_index >= ring->priv->mdev->irq_count) + if (!ring->buf || !irq) return -EINVAL; // register interrupt - ring->int_index = int_index; - ret = atomic_notifier_chain_register(&ring->priv->mdev->irq_nh[int_index], &ring->irq_nb); - + ret = atomic_notifier_chain_register(&irq->nh, &ring->irq_nb); if (ret) return ret; + ring->irq = irq; + ring->irq_index = irq->index; + // deactivate queue iowrite32(0, ring->hw_addr + MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG); // set base address iowrite32(ring->buf_dma_addr, ring->hw_addr + MQNIC_EVENT_QUEUE_BASE_ADDR_REG + 0); iowrite32(ring->buf_dma_addr >> 32, ring->hw_addr + MQNIC_EVENT_QUEUE_BASE_ADDR_REG + 4); // set interrupt index - iowrite32(int_index, ring->hw_addr + MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG); + iowrite32(ring->irq_index, ring->hw_addr + MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG); // set pointers iowrite32(ring->head_ptr & ring->hw_ptr_mask, ring->hw_addr + MQNIC_EVENT_QUEUE_HEAD_PTR_REG); @@ -167,7 +165,7 @@ int mqnic_activate_eq_ring(struct mqnic_eq_ring *ring, int int_index) ring->active = 1; - return ret; + return 0; } void mqnic_deactivate_eq_ring(struct mqnic_eq_ring *ring) @@ -177,11 +175,13 @@ void mqnic_deactivate_eq_ring(struct mqnic_eq_ring *ring) // deactivate queue iowrite32(ilog2(ring->size), ring->hw_addr + MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG); // disarm queue - iowrite32(ring->int_index, ring->hw_addr + MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG); + iowrite32(0, ring->hw_addr + MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG); // unregister interrupt - if (ring->active) - ret = atomic_notifier_chain_unregister(&ring->priv->mdev->irq_nh[ring->int_index], &ring->irq_nb); + if (ring->irq) { + ret = atomic_notifier_chain_unregister(&ring->irq->nh, &ring->irq_nb); + ring->irq = NULL; + } ring->active = 0; } @@ -208,7 +208,10 @@ void mqnic_eq_write_tail_ptr(struct mqnic_eq_ring *ring) void mqnic_arm_eq(struct mqnic_eq_ring *ring) { - iowrite32(ring->int_index | MQNIC_EVENT_QUEUE_ARM_MASK, + if (!ring->active) + return; + + iowrite32(ring->irq_index | MQNIC_EVENT_QUEUE_ARM_MASK, ring->hw_addr + MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG); } diff --git a/modules/mqnic/mqnic_irq.c b/modules/mqnic/mqnic_irq.c new file mode 100644 index 000000000..eeae017c1 --- /dev/null +++ b/modules/mqnic/mqnic_irq.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: BSD-2-Clause-Views +/* + * Copyright 2021, The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of The Regents of the + * University of California. + */ + +#include "mqnic.h" + +static irqreturn_t mqnic_irq_handler(int irqn, void *data) +{ + struct mqnic_irq *irq = data; + + atomic_notifier_call_chain(&irq->nh, 0, NULL); + + return IRQ_HANDLED; +} + +int mqnic_irq_init_pcie(struct mqnic_dev *mdev) +{ + struct pci_dev *pdev = mdev->pdev; + struct device *dev = mdev->dev; + int ret = 0; + int k; + + // Allocate MSI IRQs + mdev->irq_count = pci_alloc_irq_vectors(pdev, 1, MQNIC_MAX_IRQ, PCI_IRQ_MSI); + if (mdev->irq_count < 0) { + dev_err(dev, "Failed to allocate IRQs"); + return -ENOMEM; + } + + // Set up interrupts + for (k = 0; k < mdev->irq_count; k++) { + struct mqnic_irq *irq; + + irq = kzalloc(sizeof(*irq), GFP_KERNEL); + if (!irq) { + ret = -ENOMEM; + goto fail; + } + + ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); + + ret = pci_request_irq(pdev, k, mqnic_irq_handler, NULL, + irq, "%s-%d", mdev->name, k); + if (ret < 0) { + kfree(irq); + ret = -ENOMEM; + dev_err(dev, "Failed to request IRQ %d", k); + goto fail; + } + + irq->index = k; + irq->irqn = pci_irq_vector(pdev, k); + mdev->irq[k] = irq; + } + + return 0; +fail: + mqnic_irq_deinit_pcie(mdev); + return ret; +} + +void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev) +{ + struct pci_dev *pdev = mdev->pdev; + int k; + + for (k = 0; k < MQNIC_MAX_IRQ; k++) { + if (mdev->irq[k]) { + pci_free_irq(pdev, k, mdev->irq[k]); + kfree(mdev->irq[k]); + mdev->irq[k] = NULL; + } + } + + pci_free_irq_vectors(pdev); +} diff --git a/modules/mqnic/mqnic_main.c b/modules/mqnic/mqnic_main.c index 4af486cbc..111524e1c 100644 --- a/modules/mqnic/mqnic_main.c +++ b/modules/mqnic/mqnic_main.c @@ -78,15 +78,6 @@ static unsigned int mqnic_get_free_id(void) return id; } -static irqreturn_t mqnic_interrupt(int irq, void *data) -{ - struct atomic_notifier_head *nh = data; - - atomic_notifier_call_chain(nh, 0, NULL); - - return IRQ_HANDLED; -} - static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = 0; @@ -261,28 +252,11 @@ static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent goto fail_map_bars; } - // Allocate MSI IRQs - mqnic->irq_count = pci_alloc_irq_vectors(pdev, 1, MQNIC_MAX_IRQ, PCI_IRQ_MSI); - if (mqnic->irq_count < 0) { - ret = -ENOMEM; - dev_err(dev, "Failed to allocate IRQs"); - goto fail_map_bars; - } - // Set up interrupts - for (k = 0; k < MQNIC_MAX_IRQ; k++) { - ATOMIC_INIT_NOTIFIER_HEAD(&mqnic->irq_nh[k]); - } - - for (k = 0; k < mqnic->irq_count; k++) { - ret = pci_request_irq(pdev, k, mqnic_interrupt, NULL, - &mqnic->irq_nh[k], "%s-%d", mqnic->name, k); - if (ret < 0) { - dev_err(dev, "Failed to request IRQ"); - goto fail_irq; - } - - mqnic->irq_map[k] = pci_irq_vector(pdev, k); + ret = mqnic_irq_init_pcie(mqnic); + if (ret) { + dev_err(dev, "Failed to set up interrupts"); + goto fail_map_bars; } // Board-specific init @@ -346,11 +320,7 @@ fail_init_netdev: mqnic_unregister_phc(mqnic); pci_clear_master(pdev); fail_board: - mqnic_board_deinit(mqnic); - for (k = 0; k < mqnic->irq_count; k++) - pci_free_irq(pdev, k, &mqnic->irq_nh[k]); -fail_irq: - pci_free_irq_vectors(pdev); + mqnic_irq_deinit_pcie(mqnic); fail_map_bars: if (mqnic->hw_addr) pci_iounmap(pdev, mqnic->hw_addr); @@ -390,9 +360,7 @@ static void mqnic_pci_remove(struct pci_dev *pdev) pci_clear_master(pdev); mqnic_board_deinit(mqnic); - for (k = 0; k < mqnic->irq_count; k++) - pci_free_irq(pdev, k, &mqnic->irq_nh[k]); - pci_free_irq_vectors(pdev); + mqnic_irq_deinit_pcie(mqnic); if (mqnic->hw_addr) pci_iounmap(pdev, mqnic->hw_addr); if (mqnic->app_hw_addr) diff --git a/modules/mqnic/mqnic_netdev.c b/modules/mqnic/mqnic_netdev.c index 22ceace3e..380d21302 100644 --- a/modules/mqnic/mqnic_netdev.c +++ b/modules/mqnic/mqnic_netdev.c @@ -45,7 +45,7 @@ static int mqnic_start_port(struct net_device *ndev) // set up event queues for (k = 0; k < priv->event_queue_count; k++) { - mqnic_activate_eq_ring(priv->event_ring[k], k % mdev->irq_count); + mqnic_activate_eq_ring(priv->event_ring[k], priv->mdev->irq[k % mdev->irq_count]); mqnic_arm_eq(priv->event_ring[k]); }