From: illusion wang Add device start/stop helper functions to manage MSI-X vector mapping, mailbox interrupt resource initialization and deinitialization. Create unbound global workqueue for mailbox task scheduling; this shared work infrastructure supports both current interrupt-driven receive and future polling receive path. This patch implements complete startup and shutdown flow for common device resources: 1. Add nbl_dev_start() to finish device startup procedure: - Configure hardware MSI-X mapping table for different interrupt types - Allocate required MSI-X irq vectors via pci_alloc_irq_vectors - Request mailbox IRQ; interrupt handler schedules mailbox processing work - Enable hardware mailbox interrupt and mark channel interrupt ready 2. Add nbl_dev_stop() to tear down device resources safely in strict order: - Update software channel state first to switch to polling, then mask hardware interrupt to avoid stale ACK handling - Free mailbox IRQ handler and release MSI-X vector resources - Destroy hardware MSI-X mapping table Extend channel TX waiting logic with polling fallback and shutdown detection. When interrupt path is disabled, send routines rely on active polling to wait for message ACK, preventing deadlock during device stop. Hook nbl_core_start() / nbl_core_stop() into PCI probe and remove paths. Signed-off-by: illusion wang --- .../net/ethernet/nebula-matrix/nbl/nbl_core.h | 2 + .../nebula-matrix/nbl/nbl_core/nbl_dev.c | 259 ++++++++++++++++++ .../nbl/nbl_include/nbl_def_dev.h | 2 + .../net/ethernet/nebula-matrix/nbl/nbl_main.c | 21 +- 4 files changed, 283 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h index 9519aacbf0ea..a48bd9a0ef1e 100644 --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h @@ -39,5 +39,7 @@ struct nbl_adapter { struct nbl_adapter *nbl_core_init(struct pci_dev *pdev, struct nbl_init_param *param); void nbl_core_remove(struct nbl_adapter *adapter); +int nbl_core_start(struct nbl_adapter *adapter); +void nbl_core_stop(struct nbl_adapter *adapter); #endif diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c index aa51f62c83b9..6d401936ac43 100644 --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c @@ -6,6 +6,17 @@ #include #include "nbl_dev.h" +static void nbl_dev_clean_mailbox_schedule(struct nbl_dev_mgt *dev_mgt); + +/* ---------- Interrupt config ---------- */ +static irqreturn_t nbl_dev_clean_mailbox(int __always_unused irq, void *data) +{ + struct nbl_dev_mgt *dev_mgt = (struct nbl_dev_mgt *)data; + + nbl_dev_clean_mailbox_schedule(dev_mgt); + return IRQ_HANDLED; +} + static void nbl_dev_init_msix_cnt(struct nbl_dev_mgt *dev_mgt) { struct nbl_dev_common *dev_common = dev_mgt->common_dev; @@ -14,6 +25,187 @@ static void nbl_dev_init_msix_cnt(struct nbl_dev_mgt *dev_mgt) msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num = 1; } +static int nbl_dev_request_mailbox_irq(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + struct nbl_common_info *common = dev_mgt->common; + u16 local_vec_id; + int irq_num; + int err; + + if (!msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num) + return 0; + + local_vec_id = + msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].base_vector_id; + irq_num = pci_irq_vector(common->pdev, local_vec_id); + if (irq_num < 0) { + dev_err(common->dev, "Failed to get mailbox IRQ vector: %d\n", + irq_num); + return irq_num; + } + + snprintf(dev_common->mailbox_name, sizeof(dev_common->mailbox_name), + "nbl_mailbox@pci:%s", pci_name(common->pdev)); + err = request_irq(irq_num, nbl_dev_clean_mailbox, 0, + dev_common->mailbox_name, dev_mgt); + if (err) + return err; + + return 0; +} + +static void nbl_dev_free_mailbox_irq(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + struct nbl_common_info *common = dev_mgt->common; + u16 local_vec_id; + int irq_num; + + if (!msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num) + return; + + local_vec_id = + msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].base_vector_id; + irq_num = pci_irq_vector(common->pdev, local_vec_id); + if (irq_num >= 0) + free_irq(irq_num, dev_mgt); +} + +static int nbl_dev_enable_mailbox_irq(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + u16 local_vec_id; + + if (!msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num) + return 0; + + local_vec_id = + msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].base_vector_id; + chan_ops->set_queue_state(dev_mgt->chan_ops_tbl->priv, + NBL_CHAN_INTERRUPT_READY, + NBL_CHAN_TYPE_MAILBOX, true); + + return disp_ops->set_mailbox_irq(dev_mgt->disp_ops_tbl->priv, + local_vec_id, true); +} + +static int nbl_dev_disable_mailbox_irq(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + u16 local_vec_id; + + if (!msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num) + return 0; + + local_vec_id = + msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].base_vector_id; + /* + * Disable sequence invariant: update software state first, then mask + * hardware interrupt. Must not reverse the order. + * + * If hardware interrupt is masked before clearing INTERRUPT_READY, + * the hardware may still transmit outstanding ACK packets for in-flight + * messages. Subsequent switch to polling mode discards pending ACK + * processing, triggering "Channel waiting ack failed" and "Skip ack + * with invalid status" errors. + * + * By entering polling mode first, any late hardware interrupts are + * ignored without pending ACK expectations, then hardware interrupt + * can be safely disabled. + * + * This helper is invoked in two paths: + * 1. Error unwind path of nbl_dev_start(): immediately followed by + * nbl_dev_free_mailbox_irq(), channel resources + * are fully torn down afterwards, no stale descriptors remain. + * 2. Normal device stop path nbl_dev_stop(): free_irq() synchronously + * waits for any in-flight threaded irq bottom-half handler to + * finish execution, all pending mailbox cleanup work completes + * before channel teardown in late remove stage. No stuck + * descriptors will linger in either scenario. + */ + chan_ops->set_queue_state(dev_mgt->chan_ops_tbl->priv, + NBL_CHAN_INTERRUPT_READY, + NBL_CHAN_TYPE_MAILBOX, false); + + return disp_ops->set_mailbox_irq(dev_mgt->disp_ops_tbl->priv, + local_vec_id, false); +} + +static int nbl_dev_configure_msix_map(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + bool mask_en = msix_info->serv_info[NBL_MSIX_NET_TYPE].hw_self_mask_en; + u16 msix_net_num = msix_info->serv_info[NBL_MSIX_NET_TYPE].num; + u16 msix_not_net_num = 0; + int err, i; + + msix_info->serv_info[NBL_MSIX_VIRTIO_TYPE].base_vector_id = 0; + for (i = NBL_MSIX_NET_TYPE; i < NBL_MSIX_TYPE_MAX; i++) + msix_info->serv_info[i].base_vector_id = + msix_info->serv_info[i - 1].base_vector_id + + msix_info->serv_info[i - 1].num; + + for (i = 0; i < NBL_MSIX_TYPE_MAX; i++) { + if (i == NBL_MSIX_NET_TYPE) + continue; + msix_not_net_num += msix_info->serv_info[i].num; + } + + err = disp_ops->configure_msix_map(dev_mgt->disp_ops_tbl->priv, + msix_net_num, msix_not_net_num, + mask_en); + + return err; +} + +static int nbl_dev_destroy_msix_map(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + + return disp_ops->destroy_msix_map(dev_mgt->disp_ops_tbl->priv); +} + +static int nbl_dev_init_interrupt_scheme(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + struct nbl_common_info *common = dev_mgt->common; + int needed = 0; + int err; + int i; + + for (i = 0; i < NBL_MSIX_TYPE_MAX; i++) + needed += msix_info->serv_info[i].num; + + err = pci_alloc_irq_vectors(common->pdev, needed, needed, + PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); + if (err < 0) { + dev_err(common->dev, + "pci_alloc_irq_vectors failed, err = %d\n", err); + return err; + } + + return 0; +} + +static void nbl_dev_clear_interrupt_scheme(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_common_info *common = dev_mgt->common; + + pci_free_irq_vectors(common->pdev); +} + /* ---------- Channel config ---------- */ static void nbl_dev_setup_chan_qinfo(struct nbl_dev_mgt *dev_mgt, u8 chan_type) { @@ -73,6 +265,14 @@ static void nbl_dev_clean_mailbox_task(struct work_struct *work) NBL_CHAN_TYPE_MAILBOX); } +static void nbl_dev_clean_mailbox_schedule(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dev_common *common_dev = dev_mgt->common_dev; + struct nbl_common_info *common = dev_mgt->common; + + queue_work(common->wq, &common_dev->clean_mbx_task); +} + /* ---------- Dev init process ---------- */ static int nbl_dev_setup_common_dev(struct nbl_adapter *adapter) { @@ -236,3 +436,62 @@ void nbl_dev_remove(struct nbl_adapter *adapter) nbl_dev_remove_common_dev(adapter); nbl_common_destroy_wq(common); } + +/* ---------- Dev start process ---------- */ +int nbl_dev_start(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dev_common *common_dev = dev_mgt->common_dev; + int cleanup_ret; + int ret; + + ret = nbl_dev_configure_msix_map(dev_mgt); + if (ret) + return ret; + + ret = nbl_dev_init_interrupt_scheme(dev_mgt); + if (ret) + goto init_interrupt_scheme_err; + ret = nbl_dev_request_mailbox_irq(dev_mgt); + if (ret) + goto mailbox_request_irq_err; + ret = nbl_dev_enable_mailbox_irq(dev_mgt); + if (ret) + goto enable_mailbox_irq_err; + + return 0; +enable_mailbox_irq_err: + cleanup_ret = nbl_dev_disable_mailbox_irq(dev_mgt); + if (cleanup_ret) + dev_err(dev_mgt->common->dev, + "Failed to disable mailbox IRQ: %d\n", cleanup_ret); + nbl_dev_free_mailbox_irq(dev_mgt); + cancel_work_sync(&common_dev->clean_mbx_task); +mailbox_request_irq_err: + nbl_dev_clear_interrupt_scheme(dev_mgt); +init_interrupt_scheme_err: + cleanup_ret = nbl_dev_destroy_msix_map(dev_mgt); + if (cleanup_ret) + dev_err(dev_mgt->common->dev, + "Failed to destroy MSI-X map: %d\n", cleanup_ret); + return ret; +} + +void nbl_dev_stop(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dev_common *common_dev = dev_mgt->common_dev; + int ret; + + ret = nbl_dev_disable_mailbox_irq(dev_mgt); + if (ret) + dev_err(dev_mgt->common->dev, + "Failed to disable mailbox IRQ: %d\n", ret); + nbl_dev_free_mailbox_irq(dev_mgt); + cancel_work_sync(&common_dev->clean_mbx_task); + nbl_dev_clear_interrupt_scheme(dev_mgt); + ret = nbl_dev_destroy_msix_map(dev_mgt); + if (ret) + dev_err(dev_mgt->common->dev, + "Failed to destroy MSI-X map: %d\n", ret); +} diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h index b422a4edf0a9..32e6cce38d39 100644 --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h @@ -10,5 +10,7 @@ struct nbl_adapter; int nbl_dev_init(struct nbl_adapter *adapter); void nbl_dev_remove(struct nbl_adapter *adapter); +int nbl_dev_start(struct nbl_adapter *adapter); +void nbl_dev_stop(struct nbl_adapter *adapter); #endif diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c index 27817a137e36..39a69c42e9bf 100644 --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c @@ -16,6 +16,16 @@ #include "nbl_include/nbl_def_common.h" #include "nbl_core.h" +int nbl_core_start(struct nbl_adapter *adapter) +{ + return nbl_dev_start(adapter); +} + +void nbl_core_stop(struct nbl_adapter *adapter) +{ + nbl_dev_stop(adapter); +} + struct nbl_adapter *nbl_core_init(struct pci_dev *pdev, struct nbl_init_param *param) { @@ -125,7 +135,14 @@ static int nbl_probe(struct pci_dev *pdev, goto adapter_init_err; } pci_set_drvdata(pdev, adapter); + err = nbl_core_start(adapter); + if (err) + goto core_start_err; + return 0; +core_start_err: + pci_set_drvdata(pdev, NULL); + nbl_core_remove(adapter); adapter_init_err: pci_clear_master(pdev); return err; @@ -135,8 +152,10 @@ static void nbl_remove(struct pci_dev *pdev) { struct nbl_adapter *adapter = pci_get_drvdata(pdev); - if (adapter) + if (adapter) { + nbl_core_stop(adapter); nbl_core_remove(adapter); + } pci_clear_master(pdev); } -- 2.47.3