Merge tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver patches from Greg KH:
 "Here's the big char/misc driver updates for 3.15-rc1.

  Lots of various things here, including the new mcb driver subsystem.

  All of these have been in linux-next for a while"

* tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (118 commits)
  extcon: Move OF helper function to extcon core and change function name
  extcon: of: Remove unnecessary function call by using the name of device_node
  extcon: gpio: Use SIMPLE_DEV_PM_OPS macro
  extcon: palmas: Use SIMPLE_DEV_PM_OPS macro
  mei: don't use deprecated DEFINE_PCI_DEVICE_TABLE macro
  mei: amthif: fix checkpatch error
  mei: client.h fix checkpatch errors
  mei: use cl_dbg where appropriate
  mei: fix Unnecessary space after function pointer name
  mei: report consistently copy_from/to_user failures
  mei: drop pr_fmt macros
  mei: make me hw headers private to me hw.
  mei: fix memory leak of pending write cb objects
  mei: me: do not reset when less than expected data is received
  drivers: mcb: Fix build error discovered by 0-day bot
  cs5535-mfgpt: Simplify dependencies
  spmi: pm: drop bus-level PM suspend/resume routines
  spmi: pmic_arb: make selectable on ARCH_QCOM
  Drivers: hv: vmbus: Increase the limit on the number of pfns we can handle
  pch_phub: Report error writing MAC back to user
  ...
This commit is contained in:
Linus Torvalds
2014-04-01 16:13:21 -07:00
167 changed files with 8869 additions and 1726 deletions

View File

@@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
channel_mgmt.o ring_buffer.o
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o

View File

@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/hyperv.h>
#include <linux/uio.h>
#include "hyperv_vmbus.h"
@@ -554,14 +555,14 @@ EXPORT_SYMBOL_GPL(vmbus_close);
*
* Mainly used by Hyper-V drivers.
*/
int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u64 requestid,
enum vmbus_packet_type type, u32 flags)
{
struct vmpacket_descriptor desc;
u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
struct scatterlist bufferlist[3];
struct kvec bufferlist[3];
u64 aligned_data = 0;
int ret;
bool signal = false;
@@ -575,11 +576,12 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
desc.len8 = (u16)(packetlen_aligned >> 3);
desc.trans_id = requestid;
sg_init_table(bufferlist, 3);
sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor));
sg_set_buf(&bufferlist[1], buffer, bufferlen);
sg_set_buf(&bufferlist[2], &aligned_data,
packetlen_aligned - packetlen);
bufferlist[0].iov_base = &desc;
bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor);
bufferlist[1].iov_base = buffer;
bufferlist[1].iov_len = bufferlen;
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
@@ -605,7 +607,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
u32 descsize;
u32 packetlen;
u32 packetlen_aligned;
struct scatterlist bufferlist[3];
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool signal = false;
@@ -637,11 +639,12 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
desc.range[i].pfn = pagebuffers[i].pfn;
}
sg_init_table(bufferlist, 3);
sg_set_buf(&bufferlist[0], &desc, descsize);
sg_set_buf(&bufferlist[1], buffer, bufferlen);
sg_set_buf(&bufferlist[2], &aligned_data,
packetlen_aligned - packetlen);
bufferlist[0].iov_base = &desc;
bufferlist[0].iov_len = descsize;
bufferlist[1].iov_base = buffer;
bufferlist[1].iov_len = bufferlen;
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
@@ -665,7 +668,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
u32 descsize;
u32 packetlen;
u32 packetlen_aligned;
struct scatterlist bufferlist[3];
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool signal = false;
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
@@ -700,11 +703,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array,
pfncount * sizeof(u64));
sg_init_table(bufferlist, 3);
sg_set_buf(&bufferlist[0], &desc, descsize);
sg_set_buf(&bufferlist[1], buffer, bufferlen);
sg_set_buf(&bufferlist[2], &aligned_data,
packetlen_aligned - packetlen);
bufferlist[0].iov_base = &desc;
bufferlist[0].iov_len = descsize;
bufferlist[1].iov_base = buffer;
bufferlist[1].iov_len = bufferlen;
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);

