Commit 2c269f42 authored by Marek Marczykowski-Górecki's avatar Marek Marczykowski-Górecki Committed by Juergen Gross

xen-pciback: Consider INTx disabled when MSI/MSI-X is enabled

Linux enables MSI-X before disabling INTx, but keeps MSI-X masked until
the table is filled. Then it disables INTx just before clearing MASKALL
bit. Currently this approach is rejected by xen-pciback.
According to the PCIe spec, device cannot use INTx when MSI/MSI-X is
enabled (in other words: enabling MSI/MSI-X implicitly disables INTx).

Change the logic to consider INTx disabled if MSI/MSI-X is enabled. This
applies to three places:
 - checking currently enabled interrupts type,
 - transition to MSI/MSI-X - where INTx would be implicitly disabled,
 - clearing INTx disable bit - which can be allowed even if MSI/MSI-X is
   enabled, as device should consider INTx disabled anyway in that case

Fixes: 5e29500e ("xen-pciback: Allow setting PCI_MSIX_FLAGS_MASKALL too")
Signed-off-by: default avatarMarek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Acked-by: default avatarJuergen Gross <jgross@suse.com>
Link: https://lore.kernel.org/r/20231016131348.1734721-1-marmarek@invisiblethingslab.comSigned-off-by: default avatarJuergen Gross <jgross@suse.com>
parent f0d7db7b
...@@ -288,12 +288,6 @@ int xen_pcibk_get_interrupt_type(struct pci_dev *dev) ...@@ -288,12 +288,6 @@ int xen_pcibk_get_interrupt_type(struct pci_dev *dev)
u16 val; u16 val;
int ret = 0; int ret = 0;
err = pci_read_config_word(dev, PCI_COMMAND, &val);
if (err)
return err;
if (!(val & PCI_COMMAND_INTX_DISABLE))
ret |= INTERRUPT_TYPE_INTX;
/* /*
* Do not trust dev->msi(x)_enabled here, as enabling could be done * Do not trust dev->msi(x)_enabled here, as enabling could be done
* bypassing the pci_*msi* functions, by the qemu. * bypassing the pci_*msi* functions, by the qemu.
...@@ -316,6 +310,19 @@ int xen_pcibk_get_interrupt_type(struct pci_dev *dev) ...@@ -316,6 +310,19 @@ int xen_pcibk_get_interrupt_type(struct pci_dev *dev)
if (val & PCI_MSIX_FLAGS_ENABLE) if (val & PCI_MSIX_FLAGS_ENABLE)
ret |= INTERRUPT_TYPE_MSIX; ret |= INTERRUPT_TYPE_MSIX;
} }
/*
* PCIe spec says device cannot use INTx if MSI/MSI-X is enabled,
* so check for INTx only when both are disabled.
*/
if (!ret) {
err = pci_read_config_word(dev, PCI_COMMAND, &val);
if (err)
return err;
if (!(val & PCI_COMMAND_INTX_DISABLE))
ret |= INTERRUPT_TYPE_INTX;
}
return ret ?: INTERRUPT_TYPE_NONE; return ret ?: INTERRUPT_TYPE_NONE;
} }
......
...@@ -236,10 +236,16 @@ static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value, ...@@ -236,10 +236,16 @@ static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value,
return PCIBIOS_SET_FAILED; return PCIBIOS_SET_FAILED;
if (new_value & field_config->enable_bit) { if (new_value & field_config->enable_bit) {
/* don't allow enabling together with other interrupt types */ /*
* Don't allow enabling together with other interrupt type, but do
* allow enabling MSI(-X) while INTx is still active to please Linuxes
* MSI(-X) startup sequence. It is safe to do, as according to PCI
* spec, device with enabled MSI(-X) shouldn't use INTx.
*/
int int_type = xen_pcibk_get_interrupt_type(dev); int int_type = xen_pcibk_get_interrupt_type(dev);
if (int_type == INTERRUPT_TYPE_NONE || if (int_type == INTERRUPT_TYPE_NONE ||
int_type == INTERRUPT_TYPE_INTX ||
int_type == field_config->int_type) int_type == field_config->int_type)
goto write; goto write;
return PCIBIOS_SET_FAILED; return PCIBIOS_SET_FAILED;
......
...@@ -104,24 +104,9 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) ...@@ -104,24 +104,9 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
pci_clear_mwi(dev); pci_clear_mwi(dev);
} }
if (dev_data && dev_data->allow_interrupt_control) { if (dev_data && dev_data->allow_interrupt_control &&
if ((cmd->val ^ value) & PCI_COMMAND_INTX_DISABLE) { ((cmd->val ^ value) & PCI_COMMAND_INTX_DISABLE))
if (value & PCI_COMMAND_INTX_DISABLE) { pci_intx(dev, !(value & PCI_COMMAND_INTX_DISABLE));
pci_intx(dev, 0);
} else {
/* Do not allow enabling INTx together with MSI or MSI-X. */
switch (xen_pcibk_get_interrupt_type(dev)) {
case INTERRUPT_TYPE_NONE:
pci_intx(dev, 1);
break;
case INTERRUPT_TYPE_INTX:
break;
default:
return PCIBIOS_SET_FAILED;
}
}
}
}
cmd->val = value; cmd->val = value;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment