mutex_lock(&chip_data->map_lock);
bitmap_release_region(chip_data->spi_map,
first,
get_count_order(nr_bm_irqs));
mutex_unlock(&chip_data->map_lock); for (i = 0; i < nr_dom_irqs; i++) { if (i)
d = irq_domain_get_irq_data(domain, virq + i);
irq_domain_reset_irq_data(d);
}
/* Find and allocate region from the SPI bitmap */
mutex_lock(&chip_data->map_lock);
index = bitmap_find_free_region(chip_data->spi_map,
HV_PCI_MSI_SPI_NR,
get_count_order(nr_irqs));
mutex_unlock(&chip_data->map_lock); if (index < 0) return -ENOSPC;
ret = hv_pci_vec_alloc_device_irq(domain, nr_irqs, &hwirq); if (ret) return ret;
for (i = 0; i < nr_irqs; i++) {
ret = hv_pci_vec_irq_gic_domain_alloc(domain, virq + i,
hwirq + i); if (ret) {
hv_pci_vec_irq_free(domain, virq, nr_irqs, i); return ret;
}
irq_domain_set_hwirq_and_chip(domain, virq + i,
hwirq + i,
&hv_arm64_msi_irq_chip,
domain->host_data);
pr_debug("pID:%d vID:%u\n", (int)(hwirq + i), virq + i);
}
return0;
}
/* *Pickthefirstcpuastheirqaffinitythatcanbetemporarilyusedfor *composingMSIfromthehypervisor.GICwilleventuallysettheright *affinityfortheirqandthe'unmask'willretargettheinterrupttothat *cpu.
*/ staticint hv_pci_vec_irq_domain_activate(struct irq_domain *domain, struct irq_data *irqd, bool reserve)
{ int cpu = cpumask_first(cpu_present_mask);
hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
hpdev->desc.win_slot.slot);
hv_pci_read_mmio(dev, addr, 2, &val);
ret = val; /* Truncates to 16 bits */
} else { void __iomem *addr = hbus->cfg_addr + CFG_PAGE_OFFSET +
PCI_VENDOR_ID; /* Choose the function to be read. (See comment above) */
writel(hpdev->desc.win_slot.slot, 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()isnotrequiredhere,becausethe *spin_unlock_irqrestore()isabarrier.
*/
}
/**
* hv_compose_msi_msg() - Supplies a valid MSI address/data
* @data: Everything about this MSI
* @msg: Buffer that is filled in by this function
*
* This function unpacks the IRQ looking for target CPU set, IDT
* vector and mode and sends a message to the parent partition
* asking for a mapping for that tuple in this partition. The
* response supplies a data value and address to which that data
* should be written to trigger that interrupt.
*/
static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct hv_pcibus_device *hbus;
struct vmbus_channel *channel;
struct hv_pci_dev *hpdev;
struct pci_bus *pbus;
struct pci_dev *pdev;
const struct cpumask *dest;
struct compose_comp_ctxt comp;
struct tran_int_desc *int_desc;
struct msi_desc *msi_desc;
/*
* vector_count should be u16: see hv_msi_desc, hv_msi_desc2
* and hv_msi_desc3. vector must be u32: see hv_msi_desc3.
*/
u16 vector_count;
u32 vector;
struct {
struct pci_packet pci_pkt;
union {
struct pci_create_interrupt v1;
struct pci_create_interrupt2 v2;
struct pci_create_interrupt3 v3;
} int_pkts;
} __packed ctxt;
bool multi_msi;
u64 trans_id;
u32 size;
int ret;
int cpu;
/* Free any previous message that might have already been composed. */
if (data->chip_data && !multi_msi) {
int_desc = data->chip_data;
data->chip_data = NULL;
hv_int_desc_free(hpdev, int_desc);
}
int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC);
if (!int_desc)
goto drop_reference;
if (multi_msi) {
/*
* If this is not the first MSI of Multi MSI, we already have
* a mapping. Can exit early.
*/
if (msi_desc->irq != data->irq) {
data->chip_data = int_desc;
int_desc->address = msi_desc->msg.address_lo |
(u64)msi_desc->msg.address_hi << 32;
int_desc->data = msi_desc->msg.data +
(data->irq - msi_desc->irq);
msg->address_hi = msi_desc->msg.address_hi;
msg->address_lo = msi_desc->msg.address_lo;
msg->data = int_desc->data;
put_pcichild(hpdev);
return;
}
/*
* The vector we select here is a dummy value. The correct
* value gets sent to the hypervisor in unmask(). This needs
* to be aligned with the count, and also not zero. Multi-msi
* is powers of 2 up to 32, so 32 will always work here.
*/
vector = 32;
vector_count = msi_desc->nvec_used;
cpu = hv_compose_multi_msi_req_get_cpu();
} else {
vector = hv_msi_get_int_vector(data);
vector_count = 1;
cpu = hv_compose_msi_req_get_cpu(dest);
}
/*
* hv_compose_msi_req_v1 and v2 are for x86 only, meaning 'vector'
* can't exceed u8. Cast 'vector' down to u8 for v1/v2 explicitly
* for better readability.
*/
memset(&ctxt, 0, sizeof(ctxt));
init_completion(&comp.comp_pkt.host_event);
ctxt.pci_pkt.completion_func = hv_pci_compose_compl;
ctxt.pci_pkt.compl_ctxt = ∁
case PCI_PROTOCOL_VERSION_1_2:
case PCI_PROTOCOL_VERSION_1_3:
size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2,
cpu,
hpdev->desc.win_slot.slot,
(u8)vector,
vector_count);
break;
case PCI_PROTOCOL_VERSION_1_4:
size = hv_compose_msi_req_v3(&ctxt.int_pkts.v3,
cpu,
hpdev->desc.win_slot.slot,
vector,
vector_count);
break;
default:
/* As we only negotiate protocol versions known to this driver,
* this path should never hit. However, this is it not a hot
* path so we print a message to aid future updates.
*/
dev_err(&hbus->hdev->device,
"Unexpected vPCI protocol, update driver.");
goto free_int_desc;
}
ret = vmbus_sendpacket_getid(hpdev->hbus->hdev->channel, &ctxt.int_pkts,
size, (unsigned long)&ctxt.pci_pkt,
&trans_id, VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (ret) {
dev_err(&hbus->hdev->device,
"Sending request for interrupt failed: 0x%x",
comp.comp_pkt.completion_status);
goto free_int_desc;
}
/*
* Prevents hv_pci_onchannelcallback() from running concurrently
* in the tasklet.
*/
tasklet_disable_in_atomic(&channel->callback_event);
/*
* 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)) {
unsigned long flags;
/* 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 enable_tasklet;
}
/*
* Make sure that the ring buffer data structure doesn't get
* freed while we dereference the ring buffer pointer. Test
* for the channel's onchannel_callback being NULL within a
* sched_lock critical section. See also the inline comments
* in vmbus_reset_channel_cb().
*/
spin_lock_irqsave(&channel->sched_lock, flags);
if (unlikely(channel->onchannel_callback == NULL)) {
spin_unlock_irqrestore(&channel->sched_lock, flags);
goto enable_tasklet;
}
hv_pci_onchannelcallback(hbus);
spin_unlock_irqrestore(&channel->sched_lock, flags);
udelay(100);
}
tasklet_enable(&channel->callback_event);
if (comp.comp_pkt.completion_status < 0) {
dev_err(&hbus->hdev->device,
"Request for interrupt failed: 0x%x",
comp.comp_pkt.completion_status);
goto free_int_desc;
}
/*
* Record the assignment so that this can be unwound later. Using
* irq_set_chip_data() here would be appropriate, but the lock it takes
* is already held.
*/
*int_desc = comp.int_desc;
data->chip_data = int_desc;
/* Pass up the result. */
msg->address_hi = comp.int_desc.address >> 32;
msg->address_lo = comp.int_desc.address & 0xffffffff;
msg->data = comp.int_desc.data;
put_pcichild(hpdev);
return;
enable_tasklet:
tasklet_enable(&channel->callback_event);
/*
* The completion packet on the stack becomes invalid after 'return';
* remove the ID from the VMbus requestor if the identifier is still
* mapped to/associated with the packet. (The identifier could have
* been 're-used', i.e., already removed and (re-)mapped.)
*
* Cf. hv_pci_onchannelcallback().
*/
vmbus_request_addr_match(channel, trans_id, (unsigned long)&ctxt.pci_pkt);
free_int_desc:
kfree(int_desc);
drop_reference:
put_pcichild(hpdev);
return_null_message:
msg->address_hi = 0;
msg->address_lo = 0;
msg->data = 0;
}
static int hv_pcie_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs,
void *arg)
{
/*
* TODO: Allocating and populating struct tran_int_desc in hv_compose_msi_msg()
* should be moved here.
*/
int ret;
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, arg);
if (ret < 0)
return ret;
for (int i = 0; i < nr_irqs; i++) {
irq_domain_set_hwirq_and_chip(d, virq + i, 0, &hv_msi_irq_chip, NULL);
if (IS_ENABLED(CONFIG_X86))
__irq_set_handler(virq + i, handle_edge_irq, 0, "edge");
}
return 0;
}
static void hv_pcie_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs)
{
struct msi_domain_info *info = d->host_data;
for (int i = 0; i < nr_irqs; i++)
hv_msi_free(d, info, virq + i);
/**
* hv_pcie_init_irq_domain() - Initialize IRQ domain
* @hbus: The root PCI bus
*
* This function creates an IRQ domain which will be used for
* interrupts from devices that have been passed through. These
* devices only support MSI and MSI-X, not line-based interrupts
* or simulations of line-based interrupts through PCIe's
* fabric-layer messages. Because interrupts are remapped, we
* can support multi-message MSI here.
*
* Return: '0' on success and error value on failure
*/
static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus)
{
struct irq_domain_info info = {
.fwnode = hbus->fwnode,
.ops = &hv_pcie_domain_ops,
.host_data = hbus,
.parent = hv_pci_get_root_domain(),
};
hbus->irq_domain = msi_create_parent_irq_domain(&info, &hv_pcie_msi_parent_ops);
if (!hbus->irq_domain) {
dev_err(&hbus->hdev->device,
"Failed to build an MSI IRQ domain\n");
return -ENODEV;
}
/**
* get_bar_size() - Get the address space consumed by a BAR
* @bar_val: Value that a BAR returned after -1 was written
* to it.
*
* This function returns the size of the BAR, rounded up to 1
* page. It has to be rounded up because the hypervisor's page
* table entry that maps the BAR into the VM can't specify an
* offset within a page. The invariant is that the hypervisor
* must place any BARs of smaller than page length at the
* beginning of a page.
*
* Return: Size in bytes of the consumed MMIO space.
*/
static u64 get_bar_size(u64 bar_val)
{
return round_up((1 + ~(bar_val & PCI_BASE_ADDRESS_MEM_MASK)),
PAGE_SIZE);
}
/**
* survey_child_resources() - Total all MMIO requirements
* @hbus: Root PCI bus, as understood by this driver
*/
static void survey_child_resources(struct hv_pcibus_device *hbus)
{
struct hv_pci_dev *hpdev;
resource_size_t bar_size = 0;
unsigned long flags;
struct completion *event;
u64 bar_val;
int i;
/* If nobody is waiting on the answer, don't compute it. */
event = xchg(&hbus->survey_event, NULL);
if (!event)
return;
/* If the answer has already been computed, go with it. */
if (hbus->low_mmio_space || hbus->high_mmio_space) {
complete(event);
return;
}
/*
* Due to an interesting quirk of the PCI spec, all memory regions
* for a child device are a power of 2 in size and aligned in memory,
* so it's sufficient to just add them up without tracking alignment.
*/
list_for_each_entry(hpdev, &hbus->children, list_entry) {
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO)
dev_err(&hbus->hdev->device,
"There's an I/O BAR in this list!\n");
if (hpdev->probed_bar[i] != 0) {
/*
* A probed BAR has all the upper bits set that
* can be changed.
*/
/**
* prepopulate_bars() - Fill in BARs with defaults
* @hbus: Root PCI bus, as understood by this driver
*
* The core PCI driver code seems much, much happier if the BARs
* for a device have values upon first scan. So fill them in.
* The algorithm below works down from large sizes to small,
* attempting to pack the assignments optimally. The assumption,
* enforced in other parts of the code, is that the beginning of
* the memory-mapped I/O space will be aligned on the largest
* BAR size.
*/
static void prepopulate_bars(struct hv_pcibus_device *hbus)
{
resource_size_t high_size = 0;
resource_size_t low_size = 0;
resource_size_t high_base = 0;
resource_size_t low_base = 0;
resource_size_t bar_size;
struct hv_pci_dev *hpdev;
unsigned long flags;
u64 bar_val;
u32 command;
bool high;
int i;
/*
* Clear the memory enable bit, in case it's already set. This occurs
* in the suspend path of hibernation, where the device is suspended,
* resumed and suspended again: see hibernation_snapshot() and
* hibernation_platform_enter().
*
* If the memory enable bit is already set, Hyper-V silently ignores
* the below BAR updates, and the related PCI device driver can not
* work, because reading from the device register(s) always returns
* 0xFFFFFFFF (PCI_ERROR_RESPONSE).
*/
list_for_each_entry(hpdev, &hbus->children, list_entry) {
_hv_pcifront_read_config(hpdev, PCI_COMMAND, 2, &command);
command &= ~PCI_COMMAND_MEMORY;
_hv_pcifront_write_config(hpdev, PCI_COMMAND, 2, command);
}
/* Pick addresses for the BARs. */
do {
list_for_each_entry(hpdev, &hbus->children, list_entry) {
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
bar_val = hpdev->probed_bar[i];
if (bar_val == 0)
continue;
high = bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64;
if (high) {
bar_val |=
((u64)hpdev->probed_bar[i + 1]
<< 32);
} else {
bar_val |= 0xffffffffULL << 32;
}
bar_size = get_bar_size(bar_val);
if (high) {
if (high_size != bar_size) {
i++;
continue;
}
_hv_pcifront_write_config(hpdev,
PCI_BASE_ADDRESS_0 + (4 * i), 4,
(u32)(high_base & 0xffffff00));
i++;
_hv_pcifront_write_config(hpdev,
PCI_BASE_ADDRESS_0 + (4 * i), 4, (u32)(high_base >> 32));
high_base += bar_size;
} else {
if (low_size != bar_size)
continue;
_hv_pcifront_write_config(hpdev,
PCI_BASE_ADDRESS_0 + (4 * i), 4,
(u32)(low_base & 0xffffff00));
low_base += bar_size;
}
}
if (high_size <= 1 && low_size <= 1) {
/*
* No need to set the PCI_COMMAND_MEMORY bit as
* the core PCI driver doesn't require the bit
* to be pre-set. Actually here we intentionally
* keep the bit off so that the PCI BAR probing
* in the core PCI driver doesn't cause Hyper-V
* to unnecessarily unmap/map the virtual BARs
* from/to the physical BARs multiple times.
* This reduces the VM boot time significantly
* if the BAR sizes are huge.
*/
break;
}
}
/*
* Assign entries in sysfs pci slot directory.
*
* Note that this function does not need to lock the children list
* because it is called from pci_devices_present_work which
* is serialized with hv_eject_device_work because they are on the
* same ordered workqueue. Therefore hbus->children list will not change
* even when pci_create_slot sleeps.
*/
static void hv_pci_assign_slots(struct hv_pcibus_device *hbus)
{
struct hv_pci_dev *hpdev;
char name[SLOT_NAME_SIZE];
int slot_nr;
list_for_each_entry(hpdev, &hbus->children, list_entry) {
if (hpdev->pci_slot)
continue;
/*
* Set NUMA node for the devices on the bus
*/
static void hv_pci_assign_numa_node(struct hv_pcibus_device *hbus)
{
struct pci_dev *dev;
struct pci_bus *bus = hbus->bridge->bus;
struct hv_pci_dev *hv_dev;
if (hv_dev->desc.flags & HV_PCI_DEVICE_FLAG_NUMA_AFFINITY &&
hv_dev->desc.virtual_numa_node < num_possible_nodes())
/*
* The kernel may boot with some NUMA nodes offline
* (e.g. in a KDUMP kernel) or with NUMA disabled via
* "numa=off". In those cases, adjust the host provided
* NUMA node to a valid NUMA node used by the kernel.
*/
set_dev_node(&dev->dev,
numa_map_to_online_node(
hv_dev->desc.virtual_numa_node));
put_pcichild(hv_dev);
}
}
/**
* create_root_hv_pci_bus() - Expose a new root PCI bus
* @hbus: Root PCI bus, as understood by this driver
*
* Return: 0 on success, -errno on failure
*/
static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)
{
int error;
struct pci_host_bridge *bridge = hbus->bridge;
/**
* q_resource_requirements() - Query Resource Requirements
* @context: The completion context.
* @resp: The response that came from the host.
* @resp_packet_size: The size in bytes of resp.
*
* This function is invoked on completion of a Query Resource
* Requirements packet.
*/
static void q_resource_requirements(void *context, struct pci_response *resp,
int resp_packet_size)
{
struct q_res_req_compl *completion = context;
struct pci_q_res_req_response *q_res_req =
(struct pci_q_res_req_response *)resp;
s32 status;
int i;
status = (resp_packet_size < sizeof(*q_res_req)) ? -1 : resp->status;
if (status < 0) {
dev_err(&completion->hpdev->hbus->hdev->device,
"query resource requirements failed: %x\n",
status);
} else {
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
completion->hpdev->probed_bar[i] =
q_res_req->probed_bar[i];
}
}
complete(&completion->host_event);
}
/**
* new_pcichild_device() - Create a new child device
* @hbus: The internal struct tracking this root PCI bus.
* @desc: The information supplied so far from the host
* about the device.
*
* This function creates the tracking structure for a new child
* device and kicks off the process of figuring out what it is.
*
* Return: Pointer to the new tracking struct
*/
static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
struct hv_pcidev_description *desc)
{
struct hv_pci_dev *hpdev;
struct pci_child_message *res_req;
struct q_res_req_compl comp_pkt;
struct {
struct pci_packet init_packet;
u8 buffer[sizeof(struct pci_child_message)];
} pkt;
unsigned long flags;
int ret;
hpdev = kzalloc(sizeof(*hpdev), GFP_KERNEL);
if (!hpdev)
return NULL;
/**
* get_pcichild_wslot() - Find device from slot
* @hbus: Root PCI bus, as understood by this driver
* @wslot: Location on the bus
*
* This function looks up a PCI device and returns the internal
* representation of it. It acquires a reference on it, so that
* the device won't be deleted while somebody is using it. The
* caller is responsible for calling put_pcichild() to release
* this reference.
*
* Return: Internal representation of a PCI device
*/
static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
u32 wslot)
{
unsigned long flags;
struct hv_pci_dev *iter, *hpdev = NULL;
/**
* pci_devices_present_work() - Handle new list of child devices
* @work: Work struct embedded in struct hv_dr_work
*
* "Bus Relations" is the Windows term for "children of this
* bus." The terminology is preserved here for people trying to
* debug the interaction between Hyper-V and Linux. This
* function is called when the parent partition reports a list
* of functions that should be observed under this PCI Express
* port (bus).
*
* This function updates the list, and must tolerate being
* called multiple times with the same information. The typical
* number of child devices is one, with very atypical cases
* involving three or four, so the algorithms used here can be
* simple and inefficient.
*
* It must also treat the omission of a previously observed device as
* notification that the device no longer exists.
*
* Note that this function is serialized with hv_eject_device_work(),
* because both are pushed to the ordered workqueue hbus->wq.
*/
static void pci_devices_present_work(struct work_struct *work)
{
u32 child_no;
bool found;
struct hv_pcidev_description *new_desc;
struct hv_pci_dev *hpdev;
struct hv_pcibus_device *hbus;
struct list_head removed;
struct hv_dr_work *dr_wrk;
struct hv_dr_state *dr = NULL;
unsigned long flags;
/* Pull this off the queue and process it if it was the last one. */
spin_lock_irqsave(&hbus->device_list_lock, flags);
while (!list_empty(&hbus->dr_list)) {
dr = list_first_entry(&hbus->dr_list, struct hv_dr_state,
list_entry);
list_del(&dr->list_entry);
/* Throw this away if the list still has stuff in it. */
if (!list_empty(&hbus->dr_list)) {
kfree(dr);
continue;
}
}
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
if (!dr)
return;
mutex_lock(&hbus->state_lock);
/* First, mark all existing children as reported missing. */
spin_lock_irqsave(&hbus->device_list_lock, flags);
list_for_each_entry(hpdev, &hbus->children, list_entry) {
hpdev->reported_missing = true;
}
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
/* Next, add back any reported devices. */
for (child_no = 0; child_no < dr->device_count; child_no++) {
found = false;
new_desc = &dr->func[child_no];
if (!found) {
hpdev = new_pcichild_device(hbus, new_desc);
if (!hpdev)
dev_err(&hbus->hdev->device,
"couldn't record a child device.\n");
}
}
/* Move missing children to a list on the stack. */
spin_lock_irqsave(&hbus->device_list_lock, flags);
do {
found = false;
list_for_each_entry(hpdev, &hbus->children, list_entry) {
if (hpdev->reported_missing) {
found = true;
put_pcichild(hpdev);
list_move_tail(&hpdev->list_entry, &removed);
break;
}
}
} while (found);
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
/* Delete everything that should no longer exist. */
while (!list_empty(&removed)) {
hpdev = list_first_entry(&removed, struct hv_pci_dev,
list_entry);
list_del(&hpdev->list_entry);
if (hpdev->pci_slot)
pci_destroy_slot(hpdev->pci_slot);
put_pcichild(hpdev);
}
switch (hbus->state) {
case hv_pcibus_installed:
/*
* Tell the core to rescan bus
* because there may have been changes.
*/
pci_lock_rescan_remove();
pci_scan_child_bus(hbus->bridge->bus);
hv_pci_assign_numa_node(hbus);
hv_pci_assign_slots(hbus);
pci_unlock_rescan_remove();
break;
case hv_pcibus_init:
case hv_pcibus_probed:
survey_child_resources(hbus);
break;
default:
break;
}
mutex_unlock(&hbus->state_lock);
kfree(dr);
}
/**
* hv_pci_start_relations_work() - Queue work to start device discovery
* @hbus: Root PCI bus, as understood by this driver
* @dr: The list of children returned from host
*
* Return: 0 on success, -errno on failure
*/
static int hv_pci_start_relations_work(struct hv_pcibus_device *hbus,
struct hv_dr_state *dr)
{
struct hv_dr_work *dr_wrk;
unsigned long flags;
bool pending_dr;
spin_lock_irqsave(&hbus->device_list_lock, flags);
/*
* If pending_dr is true, we have already queued a work,
* which will see the new dr. Otherwise, we need to
* queue a new work.
*/
pending_dr = !list_empty(&hbus->dr_list);
list_add_tail(&dr->list_entry, &hbus->dr_list);
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
if (pending_dr)
kfree(dr_wrk);
else
queue_work(hbus->wq, &dr_wrk->wrk);
return 0;
}
/**
* hv_pci_devices_present() - Handle list of new children
* @hbus: Root PCI bus, as understood by this driver
* @relations: Packet from host listing children
*
* Process a new list of devices on the bus. The list of devices is
* discovered by VSP and sent to us via VSP message PCI_BUS_RELATIONS,
* whenever a new list of devices for this bus appears.
*/
static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
struct pci_bus_relations *relations)
{
struct hv_dr_state *dr;
int i;
dr = kzalloc(struct_size(dr, func, relations->device_count),
GFP_NOWAIT);
if (!dr)
return;
if (hv_pci_start_relations_work(hbus, dr))
kfree(dr);
}
/**
* hv_pci_devices_present2() - Handle list of new children
* @hbus: Root PCI bus, as understood by this driver
* @relations: Packet from host listing children
*
* This function is the v2 version of hv_pci_devices_present()
*/
static void hv_pci_devices_present2(struct hv_pcibus_device *hbus,
struct pci_bus_relations2 *relations)
{
struct hv_dr_state *dr;
int i;
dr = kzalloc(struct_size(dr, func, relations->device_count),
GFP_NOWAIT);
if (!dr)
return;
if (hv_pci_start_relations_work(hbus, dr))
kfree(dr);
}
/**
* hv_eject_device_work() - Asynchronously handles ejection
* @work: Work struct embedded in internal device struct
*
* This function handles ejecting a device. Windows will
* attempt to gracefully eject a device, waiting 60 seconds to
* hear back from the guest OS that this completed successfully.
* If this timer expires, the device will be forcibly removed.
*/
static void hv_eject_device_work(struct work_struct *work)
{
struct pci_eject_response *ejct_pkt;
struct hv_pcibus_device *hbus;
struct hv_pci_dev *hpdev;
struct pci_dev *pdev;
unsigned long flags;
int wslot;
struct {
struct pci_packet pkt;
u8 buffer[sizeof(struct pci_eject_response)];
} ctxt;
/*
* Ejection can come before or after the PCI bus has been set up, so
* attempt to find it and tear down the bus state, if it exists. This
* must be done without constructs like pci_domain_nr(hbus->bridge->bus)
* because hbus->bridge->bus may not exist yet.
*/
wslot = wslot_to_devfn(hpdev->desc.win_slot.slot);
pdev = pci_get_domain_bus_and_slot(hbus->bridge->domain_nr, 0, wslot);
if (pdev) {
pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(pdev);
pci_dev_put(pdev);
pci_unlock_rescan_remove();
}
/* For the get_pcichild() in hv_pci_eject_device() */
put_pcichild(hpdev);
/* For the two refs got in new_pcichild_device() */
put_pcichild(hpdev);
put_pcichild(hpdev);
/* hpdev has been freed. Do not use it any more. */
mutex_unlock(&hbus->state_lock);
}
/**
* hv_pci_eject_device() - Handles device ejection
* @hpdev: Internal device tracking struct
*
* This function is invoked when an ejection packet arrives. It
* just schedules work so that we don't re-enter the packet
* delivery code handling the ejection.
*/
static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
{
struct hv_pcibus_device *hbus = hpdev->hbus;
struct hv_device *hdev = hbus->hdev;
/**
* hv_pci_onchannelcallback() - Handles incoming packets
* @context: Internal bus tracking struct
*
* This function is invoked whenever the host sends a packet to
* this channel (which is private to this root PCI bus).
*/
static void hv_pci_onchannelcallback(void *context)
{
const int packet_size = 0x100;
int ret;
struct hv_pcibus_device *hbus = context;
struct vmbus_channel *chan = hbus->hdev->channel;
u32 bytes_recvd;
u64 req_id, req_addr;
struct vmpacket_descriptor *desc;
unsigned char *buffer;
int bufferlen = packet_size;
struct pci_packet *comp_packet;
struct pci_response *response;
struct pci_incoming_message *new_message;
struct pci_bus_relations *bus_rel;
struct pci_bus_relations2 *bus_rel2;
struct pci_dev_inval_block *inval;
struct pci_dev_incoming *dev_message;
struct hv_pci_dev *hpdev;
unsigned long flags;
buffer = kmalloc(bufferlen, GFP_ATOMIC);
if (!buffer)
return;
while (1) {
ret = vmbus_recvpacket_raw(chan, buffer, bufferlen,
&bytes_recvd, &req_id);
if (ret == -ENOBUFS) {
kfree(buffer);
/* Handle large packet */
bufferlen = bytes_recvd;
buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
if (!buffer)
return;
continue;
}
/* Zero length indicates there are no more packets. */
if (ret || !bytes_recvd)
break;
/*
* All incoming packets must be at least as large as a
* response.
*/
if (bytes_recvd <= sizeof(struct pci_response))
continue;
desc = (struct vmpacket_descriptor *)buffer;
switch (desc->type) {
case VM_PKT_COMP:
lock_requestor(chan, flags);
req_addr = __vmbus_request_addr_match(chan, req_id,
VMBUS_RQST_ADDR_ANY);
if (req_addr == VMBUS_RQST_ERROR) {
unlock_requestor(chan, flags);
dev_err(&hbus->hdev->device,
"Invalid transaction ID %llx\n",
req_id);
break;
}
comp_packet = (struct pci_packet *)req_addr;
response = (struct pci_response *)buffer;
/*
* Call ->completion_func() within the critical section to make
* sure that the packet pointer is still valid during the call:
* here 'valid' means that there's a task still waiting for the
* completion, and that the packet data is still on the waiting
* task's stack. Cf. hv_compose_msi_msg().
*/
comp_packet->completion_func(comp_packet->compl_ctxt,
response,
bytes_recvd);
unlock_requestor(chan, flags);
break;
case VM_PKT_DATA_INBAND:
new_message = (struct pci_incoming_message *)buffer;
switch (new_message->message_type.type) {
case PCI_BUS_RELATIONS:
default:
dev_err(&hbus->hdev->device,
"unhandled packet type %d, tid %llx len %d\n",
desc->type, req_id, bytes_recvd);
break;
}
}
kfree(buffer);
}
/**
* hv_pci_protocol_negotiation() - Set up protocol
* @hdev: VMBus's tracking struct for this root PCI bus.
* @version: Array of supported channel protocol versions in
* the order of probing - highest go first.
* @num_version: Number of elements in the version array.
*
* This driver is intended to support running on Windows 10
* (server) and later versions. It will not run on earlier
* versions, as they assume that many of the operations which
* Linux needs accomplished with a spinlock held were done via
* asynchronous messaging via VMBus. Windows 10 increases the
* surface area of PCI emulation so that these actions can take
* place by suspending a virtual processor for their duration.
*
* This function negotiates the channel protocol version,
* failing if the host doesn't support the necessary protocol
* level.
*/
static int hv_pci_protocol_negotiation(struct hv_device *hdev,
enum pci_protocol_version_t version[],
int num_version)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
struct pci_version_request *version_req;
struct hv_pci_compl comp_pkt;
struct pci_packet *pkt;
int ret;
int i;
/*
* Initiate the handshake with the host and negotiate
* a version that the host can support. We start with the
* highest version number and go down if the host cannot
* support it.
*/
pkt = kzalloc(sizeof(*pkt) + sizeof(*version_req), GFP_KERNEL);
if (!pkt)
return -ENOMEM;
for (i = 0; i < num_version; i++) {
version_req->protocol_version = version[i];
ret = vmbus_sendpacket(hdev->channel, version_req,
sizeof(struct pci_version_request),
(unsigned long)pkt, VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (!ret)
ret = wait_for_response(hdev, &comp_pkt.host_event);
if (ret) {
dev_err(&hdev->device,
"PCI Pass-through VSP failed to request version: %d",
ret);
goto exit;
}
if (comp_pkt.completion_status >= 0) {
hbus->protocol_version = version[i];
dev_info(&hdev->device,
"PCI VMBus probing: Using version %#x\n",
hbus->protocol_version);
goto exit;
}
if (comp_pkt.completion_status != STATUS_REVISION_MISMATCH) {
dev_err(&hdev->device,
"PCI Pass-through VSP failed version request: %#x",
comp_pkt.completion_status);
ret = -EPROTO;
goto exit;
}
reinit_completion(&comp_pkt.host_event);
}
dev_err(&hdev->device,
"PCI pass-through VSP failed to find supported version");
ret = -EPROTO;
exit:
kfree(pkt);
return ret;
}
/**
* hv_pci_free_bridge_windows() - Release memory regions for the
* bus
* @hbus: Root PCI bus, as understood by this driver
*/
static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus)
{
/*
* Set the resources back to the way they looked when they
* were allocated by setting IORESOURCE_BUSY again.
*/
/**
* hv_pci_allocate_bridge_windows() - Allocate memory regions
* for the bus
* @hbus: Root PCI bus, as understood by this driver
*
* This function calls vmbus_allocate_mmio(), which is itself a
* bit of a compromise. Ideally, we might change the pnp layer
* in the kernel such that it comprehends either PCI devices
* which are "grandchildren of ACPI," with some intermediate bus
* node (in this case, VMBus) or change it such that it
* understands VMBus. The pnp layer, however, has been declared
* deprecated, and not subject to change.
*
* The workaround, implemented here, is to ask VMBus to allocate
* MMIO space for this bus. VMBus itself knows which ranges are
* appropriate by looking at its own ACPI objects. Then, after
* these ranges are claimed, they're modified to look like they
* would have looked if the ACPI and pnp code had allocated
* bridge windows. These descriptors have to exist in this form
* in order to satisfy the code which will get invoked when the
* endpoint PCI function driver calls request_mem_region() or
* request_mem_region_exclusive().
*
* Return: 0 on success, -errno on failure
*/
static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)
{
resource_size_t align;
int ret;
if (hbus->low_mmio_space) {
align = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space));
ret = vmbus_allocate_mmio(&hbus->low_mmio_res, hbus->hdev, 0,
(u64)(u32)0xffffffff,
hbus->low_mmio_space,
align, false);
if (ret) {
dev_err(&hbus->hdev->device,
"Need %#llx of low MMIO space. Consider reconfiguring the VM.\n",
hbus->low_mmio_space);
return ret;
}
/* Modify this resource to become a bridge window. */
hbus->low_mmio_res->flags |= IORESOURCE_WINDOW;
hbus->low_mmio_res->flags &= ~IORESOURCE_BUSY;
pci_add_resource(&hbus->bridge->windows, hbus->low_mmio_res);
}
if (hbus->high_mmio_space) {
align = 1ULL << (63 - __builtin_clzll(hbus->high_mmio_space));
ret = vmbus_allocate_mmio(&hbus->high_mmio_res, hbus->hdev, 0x100000000, -1,
hbus->high_mmio_space, align,
false);
if (ret) {
dev_err(&hbus->hdev->device,
"Need %#llx of high MMIO space. Consider reconfiguring the VM.\n",
hbus->high_mmio_space);
goto release_low_mmio;
}
/* Modify this resource to become a bridge window. */
hbus->high_mmio_res->flags |= IORESOURCE_WINDOW;
hbus->high_mmio_res->flags &= ~IORESOURCE_BUSY;
pci_add_resource(&hbus->bridge->windows, hbus->high_mmio_res);
}
return 0;
release_low_mmio:
if (hbus->low_mmio_res) {
vmbus_free_mmio(hbus->low_mmio_res->start,
resource_size(hbus->low_mmio_res));
}
return ret;
}
/**
* hv_allocate_config_window() - Find MMIO space for PCI Config
* @hbus: Root PCI bus, as understood by this driver
*
* This function claims memory-mapped I/O space for accessing
* configuration space for the functions on this bus.
*
* Return: 0 on success, -errno on failure
*/
static int hv_allocate_config_window(struct hv_pcibus_device *hbus)
{
int ret;
/*
* Set up a region of MMIO space to use for accessing configuration
* space.
*/
ret = vmbus_allocate_mmio(&hbus->mem_config, hbus->hdev, 0, -1,
PCI_CONFIG_MMIO_LENGTH, 0x1000, false);
if (ret)
return ret;
/*
* vmbus_allocate_mmio() gets used for allocating both device endpoint
* resource claims (those which cannot be overlapped) and the ranges
* which are valid for the children of this bus, which are intended
* to be overlapped by those children. Set the flag on this claim
* meaning that this region can't be overlapped.
*/
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
*
* Return: 0 on success, -errno on failure
*/
static int hv_pci_enter_d0(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(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
* of memory-mapped I/O space has been chosen for configuration space
* access.
*/
pkt = kzalloc(sizeof(*pkt) + sizeof(*d0_entry), GFP_KERNEL);
if (!pkt)
return -ENOMEM;
ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry),
(unsigned long)pkt, VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (!ret)
ret = wait_for_response(hdev, &comp_pkt.host_event);
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",
comp_pkt.completion_status);
ret = -EPROTO;
goto exit;
}
ret = 0;
exit:
kfree(pkt);
return ret;
}
/**
* hv_pci_query_relations() - Ask host to send list of child
* devices
* @hdev: VMBus's tracking struct for this root PCI bus
*
* Return: 0 on success, -errno on failure
*/
static int hv_pci_query_relations(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
struct pci_message message;
struct completion comp;
int ret;
/* Ask the host to send along the list of child devices */
init_completion(&comp);
if (cmpxchg(&hbus->survey_event, NULL, &comp))
return -ENOTEMPTY;
ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message), 0, VM_PKT_DATA_INBAND, 0);
if (!ret)
ret = wait_for_response(hdev, &comp);
/*
* In the case of fast device addition/removal, it's possible that
* vmbus_sendpacket() or wait_for_response() returns -ENODEV but we
* already got a PCI_BUS_RELATIONS* message from the host and the
* channel callback already scheduled a work to hbus->wq, which can be
* running pci_devices_present_work() -> survey_child_resources() ->
* complete(&hbus->survey_event), even after hv_pci_query_relations()
* exits and the stack variable 'comp' is no longer valid; as a result,
* a hang or a page fault may happen when the complete() calls
* raw_spin_lock_irqsave(). Flush hbus->wq before we exit from
* hv_pci_query_relations() to avoid the issues. Note: if 'ret' is
* -ENODEV, there can't be any more work item scheduled to hbus->wq
* after the flush_workqueue(): see vmbus_onoffer_rescind() ->
* vmbus_reset_channel_cb(), vmbus_rescind_cleanup() ->
* channel->rescind = true.
*/
flush_workqueue(hbus->wq);
return ret;
}
/**
* hv_send_resources_allocated() - Report local resource choices
* @hdev: VMBus's tracking struct for this root PCI bus
*
* The host OS is expecting to be sent a request as a message
* which contains all the resources that the device will use.
* The response contains those same resources, "translated"
* which is to say, the values which should be used by the
* hardware, when it delivers an interrupt. (MMIO resources are
* used in local terms.) This is nice for Windows, and lines up
* with the FDO/PDO split, which doesn't exist in Linux. Linux
* is deeply expecting to scan an emulated PCI configuration
* space. So this message is sent here only to drive the state
* machine on the host forward.
*
* Return: 0 on success, -errno on failure
*/
static int hv_send_resources_allocated(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
struct pci_resources_assigned *res_assigned;
struct pci_resources_assigned2 *res_assigned2;
struct hv_pci_compl comp_pkt;
struct hv_pci_dev *hpdev;
struct pci_packet *pkt;
size_t size_res;
int wslot;
int ret;
ret = vmbus_sendpacket(hdev->channel, pkt + 1,
size_res, (unsigned long)pkt,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (!ret)
ret = wait_for_response(hdev, &comp_pkt.host_event);
if (ret)
break;
if (comp_pkt.completion_status < 0) {
ret = -EPROTO;
dev_err(&hdev->device,
"resource allocated returned 0x%x",
comp_pkt.completion_status);
break;
}
hbus->wslot_res_allocated = wslot;
}
kfree(pkt);
return ret;
}
/**
* hv_send_resources_released() - Report local resources
* released
* @hdev: VMBus's tracking struct for this root PCI bus
*
* Return: 0 on success, -errno on failure
*/
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;
int wslot;
int ret;
for (wslot = hbus->wslot_res_allocated; wslot >= 0; wslot--) {
hpdev = get_pcichild_wslot(hbus, wslot);
if (!hpdev)
continue;
/*
* PCI domain number 0 is used by emulated devices on Gen1 VMs, so define 0
* as invalid for passthrough PCI devices of this driver.
*/
#define HVPCI_DOM_INVALID 0
/**
* hv_get_dom_num() - Get a valid PCI domain number
* Check if the PCI domain number is in use, and return another number if
* it is in use.
*
* @dom: Requested domain number
*
* return: domain number on success, HVPCI_DOM_INVALID on failure
*/
static u16 hv_get_dom_num(u16 dom)
{
unsigned int i;
if (test_and_set_bit(dom, hvpci_dom_map) == 0)
return dom;
/**
* hv_put_dom_num() - Mark the PCI domain number as free
* @dom: Domain number to be freed
*/
static void hv_put_dom_num(u16 dom)
{
clear_bit(dom, hvpci_dom_map);
}
/**
* hv_pci_probe() - New VMBus channel probe, for a root PCI bus
* @hdev: VMBus's tracking struct for this root PCI bus
* @dev_id: Identifies the device itself
*
* Return: 0 on success, -errno on failure
*/
static int hv_pci_probe(struct hv_device *hdev,
const struct hv_vmbus_device_id *dev_id)
{
struct pci_host_bridge *bridge;
struct hv_pcibus_device *hbus;
u16 dom_req, dom;
char *name;
int ret;
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
if (!bridge)
return -ENOMEM;
hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
if (!hbus)
return -ENOMEM;
/*
* The PCI bus "domain" is what is called "segment" in ACPI and other
* specs. Pull it from the instance ID, to get something usually
* unique. In rare cases of collision, we will find out another number
* not in use.
*
* Note that, since this code only runs in a Hyper-V VM, Hyper-V
* together with this guest driver can guarantee that (1) The only
* domain used by Gen1 VMs for something that looks like a physical
* PCI bus (which is actually emulated by the hypervisor) is domain 0.
* (2) There will be no overlap between domains (after fixing possible
* collisions) in the same VM.
*/
dom_req = hdev->dev_instance.b[5] << 8 | hdev->dev_instance.b[4];
dom = hv_get_dom_num(dom_req);
if (dom == HVPCI_DOM_INVALID) {
dev_err(&hdev->device,
"Unable to use dom# 0x%x or other numbers", dom_req);
ret = -EINVAL;
goto free_bus;
}
if (dom != dom_req)
dev_info(&hdev->device,
"PCI dom# 0x%x has collision, using 0x%x",
dom_req, dom);
hbus->bridge->domain_nr = dom;
#ifdef CONFIG_X86
hbus->sysdata.domain = dom;
hbus->use_calls = !!(ms_hyperv.hints & HV_X64_USE_MMIO_HYPERCALLS);
#elif defined(CONFIG_ARM64)
/*
* Set the PCI bus parent to be the corresponding VMbus
* device. Then the VMbus device will be assigned as the
* ACPI companion in pcibios_root_bridge_prepare() and
* pci_dma_configure() will propagate device coherence
* information to devices created on the bus.
*/
hbus->sysdata.parent = hdev->device.parent;
hbus->use_calls = false;
#endif
ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
hv_pci_onchannelcallback, hbus);
if (ret)
goto destroy_wq;
hv_set_drvdata(hdev, hbus);
ret = hv_pci_protocol_negotiation(hdev, pci_protocol_versions,
ARRAY_SIZE(pci_protocol_versions));
if (ret)
goto close;
ret = hv_allocate_config_window(hbus);
if (ret)
goto close;
hbus->cfg_addr = ioremap(hbus->mem_config->start,
PCI_CONFIG_MMIO_LENGTH);
if (!hbus->cfg_addr) {
dev_err(&hdev->device,
"Unable to map a virtual address for config space\n");
ret = -ENOMEM;
goto free_config;
}
name = kasprintf(GFP_KERNEL, "%pUL", &hdev->dev_instance);
if (!name) {
ret = -ENOMEM;
goto unmap;
}
hbus->fwnode = irq_domain_alloc_named_fwnode(name);
kfree(name);
if (!hbus->fwnode) {
ret = -ENOMEM;
goto unmap;
}
ret = hv_pcie_init_irq_domain(hbus);
if (ret)
goto free_fwnode;
ret = hv_pci_query_relations(hdev);
if (ret)
goto free_irq_domain;
mutex_lock(&hbus->state_lock);
ret = hv_pci_enter_d0(hdev);
if (ret)
goto release_state_lock;
ret = hv_pci_allocate_bridge_windows(hbus);
if (ret)
goto exit_d0;
ret = hv_send_resources_allocated(hdev);
if (ret)
goto free_windows;
prepopulate_bars(hbus);
hbus->state = hv_pcibus_probed;
ret = create_root_hv_pci_bus(hbus);
if (ret)
goto free_windows;
/*
* After the host sends the RESCIND_CHANNEL message, it doesn't
* access the per-channel ringbuffer any longer.
*/
if (chan->rescind)
return 0;
if (!keep_devs) {
struct list_head removed;
/* Move all present children to the list on stack */
INIT_LIST_HEAD(&removed);
spin_lock_irqsave(&hbus->device_list_lock, flags);
list_for_each_entry_safe(hpdev, tmp, &hbus->children, list_entry)
list_move_tail(&hpdev->list_entry, &removed);
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
/* Remove all children in the list */
list_for_each_entry_safe(hpdev, tmp, &removed, list_entry) {
list_del(&hpdev->list_entry);
if (hpdev->pci_slot)
pci_destroy_slot(hpdev->pci_slot);
/* For the two refs got in new_pcichild_device() */
put_pcichild(hpdev);
put_pcichild(hpdev);
}
}
ret = hv_send_resources_released(hdev);
if (ret) {
dev_err(&hdev->device,
"Couldn't send resources released packet(s)\n");
return ret;
}
ret = vmbus_sendpacket_getid(chan, msg, sizeof(*msg),
(unsigned long)&pkt.teardown_packet,
&trans_id, VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (ret)
return ret;
if (wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ) == 0) {
/*
* The completion packet on the stack becomes invalid after
* 'return'; remove the ID from the VMbus requestor if the
* identifier is still mapped to/associated with the packet.
*
* Cf. hv_pci_onchannelcallback().
*/
vmbus_request_addr_match(chan, trans_id,
(unsigned long)&pkt.teardown_packet);
return -ETIMEDOUT;
}
return 0;
}
/**
* hv_pci_remove() - Remove routine for this VMBus channel
* @hdev: VMBus's tracking struct for this root PCI bus
*/
static void hv_pci_remove(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus;
hbus = hv_get_drvdata(hdev);
if (hbus->state == hv_pcibus_installed) {
tasklet_disable(&hdev->channel->callback_event);
hbus->state = hv_pcibus_removing;
tasklet_enable(&hdev->channel->callback_event);
destroy_workqueue(hbus->wq);
hbus->wq = NULL;
/*
* At this point, no work is running or can be scheduled
* on hbus-wq. We can't race with hv_pci_devices_present()
* or hv_pci_eject_device(), it's safe to proceed.
*/
/* Remove the bus from PCI's point of view. */
pci_lock_rescan_remove();
pci_stop_root_bus(hbus->bridge->bus);
hv_pci_remove_slots(hbus);
pci_remove_root_bus(hbus->bridge->bus);
pci_unlock_rescan_remove();
}
static int hv_pci_suspend(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
enum hv_pcibus_state old_state;
int ret;
/*
* hv_pci_suspend() must make sure there are no pending work items
* before calling vmbus_close(), since it runs in a process context
* as a callback in dpm_suspend(). When it starts to run, the channel
* callback hv_pci_onchannelcallback(), which runs in a tasklet
* context, can be still running concurrently and scheduling new work
* items onto hbus->wq in hv_pci_devices_present() and
* hv_pci_eject_device(), and the work item handlers can access the
* vmbus channel, which can be being closed by hv_pci_suspend(), e.g.
* the work item handler pci_devices_present_work() ->
* new_pcichild_device() writes to the vmbus channel.
*
* To eliminate the race, hv_pci_suspend() disables the channel
* callback tasklet, sets hbus->state to hv_pcibus_removing, and
* re-enables the tasklet. This way, when hv_pci_suspend() proceeds,
* it knows that no new work item can be scheduled, and then it flushes
* hbus->wq and safely closes the vmbus channel.
*/
tasklet_disable(&hdev->channel->callback_event);
/* Change the hbus state to prevent new work items. */
old_state = hbus->state;
if (hbus->state == hv_pcibus_installed)
hbus->state = hv_pcibus_removing;
tasklet_enable(&hdev->channel->callback_event);
if (old_state != hv_pcibus_installed)
return -EINVAL;
flush_workqueue(hbus->wq);
ret = hv_pci_bus_exit(hdev, true);
if (ret)
return ret;
/*
* Upon resume, pci_restore_msi_state() -> ... -> __pci_write_msi_msg()
* directly writes the MSI/MSI-X registers via MMIO, but since Hyper-V
* doesn't trap and emulate the MMIO accesses, here hv_compose_msi_msg()
* must be used to ask Hyper-V to re-create the IOMMU Interrupt Remapping
* Table entries.
*/
static void hv_pci_restore_msi_state(struct hv_pcibus_device *hbus)
{
pci_walk_bus(hbus->bridge->bus, hv_pci_restore_msi_msg, NULL);
}
static int hv_pci_resume(struct hv_device *hdev)
{
struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
enum pci_protocol_version_t version[1];
int ret;
ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
hv_pci_onchannelcallback, hbus);
if (ret)
return ret;
/* Only use the version that was in use before hibernation. */
version[0] = hbus->protocol_version;
ret = hv_pci_protocol_negotiation(hdev, version, 1);
if (ret)
goto out;
ret = hv_pci_query_relations(hdev);
if (ret)
goto out;
mutex_lock(&hbus->state_lock);
ret = hv_pci_enter_d0(hdev);
if (ret)
goto release_state_lock;
ret = hv_send_resources_allocated(hdev);
if (ret)
goto release_state_lock;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.120Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.