View File

@@ -1171,7 +1171,8 @@ static int dm_thread_func(void *dm_dev)
int t;
while (!kthread_should_stop()) {
t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ);
t = wait_for_completion_interruptible_timeout(
&dm_device.config_event, 1*HZ);
/*
* The host expects us to post information on the memory
* pressure every second.

414
drivers/hv/hv_fcopy.c Normal file
View File

@@ -0,0 +1,414 @@
/*
* An implementation of file copy service.
*
* Copyright (C) 2014, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/semaphore.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/workqueue.h>
#include <linux/cdev.h>
#include <linux/hyperv.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include "hyperv_vmbus.h"
#define WIN8_SRV_MAJOR 1
#define WIN8_SRV_MINOR 1
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
/*
* Global state maintained for transaction that is being processed.
* For a class of integration services, including the "file copy service",
* the specified protocol is a "request/response" protocol which means that
* there can only be single outstanding transaction from the host at any
* given point in time. We use this to simplify memory management in this
* driver - we cache and process only one message at a time.
*
* While the request/response protocol is guaranteed by the host, we further
* ensure this by serializing packet processing in this driver - we do not
* read additional packets from the VMBUs until the current packet is fully
* handled.
*
* The transaction "active" state is set when we receive a request from the
* host and we cleanup this state when the transaction is completed - when we
* respond to the host with our response. When the transaction active state is
* set, we defer handling incoming packets.
*/
static struct {
bool active; /* transaction status - active or not */
int recv_len; /* number of bytes received. */
struct hv_fcopy_hdr *fcopy_msg; /* current message */
struct hv_start_fcopy message; /* sent to daemon */
struct vmbus_channel *recv_channel; /* chn we got the request */
u64 recv_req_id; /* request ID. */
void *fcopy_context; /* for the channel callback */
struct semaphore read_sema;
} fcopy_transaction;
static bool opened; /* currently device opened */
/*
* Before we can accept copy messages from the host, we need
* to handshake with the user level daemon. This state tracks
* if we are in the handshake phase.
*/
static bool in_hand_shake = true;
static void fcopy_send_data(void);
static void fcopy_respond_to_host(int error);
static void fcopy_work_func(struct work_struct *dummy);
static DECLARE_DELAYED_WORK(fcopy_work, fcopy_work_func);
static u8 *recv_buffer;
static void fcopy_work_func(struct work_struct *dummy)
{
/*
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
fcopy_respond_to_host(HV_E_FAIL);
}
static int fcopy_handle_handshake(u32 version)
{
switch (version) {
case FCOPY_CURRENT_VERSION:
break;
default:
/*
* For now we will fail the registration.
* If and when we have multiple versions to
* deal with, we will be backward compatible.
* We will add this code when needed.
*/
return -EINVAL;
}
pr_info("FCP: user-mode registering done. Daemon version: %d\n",
version);
fcopy_transaction.active = false;
if (fcopy_transaction.fcopy_context)
hv_fcopy_onchannelcallback(fcopy_transaction.fcopy_context);
in_hand_shake = false;
return 0;
}
static void fcopy_send_data(void)
{
struct hv_start_fcopy *smsg_out = &fcopy_transaction.message;
int operation = fcopy_transaction.fcopy_msg->operation;
struct hv_start_fcopy *smsg_in;
/*
* The strings sent from the host are encoded in
* in utf16; convert it to utf8 strings.
* The host assures us that the utf16 strings will not exceed
* the max lengths specified. We will however, reserve room
* for the string terminating character - in the utf16s_utf8s()
* function we limit the size of the buffer where the converted
* string is placed to W_MAX_PATH -1 to guarantee
* that the strings can be properly terminated!
*/
switch (operation) {
case START_FILE_COPY:
memset(smsg_out, 0, sizeof(struct hv_start_fcopy));
smsg_out->hdr.operation = operation;
smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
UTF16_LITTLE_ENDIAN,
(__u8 *)smsg_out->file_name, W_MAX_PATH - 1);
utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
UTF16_LITTLE_ENDIAN,
(__u8 *)smsg_out->path_name, W_MAX_PATH - 1);
smsg_out->copy_flags = smsg_in->copy_flags;
smsg_out->file_size = smsg_in->file_size;
break;
default:
break;
}
up(&fcopy_transaction.read_sema);
return;
}
/*
* Send a response back to the host.
*/
static void
fcopy_respond_to_host(int error)
{
struct icmsg_hdr *icmsghdr;
u32 buf_len;
struct vmbus_channel *channel;
u64 req_id;
/*
* Copy the global state for completing the transaction. Note that
* only one transaction can be active at a time. This is guaranteed
* by the file copy protocol implemented by the host. Furthermore,
* the "transaction active" state we maintain ensures that there can
* only be one active transaction at a time.
*/
buf_len = fcopy_transaction.recv_len;
channel = fcopy_transaction.recv_channel;
req_id = fcopy_transaction.recv_req_id;
fcopy_transaction.active = false;
icmsghdr = (struct icmsg_hdr *)
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
if (channel->onchannel_callback == NULL)
/*
* We have raced with util driver being unloaded;
* silently return.
*/
return;
icmsghdr->status = error;
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
VM_PKT_DATA_INBAND, 0);
}
void hv_fcopy_onchannelcallback(void *context)
{
struct vmbus_channel *channel = context;
u32 recvlen;
u64 requestid;
struct hv_fcopy_hdr *fcopy_msg;
struct icmsg_hdr *icmsghdr;
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int fcopy_srv_version;
if (fcopy_transaction.active) {
/*
* We will defer processing this callback once
* the current transaction is complete.
*/
fcopy_transaction.fcopy_context = context;
return;
}
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
&requestid);
if (recvlen <= 0)
return;
icmsghdr = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)];
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
util_fw_version = UTIL_FW_VERSION;
fcopy_srv_version = WIN8_SRV_VERSION;
vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer,
util_fw_version, fcopy_srv_version);
} else {
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
/*
* Stash away this global state for completing the
* transaction; note transactions are serialized.
*/
fcopy_transaction.active = true;
fcopy_transaction.recv_len = recvlen;
fcopy_transaction.recv_channel = channel;
fcopy_transaction.recv_req_id = requestid;
fcopy_transaction.fcopy_msg = fcopy_msg;
/*
* Send the information to the user-level daemon.
*/
fcopy_send_data();
schedule_delayed_work(&fcopy_work, 5*HZ);
return;
}
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
}
/*
* Create a char device that can support read/write for passing
* the payload.
*/
static ssize_t fcopy_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
void *src;
size_t copy_size;
int operation;
/*
* Wait until there is something to be read.
*/
if (down_interruptible(&fcopy_transaction.read_sema))
return -EINTR;
/*
* The channel may be rescinded and in this case, we will wakeup the
* the thread blocked on the semaphore and we will use the opened
* state to correctly handle this case.
*/
if (!opened)
return -ENODEV;
operation = fcopy_transaction.fcopy_msg->operation;
if (operation == START_FILE_COPY) {
src = &fcopy_transaction.message;
copy_size = sizeof(struct hv_start_fcopy);
if (count < copy_size)
return 0;
} else {
src = fcopy_transaction.fcopy_msg;
copy_size = sizeof(struct hv_do_fcopy);
if (count < copy_size)
return 0;
}
if (copy_to_user(buf, src, copy_size))
return -EFAULT;
return copy_size;
}
static ssize_t fcopy_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int response = 0;
if (count != sizeof(int))
return -EINVAL;
if (copy_from_user(&response, buf, sizeof(int)))
return -EFAULT;
if (in_hand_shake) {
if (fcopy_handle_handshake(response))
return -EINVAL;
return sizeof(int);
}
/*
* Complete the transaction by forwarding the result
* to the host. But first, cancel the timeout.
*/
if (cancel_delayed_work_sync(&fcopy_work))
fcopy_respond_to_host(response);
return sizeof(int);
}
static int fcopy_open(struct inode *inode, struct file *f)
{
/*
* The user level daemon that will open this device is
* really an extension of this driver. We can have only
* active open at a time.
*/
if (opened)
return -EBUSY;
/*
* The daemon is alive; setup the state.
*/
opened = true;
return 0;
}
static int fcopy_release(struct inode *inode, struct file *f)
{
/*
* The daemon has exited; reset the state.
*/
in_hand_shake = true;
opened = false;
return 0;
}
static const struct file_operations fcopy_fops = {
.read = fcopy_read,
.write = fcopy_write,
.release = fcopy_release,
.open = fcopy_open,
};
static struct miscdevice fcopy_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "vmbus/hv_fcopy",
.fops = &fcopy_fops,
};
static int fcopy_dev_init(void)
{
return misc_register(&fcopy_misc);
}
static void fcopy_dev_deinit(void)
{
/*
* The device is going away - perhaps because the
* host has rescinded the channel. Setup state so that
* user level daemon can gracefully exit if it is blocked
* on the read semaphore.
*/
opened = false;
/*
* Signal the semaphore as the device is
* going away.
*/
up(&fcopy_transaction.read_sema);
misc_deregister(&fcopy_misc);
}
int hv_fcopy_init(struct hv_util_service *srv)
{
recv_buffer = srv->recv_buffer;
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
* Defer processing channel callbacks until the daemon
* has registered.
*/
fcopy_transaction.active = true;
sema_init(&fcopy_transaction.read_sema, 0);
return fcopy_dev_init();
}
void hv_fcopy_deinit(void)
{
cancel_delayed_work_sync(&fcopy_work);
fcopy_dev_deinit();
}

