mirror of https://gitee.com/openkylin/linux.git
Merge branch 'remotes/lorenzo/pci/hv'
- Release resource in probe failure path (Wei Hu) - Retry PCI bus D0 entry if device state is invalid (Wei Hu) - Use struct_size() to help avoid type mistakes (Gustavo A. R. Silva) * remotes/lorenzo/pci/hv: PCI: hv: Use struct_size() helper PCI: hv: Retry PCI bus D0 entry on invalid device state PCI: hv: Fix the PCI HyperV probe failure path to release resource properly
This commit is contained in:
commit
7c9908d3c8
|
@ -480,6 +480,9 @@ struct hv_pcibus_device {
|
|||
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/* Highest slot of child device with resources allocated */
|
||||
int wslot_res_allocated;
|
||||
|
||||
/* hypercall arg, must not cross page boundary */
|
||||
struct hv_retarget_device_interrupt retarget_msi_interrupt_params;
|
||||
|
||||
|
@ -2198,10 +2201,8 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
|
|||
struct hv_dr_state *dr;
|
||||
int i;
|
||||
|
||||
dr = kzalloc(offsetof(struct hv_dr_state, func) +
|
||||
(sizeof(struct hv_pcidev_description) *
|
||||
(relations->device_count)), GFP_NOWAIT);
|
||||
|
||||
dr = kzalloc(struct_size(dr, func, relations->device_count),
|
||||
GFP_NOWAIT);
|
||||
if (!dr)
|
||||
return;
|
||||
|
||||
|
@ -2235,10 +2236,8 @@ static void hv_pci_devices_present2(struct hv_pcibus_device *hbus,
|
|||
struct hv_dr_state *dr;
|
||||
int i;
|
||||
|
||||
dr = kzalloc(offsetof(struct hv_dr_state, func) +
|
||||
(sizeof(struct hv_pcidev_description) *
|
||||
(relations->device_count)), GFP_NOWAIT);
|
||||
|
||||
dr = kzalloc(struct_size(dr, func, relations->device_count),
|
||||
GFP_NOWAIT);
|
||||
if (!dr)
|
||||
return;
|
||||
|
||||
|
@ -2432,9 +2431,8 @@ static void hv_pci_onchannelcallback(void *context)
|
|||
|
||||
bus_rel = (struct pci_bus_relations *)buffer;
|
||||
if (bytes_recvd <
|
||||
offsetof(struct pci_bus_relations, func) +
|
||||
(sizeof(struct pci_function_description) *
|
||||
(bus_rel->device_count))) {
|
||||
struct_size(bus_rel, func,
|
||||
bus_rel->device_count)) {
|
||||
dev_err(&hbus->hdev->device,
|
||||
"bus relations too small\n");
|
||||
break;
|
||||
|
@ -2447,9 +2445,8 @@ static void hv_pci_onchannelcallback(void *context)
|
|||
|
||||
bus_rel2 = (struct pci_bus_relations2 *)buffer;
|
||||
if (bytes_recvd <
|
||||
offsetof(struct pci_bus_relations2, func) +
|
||||
(sizeof(struct pci_function_description2) *
|
||||
(bus_rel2->device_count))) {
|
||||
struct_size(bus_rel2, func,
|
||||
bus_rel2->device_count)) {
|
||||
dev_err(&hbus->hdev->device,
|
||||
"bus relations v2 too small\n");
|
||||
break;
|
||||
|
@ -2736,6 +2733,8 @@ static void hv_free_config_window(struct hv_pcibus_device *hbus)
|
|||
vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
|
||||
}
|
||||
|
||||
static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs);
|
||||
|
||||
/**
|
||||
* hv_pci_enter_d0() - Bring the "bus" into the D0 power state
|
||||
* @hdev: VMBus's tracking struct for this root PCI bus
|
||||
|
@ -2748,8 +2747,10 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
|
|||
struct pci_bus_d0_entry *d0_entry;
|
||||
struct hv_pci_compl comp_pkt;
|
||||
struct pci_packet *pkt;
|
||||
bool retry = true;
|
||||
int ret;
|
||||
|
||||
enter_d0_retry:
|
||||
/*
|
||||
* Tell the host that the bus is ready to use, and moved into the
|
||||
* powered-on state. This includes telling the host which region
|
||||
|
@ -2776,6 +2777,38 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
|
|||
if (ret)
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
* In certain case (Kdump) the pci device of interest was
|
||||
* not cleanly shut down and resource is still held on host
|
||||
* side, the host could return invalid device status.
|
||||
* We need to explicitly request host to release the resource
|
||||
* and try to enter D0 again.
|
||||
*/
|
||||
if (comp_pkt.completion_status < 0 && retry) {
|
||||
retry = false;
|
||||
|
||||
dev_err(&hdev->device, "Retrying D0 Entry\n");
|
||||
|
||||
/*
|
||||
* Hv_pci_bus_exit() calls hv_send_resource_released()
|
||||
* to free up resources of its child devices.
|
||||
* In the kdump kernel we need to set the
|
||||
* wslot_res_allocated to 255 so it scans all child
|
||||
* devices to release resources allocated in the
|
||||
* normal kernel before panic happened.
|
||||
*/
|
||||
hbus->wslot_res_allocated = 255;
|
||||
|
||||
ret = hv_pci_bus_exit(hdev, true);
|
||||
|
||||
if (ret == 0) {
|
||||
kfree(pkt);
|
||||
goto enter_d0_retry;
|
||||
}
|
||||
dev_err(&hdev->device,
|
||||
"Retrying D0 failed with ret %d\n", ret);
|
||||
}
|
||||
|
||||
if (comp_pkt.completion_status < 0) {
|
||||
dev_err(&hdev->device,
|
||||
"PCI Pass-through VSP failed D0 Entry with status %x\n",
|
||||
|
@ -2847,7 +2880,7 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
|
|||
struct hv_pci_dev *hpdev;
|
||||
struct pci_packet *pkt;
|
||||
size_t size_res;
|
||||
u32 wslot;
|
||||
int wslot;
|
||||
int ret;
|
||||
|
||||
size_res = (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_2)
|
||||
|
@ -2900,6 +2933,8 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
|
|||
comp_pkt.completion_status);
|
||||
break;
|
||||
}
|
||||
|
||||
hbus->wslot_res_allocated = wslot;
|
||||
}
|
||||
|
||||
kfree(pkt);
|
||||
|
@ -2918,10 +2953,10 @@ static int hv_send_resources_released(struct hv_device *hdev)
|
|||
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
|
||||
struct pci_child_message pkt;
|
||||
struct hv_pci_dev *hpdev;
|
||||
u32 wslot;
|
||||
int wslot;
|
||||
int ret;
|
||||
|
||||
for (wslot = 0; wslot < 256; wslot++) {
|
||||
for (wslot = hbus->wslot_res_allocated; wslot >= 0; wslot--) {
|
||||
hpdev = get_pcichild_wslot(hbus, wslot);
|
||||
if (!hpdev)
|
||||
continue;
|
||||
|
@ -2936,8 +2971,12 @@ static int hv_send_resources_released(struct hv_device *hdev)
|
|||
VM_PKT_DATA_INBAND, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hbus->wslot_res_allocated = wslot - 1;
|
||||
}
|
||||
|
||||
hbus->wslot_res_allocated = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3037,6 +3076,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|||
if (!hbus)
|
||||
return -ENOMEM;
|
||||
hbus->state = hv_pcibus_init;
|
||||
hbus->wslot_res_allocated = -1;
|
||||
|
||||
/*
|
||||
* The PCI bus "domain" is what is called "segment" in ACPI and other
|
||||
|
@ -3136,7 +3176,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|||
|
||||
ret = hv_pci_allocate_bridge_windows(hbus);
|
||||
if (ret)
|
||||
goto free_irq_domain;
|
||||
goto exit_d0;
|
||||
|
||||
ret = hv_send_resources_allocated(hdev);
|
||||
if (ret)
|
||||
|
@ -3154,6 +3194,8 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|||
|
||||
free_windows:
|
||||
hv_pci_free_bridge_windows(hbus);
|
||||
exit_d0:
|
||||
(void) hv_pci_bus_exit(hdev, true);
|
||||
free_irq_domain:
|
||||
irq_domain_remove(hbus->irq_domain);
|
||||
free_fwnode:
|
||||
|
@ -3173,7 +3215,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating)
|
||||
static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
|
||||
{
|
||||
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
|
||||
struct {
|
||||
|
@ -3191,7 +3233,7 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating)
|
|||
if (hdev->channel->rescind)
|
||||
return 0;
|
||||
|
||||
if (!hibernating) {
|
||||
if (!keep_devs) {
|
||||
/* Delete any children which might still exist. */
|
||||
dr = kzalloc(sizeof(*dr), GFP_KERNEL);
|
||||
if (dr && hv_pci_start_relations_work(hbus, dr))
|
||||
|
|
Loading…
Reference in New Issue