vdpa/octeon_ep: enable support for multiple interrupts per device

Updated the driver to utilize all the MSI-X interrupt vectors supported
by each OCTEON endpoint VF, instead of relying on a single vector.
Enabling more interrupts allows packets from multiple rings to be
distributed across multiple cores, improving parallelism and
performance.

Reviewed-by: Dan Carpenter <dan.carpenter@linaro.org>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Shijith Thotton <sthotton@marvell.com>
Message-Id: <20250103153226.1933479-1-sthotton@marvell.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Shijith Thotton 2025-01-03 21:01:34 +05:30 committed by Michael S. Tsirkin
parent 6f3955a62c
commit 26f8ce06af
3 changed files with 62 additions and 39 deletions

View File

@ -29,12 +29,12 @@
#define OCTEP_EPF_RINFO(x) (0x000209f0 | ((x) << 25)) #define OCTEP_EPF_RINFO(x) (0x000209f0 | ((x) << 25))
#define OCTEP_VF_MBOX_DATA(x) (0x00010210 | ((x) << 17)) #define OCTEP_VF_MBOX_DATA(x) (0x00010210 | ((x) << 17))
#define OCTEP_PF_MBOX_DATA(x) (0x00022000 | ((x) << 4)) #define OCTEP_PF_MBOX_DATA(x) (0x00022000 | ((x) << 4))
#define OCTEP_VF_IN_CTRL(x) (0x00010000 | ((x) << 17))
#define OCTEP_EPF_RINFO_RPVF(val) (((val) >> 32) & 0xF) #define OCTEP_VF_IN_CTRL_RPVF(val) (((val) >> 48) & 0xF)
#define OCTEP_EPF_RINFO_NVFS(val) (((val) >> 48) & 0x7F)
#define OCTEP_FW_READY_SIGNATURE0 0xFEEDFEED #define OCTEP_FW_READY_SIGNATURE0 0xFEEDFEED
#define OCTEP_FW_READY_SIGNATURE1 0x3355ffaa #define OCTEP_FW_READY_SIGNATURE1 0x3355ffaa
#define OCTEP_MAX_CB_INTR 8
enum octep_vdpa_dev_status { enum octep_vdpa_dev_status {
OCTEP_VDPA_DEV_STATUS_INVALID, OCTEP_VDPA_DEV_STATUS_INVALID,
@ -48,9 +48,8 @@ enum octep_vdpa_dev_status {
struct octep_vring_info { struct octep_vring_info {
struct vdpa_callback cb; struct vdpa_callback cb;
void __iomem *notify_addr; void __iomem *notify_addr;
u32 __iomem *cb_notify_addr; void __iomem *cb_notify_addr;
phys_addr_t notify_pa; phys_addr_t notify_pa;
char msix_name[256];
}; };
struct octep_hw { struct octep_hw {
@ -68,7 +67,8 @@ struct octep_hw {
u64 features; u64 features;
u16 nr_vring; u16 nr_vring;
u32 config_size; u32 config_size;
int irq; int nb_irqs;
int *irqs;
}; };
u8 octep_hw_get_status(struct octep_hw *oct_hw); u8 octep_hw_get_status(struct octep_hw *oct_hw);

View File

@ -495,8 +495,6 @@ int octep_hw_caps_read(struct octep_hw *oct_hw, struct pci_dev *pdev)
if (!oct_hw->vqs) if (!oct_hw->vqs)
return -ENOMEM; return -ENOMEM;
oct_hw->irq = -1;
dev_info(&pdev->dev, "Device features : %llx\n", oct_hw->features); dev_info(&pdev->dev, "Device features : %llx\n", oct_hw->features);
dev_info(&pdev->dev, "Maximum queues : %u\n", oct_hw->nr_vring); dev_info(&pdev->dev, "Maximum queues : %u\n", oct_hw->nr_vring);

View File

@ -49,11 +49,25 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data)
struct octep_hw *oct_hw = data; struct octep_hw *oct_hw = data;
int i; int i;
for (i = 0; i < oct_hw->nr_vring; i++) { /* Each device has multiple interrupts (nb_irqs) shared among rings
if (oct_hw->vqs[i].cb.callback && ioread32(oct_hw->vqs[i].cb_notify_addr)) { * (nr_vring). Device interrupts are mapped to the rings in a
/* Acknowledge the per queue notification to the device */ * round-robin fashion.
iowrite32(0, oct_hw->vqs[i].cb_notify_addr); *
* For example, if nb_irqs = 8 and nr_vring = 64:
* 0 -> 0, 8, 16, 24, 32, 40, 48, 56;
* 1 -> 1, 9, 17, 25, 33, 41, 49, 57;
* ...
* 7 -> 7, 15, 23, 31, 39, 47, 55, 63;
*/
for (i = irq - oct_hw->irqs[0]; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) {
if (ioread8(oct_hw->vqs[i].cb_notify_addr)) {
/* Acknowledge the per ring notification to the device */
iowrite8(0, oct_hw->vqs[i].cb_notify_addr);
if (likely(oct_hw->vqs[i].cb.callback))
oct_hw->vqs[i].cb.callback(oct_hw->vqs[i].cb.private); oct_hw->vqs[i].cb.callback(oct_hw->vqs[i].cb.private);
break;
} }
} }
@ -63,44 +77,53 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data)
static void octep_free_irqs(struct octep_hw *oct_hw) static void octep_free_irqs(struct octep_hw *oct_hw)
{ {
struct pci_dev *pdev = oct_hw->pdev; struct pci_dev *pdev = oct_hw->pdev;
int irq;
if (oct_hw->irq != -1) { if (!oct_hw->irqs)
devm_free_irq(&pdev->dev, oct_hw->irq, oct_hw); return;
oct_hw->irq = -1;
for (irq = 0; irq < oct_hw->nb_irqs; irq++) {
if (!oct_hw->irqs[irq])
break;
devm_free_irq(&pdev->dev, oct_hw->irqs[irq], oct_hw);
} }
pci_free_irq_vectors(pdev); pci_free_irq_vectors(pdev);
devm_kfree(&pdev->dev, oct_hw->irqs);
oct_hw->irqs = NULL;
} }
static int octep_request_irqs(struct octep_hw *oct_hw) static int octep_request_irqs(struct octep_hw *oct_hw)
{ {
struct pci_dev *pdev = oct_hw->pdev; struct pci_dev *pdev = oct_hw->pdev;
int ret, irq; int ret, irq, idx;
/* Currently HW device provisions one IRQ per VF, hence oct_hw->irqs = devm_kcalloc(&pdev->dev, oct_hw->nb_irqs, sizeof(int), GFP_KERNEL);
* allocate one IRQ for all virtqueues call interface. if (!oct_hw->irqs)
*/ return -ENOMEM;
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
ret = pci_alloc_irq_vectors(pdev, 1, oct_hw->nb_irqs, PCI_IRQ_MSIX);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Failed to alloc msix vector"); dev_err(&pdev->dev, "Failed to alloc msix vector");
return ret; return ret;
} }
snprintf(oct_hw->vqs->msix_name, sizeof(oct_hw->vqs->msix_name), for (idx = 0; idx < oct_hw->nb_irqs; idx++) {
OCTEP_VDPA_DRIVER_NAME "-vf-%d", pci_iov_vf_id(pdev)); irq = pci_irq_vector(pdev, idx);
irq = pci_irq_vector(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, octep_vdpa_intr_handler, 0, ret = devm_request_irq(&pdev->dev, irq, octep_vdpa_intr_handler, 0,
oct_hw->vqs->msix_name, oct_hw); dev_name(&pdev->dev), oct_hw);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to register interrupt handler\n"); dev_err(&pdev->dev, "Failed to register interrupt handler\n");
goto free_irq_vec; goto free_irqs;
}
oct_hw->irqs[idx] = irq;
} }
oct_hw->irq = irq;
return 0; return 0;
free_irq_vec: free_irqs:
pci_free_irq_vectors(pdev); octep_free_irqs(oct_hw);
return ret; return ret;
} }
@ -559,6 +582,7 @@ static void octep_vdpa_setup_task(struct work_struct *work)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct octep_hw *oct_hw; struct octep_hw *oct_hw;
unsigned long timeout; unsigned long timeout;
u64 val;
int ret; int ret;
oct_hw = &mgmt_dev->oct_hw; oct_hw = &mgmt_dev->oct_hw;
@ -590,6 +614,13 @@ static void octep_vdpa_setup_task(struct work_struct *work)
if (ret) if (ret)
return; return;
val = readq(oct_hw->base[OCTEP_HW_MBOX_BAR] + OCTEP_VF_IN_CTRL(0));
oct_hw->nb_irqs = OCTEP_VF_IN_CTRL_RPVF(val);
if (!oct_hw->nb_irqs || oct_hw->nb_irqs > OCTEP_MAX_CB_INTR) {
dev_err(dev, "Invalid number of interrupts %d\n", oct_hw->nb_irqs);
goto unmap_region;
}
ret = octep_hw_caps_read(oct_hw, pdev); ret = octep_hw_caps_read(oct_hw, pdev);
if (ret < 0) if (ret < 0)
goto unmap_region; goto unmap_region;
@ -768,12 +799,6 @@ static int octep_vdpa_pf_setup(struct octep_pf *octpf)
return -EINVAL; return -EINVAL;
} }
if (OCTEP_EPF_RINFO_RPVF(val) != BIT_ULL(0)) {
val &= ~GENMASK_ULL(35, 32);
val |= BIT_ULL(32);
writeq(val, addr + OCTEP_EPF_RINFO(0));
}
len = pci_resource_len(pdev, OCTEP_HW_CAPS_BAR); len = pci_resource_len(pdev, OCTEP_HW_CAPS_BAR);
octpf->vf_stride = len / totalvfs; octpf->vf_stride = len / totalvfs;