View File

@@ -113,7 +113,7 @@ kvp_register(int reg_value)
kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION);
msg->len = sizeof(struct hv_kvp_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
kfree(msg);
}
}
@@ -435,7 +435,7 @@ kvp_send_key(struct work_struct *dummy)
}
msg->len = sizeof(struct hv_kvp_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
kfree(msg);
return;

View File

@@ -98,7 +98,7 @@ static void vss_send_op(struct work_struct *dummy)
vss_msg->vss_hdr.operation = op;
msg->len = sizeof(struct hv_vss_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
kfree(msg);
return;

View File

@@ -28,6 +28,7 @@
#include <linux/reboot.h>
#include <linux/hyperv.h>
#include "hyperv_vmbus.h"
#define SD_MAJOR 3
#define SD_MINOR 0
@@ -82,6 +83,12 @@ static struct hv_util_service util_vss = {
.util_deinit = hv_vss_deinit,
};
static struct hv_util_service util_fcopy = {
.util_cb = hv_fcopy_onchannelcallback,
.util_init = hv_fcopy_init,
.util_deinit = hv_fcopy_deinit,
};
static void perform_shutdown(struct work_struct *dummy)
{
orderly_poweroff(true);
@@ -401,6 +408,10 @@ static const struct hv_vmbus_device_id id_table[] = {
{ HV_VSS_GUID,
.driver_data = (unsigned long)&util_vss
},
/* File copy GUID */
{ HV_FCOPY_GUID,
.driver_data = (unsigned long)&util_fcopy
},
{ },
};

View File

@@ -559,8 +559,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer,
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
struct scatterlist *sglist,
u32 sgcount, bool *signal);
struct kvec *kv_list,
u32 kv_count, bool *signal);
int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
u32 buflen);
@@ -669,5 +669,9 @@ int vmbus_set_event(struct vmbus_channel *channel);
void vmbus_on_event(unsigned long data);
int hv_fcopy_init(struct hv_util_service *);
void hv_fcopy_deinit(void);
void hv_fcopy_onchannelcallback(void *);
#endif /* _HYPERV_VMBUS_H */

