diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-24 16:21:27 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-24 16:21:27 +0200 |
| commit | 8f9aa2c90530ab92301a82231ae44f3722becd93 (patch) | |
| tree | fb282e955b0a880b07131a135257fe3ec764e928 /drivers/pci/endpoint | |
| parent | 93467b31bec6da512b51544e5e4584f2745e995e (diff) | |
| parent | 155b42bec9cbb6b8cdc47dd9bd09503a81fbe493 (diff) | |
Merge v7.1.5linux-rolling-stable
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/pci/endpoint')
| -rw-r--r-- | drivers/pci/endpoint/functions/pci-epf-ntb.c | 21 | ||||
| -rw-r--r-- | drivers/pci/endpoint/functions/pci-epf-vntb.c | 130 |
2 files changed, 118 insertions, 33 deletions
diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 2bdcc35b652c..5314aca2188a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb, struct pci_epc *epc; int ret; - if (db_count > MAX_DB_COUNT) - return -EINVAL; - ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; + if (!db_count || db_count > MAX_DB_COUNT) { + dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n", + db_count, MAX_DB_COUNT); + return -EINVAL; + } + if (msix) ret = epf_ntb_configure_msix(ntb, type, db_count); else @@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; - u32 db_count; int ret; ntb_epc = ntb->epc[type]; @@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, func_no = ntb_epc->func_no; vfunc_no = ntb_epc->vfunc_no; - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; epc = ntb_epc->epc; if (msi_capable) { - ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count); if (ret) { dev_err(dev, "%s intf: MSI configuration failed\n", pci_epc_interface_string(type)); @@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, } if (msix_capable) { - ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, ntb_epc->msix_bar, ntb_epc->msix_table_offset); if (ret) { diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 2256c3062b1a..ff9db8f980af 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -37,6 +37,7 @@ */ #include <linux/atomic.h> +#include <linux/bitops.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> @@ -69,6 +70,9 @@ static struct workqueue_struct *kpcintb_workqueue; #define MAX_DB_COUNT 32 #define MAX_MW 4 +/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */ +#define VNTB_PEER_DB_WORK_BUDGET 5 + enum epf_ntb_bar { BAR_CONFIG, BAR_DB, @@ -79,6 +83,12 @@ enum epf_ntb_bar { VNTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + /* * +--------------------------------------------------+ Base * | | @@ -129,6 +139,8 @@ struct epf_ntb { u32 spad_count; u64 mws_size[MAX_MW]; atomic64_t db; + atomic64_t peer_db_pending; + struct work_struct peer_db_work; u32 vbus_number; u16 vntb_pid; u16 vntb_vid; @@ -261,10 +273,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work) ntb = container_of(work, struct epf_ntb, cmd_handler.work); - for (i = 1; i < ntb->db_count && !ntb->msi_doorbell; i++) { + for (i = EPF_IRQ_DB_START; i < ntb->db_count && !ntb->msi_doorbell; + i++) { if (ntb->epf_db[i]) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); ntb->epf_db[i] = 0; } } @@ -330,10 +343,10 @@ static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data) struct epf_ntb *ntb = data; int i; - for (i = 1; i < ntb->db_count; i++) + for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++) if (irq == ntb->epf->db_msg[i].virq) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); } return IRQ_HANDLED; @@ -483,7 +496,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; struct device *dev; - u32 db_count; int ret; dev = &ntb->epf->dev; @@ -495,14 +507,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; - if (epc_features->msi_capable) { ret = pci_epc_set_msi(ntb->epf->epc, ntb->epf->func_no, @@ -920,6 +930,9 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + atomic64_set(&ntb->peer_db_pending, 0); + enable_work(&ntb->peer_db_work); + return 0; err_write_header: @@ -943,6 +956,8 @@ err_config_interrupt: static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { disable_delayed_work_sync(&ntb->cmd_handler); + disable_work_sync(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); epf_ntb_db_bar_clear(ntb); epf_ntb_config_sspad_bar_clear(ntb); @@ -1253,7 +1268,10 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) { - return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; + if (ntb_ndev(ntb)->db_count < EPF_IRQ_DB_START) + return 0; + + return BIT_ULL(ntb_ndev(ntb)->db_count - EPF_IRQ_DB_START) - 1; } static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) @@ -1357,22 +1375,84 @@ static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 return 0; } -static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +static void vntb_epf_peer_db_work(struct work_struct *work) { - u32 interrupt_num = ffs(db_bits) + 1; - struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work); + struct pci_epf *epf = ntb->epf; + unsigned int budget = VNTB_PEER_DB_WORK_BUDGET; u8 func_no, vfunc_no; + unsigned int db_bit; + u32 interrupt_num; + u64 db_bits; int ret; - func_no = ntb->epf->func_no; - vfunc_no = ntb->epf->vfunc_no; + if (!epf || !epf->epc) + return; + + func_no = epf->func_no; + vfunc_no = epf->vfunc_no; + + /* + * Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()). + * Limit the number of snapshots handled per run so we don't monopolize + * the workqueue under a doorbell storm. + */ + while (budget--) { + db_bits = atomic64_xchg(&ntb->peer_db_pending, 0); + if (!db_bits) + return; + + while (db_bits) { + /* + * pci_epc_raise_irq() for MSI expects a 1-based + * interrupt number. The first usable doorbell starts + * at EPF_IRQ_DB_START in the legacy slot layout. + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking + * interoperability with older peers. + */ + db_bit = __ffs64(db_bits); + interrupt_num = db_bit + EPF_IRQ_DB_START + 1; + db_bits &= ~BIT_ULL(db_bit); + + ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, + PCI_IRQ_MSI, interrupt_num); + if (ret) + dev_err(&ntb->ntb.dev, + "Failed to raise IRQ for interrupt_num %u: %d\n", + interrupt_num, ret); + } + } - ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, - PCI_IRQ_MSI, interrupt_num + 1); - if (ret) - dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + if (atomic64_read(&ntb->peer_db_pending)) + queue_work(kpcintb_workqueue, &ntb->peer_db_work); +} - return ret; +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + db_bits &= vntb_epf_db_valid_mask(ndev); + if (!db_bits) + return 0; + + /* + * .peer_db_set() may be called from atomic context. pci_epc_raise_irq() + * can sleep (it takes epc->lock), so defer MSI raising to process + * context. Doorbell requests are coalesced in peer_db_pending. + */ + atomic64_or(db_bits, &ntb->peer_db_pending); + queue_work(kpcintb_workqueue, &ntb->peer_db_work); + + return 0; } static u64 vntb_epf_db_read(struct ntb_dev *ndev) @@ -1619,6 +1699,10 @@ static int epf_ntb_probe(struct pci_epf *epf, ntb->epf = epf; ntb->vbus_number = 0xff; + INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work); + disable_work(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); + /* Initially, no bar is assigned */ for (i = 0; i < VNTB_BAR_NUM; i++) ntb->epf_ntb_bar[i] = NO_BAR; |
