|
@@ -521,6 +521,8 @@ struct hv_pci_compl {
|
|
|
s32 completion_status;
|
|
|
};
|
|
|
|
|
|
+static void hv_pci_onchannelcallback(void *context);
|
|
|
+
|
|
|
/**
|
|
|
* hv_pci_generic_compl() - Invoked for a completion packet
|
|
|
* @context: Set up by the sender of the packet.
|
|
@@ -665,6 +667,31 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
|
|
|
+{
|
|
|
+ u16 ret;
|
|
|
+ unsigned long flags;
|
|
|
+ void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET +
|
|
|
+ PCI_VENDOR_ID;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
|
|
+
|
|
|
+ /* Choose the function to be read. (See comment above) */
|
|
|
+ writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
|
|
+ /* Make sure the function was chosen before we start reading. */
|
|
|
+ mb();
|
|
|
+ /* Read from that function's config space. */
|
|
|
+ ret = readw(addr);
|
|
|
+ /*
|
|
|
+ * mb() is not required here, because the spin_unlock_irqrestore()
|
|
|
+ * is a barrier.
|
|
|
+ */
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* _hv_pcifront_write_config() - Internal PCI config write
|
|
|
* @hpdev: The PCI driver's representation of the device
|
|
@@ -1107,8 +1134,37 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
|
|
* Since this function is called with IRQ locks held, can't
|
|
|
* do normal wait for completion; instead poll.
|
|
|
*/
|
|
|
- while (!try_wait_for_completion(&comp.comp_pkt.host_event))
|
|
|
+ while (!try_wait_for_completion(&comp.comp_pkt.host_event)) {
|
|
|
+ /* 0xFFFF means an invalid PCI VENDOR ID. */
|
|
|
+ if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) {
|
|
|
+ dev_err_once(&hbus->hdev->device,
|
|
|
+ "the device has gone\n");
|
|
|
+ goto free_int_desc;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * When the higher level interrupt code calls us with
|
|
|
+ * interrupt disabled, we must poll the channel by calling
|
|
|
+ * the channel callback directly when channel->target_cpu is
|
|
|
+ * the current CPU. When the higher level interrupt code
|
|
|
+ * calls us with interrupt enabled, let's add the
|
|
|
+ * local_bh_disable()/enable() to avoid race.
|
|
|
+ */
|
|
|
+ local_bh_disable();
|
|
|
+
|
|
|
+ if (hbus->hdev->channel->target_cpu == smp_processor_id())
|
|
|
+ hv_pci_onchannelcallback(hbus);
|
|
|
+
|
|
|
+ local_bh_enable();
|
|
|
+
|
|
|
+ if (hpdev->state == hv_pcichild_ejecting) {
|
|
|
+ dev_err_once(&hbus->hdev->device,
|
|
|
+ "the device is being ejected\n");
|
|
|
+ goto free_int_desc;
|
|
|
+ }
|
|
|
+
|
|
|
udelay(100);
|
|
|
+ }
|
|
|
|
|
|
if (comp.comp_pkt.completion_status < 0) {
|
|
|
dev_err(&hbus->hdev->device,
|