View File

@@ -26,6 +26,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/hyperv.h>
#include <linux/uio.h>
#include "hyperv_vmbus.h"
@@ -387,23 +388,20 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
*
*/
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
struct scatterlist *sglist, u32 sgcount, bool *signal)
struct kvec *kv_list, u32 kv_count, bool *signal)
{
int i = 0;
u32 bytes_avail_towrite;
u32 bytes_avail_toread;
u32 totalbytes_towrite = 0;
struct scatterlist *sg;
u32 next_write_location;
u32 old_write;
u64 prev_indices = 0;
unsigned long flags;
for_each_sg(sglist, sg, sgcount, i)
{
totalbytes_towrite += sg->length;
}
for (i = 0; i < kv_count; i++)
totalbytes_towrite += kv_list[i].iov_len;
totalbytes_towrite += sizeof(u64);
@@ -427,12 +425,11 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
old_write = next_write_location;
for_each_sg(sglist, sg, sgcount, i)
{
for (i = 0; i < kv_count; i++) {
next_write_location = hv_copyto_ringbuffer(outring_info,
next_write_location,
sg_virt(sg),
sg->length);
kv_list[i].iov_base,
kv_list[i].iov_len);
}
/* Set previous packet start */

View File

@@ -43,6 +43,12 @@ static struct tasklet_struct msg_dpc;
static struct completion probe_event;
static int irq;
struct resource hyperv_mmio = {
.name = "hyperv mmio",
.flags = IORESOURCE_MEM,
};
EXPORT_SYMBOL_GPL(hyperv_mmio);
static int vmbus_exists(void)
{
if (hv_acpi_dev == NULL)
@@ -843,18 +849,21 @@ void vmbus_device_unregister(struct hv_device *device_obj)
/*
* VMBUS is an acpi enumerated device. Get the the IRQ information
* from DSDT.
* VMBUS is an acpi enumerated device. Get the the information we
* need from DSDT.
*/
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
{
switch (res->type) {
case ACPI_RESOURCE_TYPE_IRQ:
irq = res->data.irq.interrupts[0];
break;
if (res->type == ACPI_RESOURCE_TYPE_IRQ) {
struct acpi_resource_irq *irqp;
irqp = &res->data.irq;
*((unsigned int *)irq) = irqp->interrupts[0];
case ACPI_RESOURCE_TYPE_ADDRESS64:
hyperv_mmio.start = res->data.address64.minimum;
hyperv_mmio.end = res->data.address64.maximum;
break;
}
return AE_OK;
@@ -863,18 +872,34 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
static int vmbus_acpi_add(struct acpi_device *device)
{
acpi_status result;
int ret_val = -ENODEV;
hv_acpi_dev = device;
result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
vmbus_walk_resources, &irq);
vmbus_walk_resources, NULL);
if (ACPI_FAILURE(result)) {
complete(&probe_event);
return -ENODEV;
if (ACPI_FAILURE(result))
goto acpi_walk_err;
/*
* The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that
* has the mmio ranges. Get that.
*/
if (device->parent) {
result = acpi_walk_resources(device->parent->handle,
METHOD_NAME__CRS,
vmbus_walk_resources, NULL);
if (ACPI_FAILURE(result))
goto acpi_walk_err;
if (hyperv_mmio.start && hyperv_mmio.end)
request_resource(&iomem_resource, &hyperv_mmio);
}
ret_val = 0;
acpi_walk_err:
complete(&probe_event);
return 0;
return ret_val;
}
static const struct acpi_device_id vmbus_acpi_device_ids[] = {