From 17be18c2b5f5c8acd75e4edf1211abf2cfd60fa5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 25 Aug 2011 09:48:29 -0700 Subject: Staging: hv: Add struct hv_vmbus_device_id to mod_devicetable.h In preparation for implementing vmbus aliases for auto-loading Hyper-V drivers, define vmbus specific device ID. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/mod_devicetable.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index ae28e93fd072..5a123774c2e6 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -405,6 +405,13 @@ struct virtio_device_id { }; #define VIRTIO_DEV_ANY_ID 0xffffffff +/* + * For Hyper-V devices we use the device guid as the id. + */ +struct hv_vmbus_device_id { + __u8 guid[16]; +}; + /* i2c */ #define I2C_NAME_SIZE 20 -- cgit v1.2.3 From a91befc116f424883602fb7b57b63b5fcfe18719 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 25 Aug 2011 10:25:37 -0700 Subject: Staging: hv: add driver_data to hv_vmbus_device_id This is going to be needed by some drivers that handle more than one device, like all other bus types do, so prepare for that in advance before the user/kernel api is used. Cc: K. Y. Srinivasan Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/mod_devicetable.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5a123774c2e6..468819cdde87 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -410,6 +410,8 @@ struct virtio_device_id { */ struct hv_vmbus_device_id { __u8 guid[16]; + kernel_ulong_t driver_data /* Data private to the driver */ + __attribute__((aligned(sizeof(kernel_ulong_t)))); }; /* i2c */ -- cgit v1.2.3 From 46a971913611a23478283931460a95be962ce329 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 4 Oct 2011 12:29:52 -0700 Subject: Staging: hv: move hyperv code out of staging directory After many years wandering the desert, it is finally time for the Microsoft HyperV code to move out of the staging directory. Or at least the core hyperv bus code, and the utility driver, the rest still have some review to get through by the various subsystem maintainers. Signed-off-by: Greg Kroah-Hartman Signed-off-by: K. Y. Srinivasan --- drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/hv/Kconfig | 14 + drivers/hv/Makefile | 7 + drivers/hv/channel.c | 815 ++++++++++++++++++++++++++ drivers/hv/channel_mgmt.c | 647 +++++++++++++++++++++ drivers/hv/connection.c | 318 ++++++++++ drivers/hv/hv.c | 429 ++++++++++++++ drivers/hv/hv_kvp.c | 339 +++++++++++ drivers/hv/hv_kvp.h | 184 ++++++ drivers/hv/hv_util.c | 354 +++++++++++ drivers/hv/hyperv_vmbus.h | 628 ++++++++++++++++++++ drivers/hv/ring_buffer.c | 527 +++++++++++++++++ drivers/hv/vmbus_drv.c | 772 ++++++++++++++++++++++++ drivers/staging/hv/Kconfig | 28 +- drivers/staging/hv/Makefile | 7 +- drivers/staging/hv/channel.c | 815 -------------------------- drivers/staging/hv/channel_mgmt.c | 647 --------------------- drivers/staging/hv/connection.c | 318 ---------- drivers/staging/hv/hv.c | 429 -------------- drivers/staging/hv/hv_kvp.c | 339 ----------- drivers/staging/hv/hv_kvp.h | 184 ------ drivers/staging/hv/hv_mouse.c | 3 +- drivers/staging/hv/hv_util.c | 354 ----------- drivers/staging/hv/hyperv.h | 969 ------------------------------- drivers/staging/hv/hyperv_net.h | 2 +- drivers/staging/hv/hyperv_vmbus.h | 629 -------------------- drivers/staging/hv/ring_buffer.c | 527 ----------------- drivers/staging/hv/storvsc_drv.c | 2 +- drivers/staging/hv/tools/hv_kvp_daemon.c | 500 ---------------- drivers/staging/hv/vmbus_drv.c | 772 ------------------------ include/linux/hyperv.h | 969 +++++++++++++++++++++++++++++++ tools/hv/hv_kvp_daemon.c | 500 ++++++++++++++++ 33 files changed, 6514 insertions(+), 6518 deletions(-) create mode 100644 drivers/hv/Kconfig create mode 100644 drivers/hv/Makefile create mode 100644 drivers/hv/channel.c create mode 100644 drivers/hv/channel_mgmt.c create mode 100644 drivers/hv/connection.c create mode 100644 drivers/hv/hv.c create mode 100644 drivers/hv/hv_kvp.c create mode 100644 drivers/hv/hv_kvp.h create mode 100644 drivers/hv/hv_util.c create mode 100644 drivers/hv/hyperv_vmbus.h create mode 100644 drivers/hv/ring_buffer.c create mode 100644 drivers/hv/vmbus_drv.c delete mode 100644 drivers/staging/hv/channel.c delete mode 100644 drivers/staging/hv/channel_mgmt.c delete mode 100644 drivers/staging/hv/connection.c delete mode 100644 drivers/staging/hv/hv.c delete mode 100644 drivers/staging/hv/hv_kvp.c delete mode 100644 drivers/staging/hv/hv_kvp.h delete mode 100644 drivers/staging/hv/hv_util.c delete mode 100644 drivers/staging/hv/hyperv.h delete mode 100644 drivers/staging/hv/hyperv_vmbus.h delete mode 100644 drivers/staging/hv/ring_buffer.c delete mode 100644 drivers/staging/hv/tools/hv_kvp_daemon.c delete mode 100644 drivers/staging/hv/vmbus_drv.c create mode 100644 include/linux/hyperv.h create mode 100644 tools/hv/hv_kvp_daemon.c (limited to 'include') diff --git a/drivers/Kconfig b/drivers/Kconfig index 95b9e7eefadc..ce3c35f4041c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -130,4 +130,6 @@ source "drivers/iommu/Kconfig" source "drivers/virt/Kconfig" +source "drivers/hv/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 7fa433a7030c..ef693cfb4813 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -127,3 +127,5 @@ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ +obj-$(CONFIG_HYPERV) += hv/ + diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig new file mode 100644 index 000000000000..9fa09ac000ad --- /dev/null +++ b/drivers/hv/Kconfig @@ -0,0 +1,14 @@ +config HYPERV + tristate "Microsoft Hyper-V client drivers" + depends on X86 && ACPI && PCI + help + Select this option to run Linux as a Hyper-V client operating + system. + +config HYPERV_UTILS + tristate "Microsoft Hyper-V Utilities driver" + depends on HYPERV && CONNECTOR && NLS + help + Select this option to enable the Hyper-V Utilities. + + diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile new file mode 100644 index 000000000000..a23938b991c9 --- /dev/null +++ b/drivers/hv/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_HYPERV) += hv_vmbus.o +obj-$(CONFIG_HYPERV_UTILS) += hv_utils.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 diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c new file mode 100644 index 000000000000..406537420fff --- /dev/null +++ b/drivers/hv/channel.c @@ -0,0 +1,815 @@ +/* + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "hyperv_vmbus.h" + +#define NUM_PAGES_SPANNED(addr, len) \ +((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) + +/* Internal routines */ +static int create_gpadl_header( + void *kbuffer, /* must be phys and virt contiguous */ + u32 size, /* page-size multiple */ + struct vmbus_channel_msginfo **msginfo, + u32 *messagecount); +static void vmbus_setevent(struct vmbus_channel *channel); + +/* + * vmbus_setevent- Trigger an event notification on the specified + * channel. + */ +static void vmbus_setevent(struct vmbus_channel *channel) +{ + struct hv_monitor_page *monitorpage; + + if (channel->offermsg.monitor_allocated) { + /* Each u32 represents 32 channels */ + sync_set_bit(channel->offermsg.child_relid & 31, + (unsigned long *) vmbus_connection.send_int_page + + (channel->offermsg.child_relid >> 5)); + + monitorpage = vmbus_connection.monitor_pages; + monitorpage++; /* Get the child to parent monitor page */ + + sync_set_bit(channel->monitor_bit, + (unsigned long *)&monitorpage->trigger_group + [channel->monitor_grp].pending); + + } else { + vmbus_set_event(channel->offermsg.child_relid); + } +} + +/* + * vmbus_get_debug_info -Retrieve various channel debug info + */ +void vmbus_get_debug_info(struct vmbus_channel *channel, + struct vmbus_channel_debug_info *debuginfo) +{ + struct hv_monitor_page *monitorpage; + u8 monitor_group = (u8)channel->offermsg.monitorid / 32; + u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; + + debuginfo->relid = channel->offermsg.child_relid; + debuginfo->state = channel->state; + memcpy(&debuginfo->interfacetype, + &channel->offermsg.offer.if_type, sizeof(uuid_le)); + memcpy(&debuginfo->interface_instance, + &channel->offermsg.offer.if_instance, + sizeof(uuid_le)); + + monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; + + debuginfo->monitorid = channel->offermsg.monitorid; + + debuginfo->servermonitor_pending = + monitorpage->trigger_group[monitor_group].pending; + debuginfo->servermonitor_latency = + monitorpage->latency[monitor_group][monitor_offset]; + debuginfo->servermonitor_connectionid = + monitorpage->parameter[monitor_group] + [monitor_offset].connectionid.u.id; + + monitorpage++; + + debuginfo->clientmonitor_pending = + monitorpage->trigger_group[monitor_group].pending; + debuginfo->clientmonitor_latency = + monitorpage->latency[monitor_group][monitor_offset]; + debuginfo->clientmonitor_connectionid = + monitorpage->parameter[monitor_group] + [monitor_offset].connectionid.u.id; + + hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); + hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); +} + +/* + * vmbus_open - Open the specified channel. + */ +int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, + u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, + void (*onchannelcallback)(void *context), void *context) +{ + struct vmbus_channel_open_channel *open_msg; + struct vmbus_channel_msginfo *open_info = NULL; + void *in, *out; + unsigned long flags; + int ret, t, err = 0; + + newchannel->onchannel_callback = onchannelcallback; + newchannel->channel_callback_context = context; + + /* Allocate the ring buffer */ + out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, + get_order(send_ringbuffer_size + recv_ringbuffer_size)); + + if (!out) + return -ENOMEM; + + + in = (void *)((unsigned long)out + send_ringbuffer_size); + + newchannel->ringbuffer_pages = out; + newchannel->ringbuffer_pagecount = (send_ringbuffer_size + + recv_ringbuffer_size) >> PAGE_SHIFT; + + ret = hv_ringbuffer_init( + &newchannel->outbound, out, send_ringbuffer_size); + + if (ret != 0) { + err = ret; + goto errorout; + } + + ret = hv_ringbuffer_init( + &newchannel->inbound, in, recv_ringbuffer_size); + if (ret != 0) { + err = ret; + goto errorout; + } + + + /* Establish the gpadl for the ring buffer */ + newchannel->ringbuffer_gpadlhandle = 0; + + ret = vmbus_establish_gpadl(newchannel, + newchannel->outbound.ring_buffer, + send_ringbuffer_size + + recv_ringbuffer_size, + &newchannel->ringbuffer_gpadlhandle); + + if (ret != 0) { + err = ret; + goto errorout; + } + + /* Create and init the channel open message */ + open_info = kmalloc(sizeof(*open_info) + + sizeof(struct vmbus_channel_open_channel), + GFP_KERNEL); + if (!open_info) { + err = -ENOMEM; + goto errorout; + } + + init_completion(&open_info->waitevent); + + open_msg = (struct vmbus_channel_open_channel *)open_info->msg; + open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; + open_msg->openid = newchannel->offermsg.child_relid; + open_msg->child_relid = newchannel->offermsg.child_relid; + open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; + open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> + PAGE_SHIFT; + open_msg->server_contextarea_gpadlhandle = 0; + + if (userdatalen > MAX_USER_DEFINED_BYTES) { + err = -EINVAL; + goto errorout; + } + + if (userdatalen) + memcpy(open_msg->userdata, userdata, userdatalen); + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&open_info->msglistentry, + &vmbus_connection.chn_msg_list); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(open_msg, + sizeof(struct vmbus_channel_open_channel)); + + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); + if (t == 0) { + err = -ETIMEDOUT; + goto errorout; + } + + + if (open_info->response.open_result.status) + err = open_info->response.open_result.status; + +cleanup: + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&open_info->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(open_info); + return err; + +errorout: + hv_ringbuffer_cleanup(&newchannel->outbound); + hv_ringbuffer_cleanup(&newchannel->inbound); + free_pages((unsigned long)out, + get_order(send_ringbuffer_size + recv_ringbuffer_size)); + kfree(open_info); + return err; +} +EXPORT_SYMBOL_GPL(vmbus_open); + +/* + * create_gpadl_header - Creates a gpadl for the specified buffer + */ +static int create_gpadl_header(void *kbuffer, u32 size, + struct vmbus_channel_msginfo **msginfo, + u32 *messagecount) +{ + int i; + int pagecount; + unsigned long long pfn; + struct vmbus_channel_gpadl_header *gpadl_header; + struct vmbus_channel_gpadl_body *gpadl_body; + struct vmbus_channel_msginfo *msgheader; + struct vmbus_channel_msginfo *msgbody = NULL; + u32 msgsize; + + int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; + + pagecount = size >> PAGE_SHIFT; + pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; + + /* do we need a gpadl body msg */ + pfnsize = MAX_SIZE_CHANNEL_MESSAGE - + sizeof(struct vmbus_channel_gpadl_header) - + sizeof(struct gpa_range); + pfncount = pfnsize / sizeof(u64); + + if (pagecount > pfncount) { + /* we need a gpadl body */ + /* fill in the header */ + msgsize = sizeof(struct vmbus_channel_msginfo) + + sizeof(struct vmbus_channel_gpadl_header) + + sizeof(struct gpa_range) + pfncount * sizeof(u64); + msgheader = kzalloc(msgsize, GFP_KERNEL); + if (!msgheader) + goto nomem; + + INIT_LIST_HEAD(&msgheader->submsglist); + msgheader->msgsize = msgsize; + + gpadl_header = (struct vmbus_channel_gpadl_header *) + msgheader->msg; + gpadl_header->rangecount = 1; + gpadl_header->range_buflen = sizeof(struct gpa_range) + + pagecount * sizeof(u64); + gpadl_header->range[0].byte_offset = 0; + gpadl_header->range[0].byte_count = size; + for (i = 0; i < pfncount; i++) + gpadl_header->range[0].pfn_array[i] = pfn+i; + *msginfo = msgheader; + *messagecount = 1; + + pfnsum = pfncount; + pfnleft = pagecount - pfncount; + + /* how many pfns can we fit */ + pfnsize = MAX_SIZE_CHANNEL_MESSAGE - + sizeof(struct vmbus_channel_gpadl_body); + pfncount = pfnsize / sizeof(u64); + + /* fill in the body */ + while (pfnleft) { + if (pfnleft > pfncount) + pfncurr = pfncount; + else + pfncurr = pfnleft; + + msgsize = sizeof(struct vmbus_channel_msginfo) + + sizeof(struct vmbus_channel_gpadl_body) + + pfncurr * sizeof(u64); + msgbody = kzalloc(msgsize, GFP_KERNEL); + + if (!msgbody) { + struct vmbus_channel_msginfo *pos = NULL; + struct vmbus_channel_msginfo *tmp = NULL; + /* + * Free up all the allocated messages. + */ + list_for_each_entry_safe(pos, tmp, + &msgheader->submsglist, + msglistentry) { + + list_del(&pos->msglistentry); + kfree(pos); + } + + goto nomem; + } + + msgbody->msgsize = msgsize; + (*messagecount)++; + gpadl_body = + (struct vmbus_channel_gpadl_body *)msgbody->msg; + + /* + * Gpadl is u32 and we are using a pointer which could + * be 64-bit + * This is governed by the guest/host protocol and + * so the hypervisor gurantees that this is ok. + */ + for (i = 0; i < pfncurr; i++) + gpadl_body->pfn[i] = pfn + pfnsum + i; + + /* add to msg header */ + list_add_tail(&msgbody->msglistentry, + &msgheader->submsglist); + pfnsum += pfncurr; + pfnleft -= pfncurr; + } + } else { + /* everything fits in a header */ + msgsize = sizeof(struct vmbus_channel_msginfo) + + sizeof(struct vmbus_channel_gpadl_header) + + sizeof(struct gpa_range) + pagecount * sizeof(u64); + msgheader = kzalloc(msgsize, GFP_KERNEL); + if (msgheader == NULL) + goto nomem; + msgheader->msgsize = msgsize; + + gpadl_header = (struct vmbus_channel_gpadl_header *) + msgheader->msg; + gpadl_header->rangecount = 1; + gpadl_header->range_buflen = sizeof(struct gpa_range) + + pagecount * sizeof(u64); + gpadl_header->range[0].byte_offset = 0; + gpadl_header->range[0].byte_count = size; + for (i = 0; i < pagecount; i++) + gpadl_header->range[0].pfn_array[i] = pfn+i; + + *msginfo = msgheader; + *messagecount = 1; + } + + return 0; +nomem: + kfree(msgheader); + kfree(msgbody); + return -ENOMEM; +} + +/* + * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer + * + * @channel: a channel + * @kbuffer: from kmalloc + * @size: page-size multiple + * @gpadl_handle: some funky thing + */ +int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, + u32 size, u32 *gpadl_handle) +{ + struct vmbus_channel_gpadl_header *gpadlmsg; + struct vmbus_channel_gpadl_body *gpadl_body; + struct vmbus_channel_msginfo *msginfo = NULL; + struct vmbus_channel_msginfo *submsginfo; + u32 msgcount; + struct list_head *curr; + u32 next_gpadl_handle; + unsigned long flags; + int ret = 0; + int t; + + next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); + atomic_inc(&vmbus_connection.next_gpadl_handle); + + ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); + if (ret) + return ret; + + init_completion(&msginfo->waitevent); + + gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; + gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; + gpadlmsg->child_relid = channel->offermsg.child_relid; + gpadlmsg->gpadl = next_gpadl_handle; + + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&msginfo->msglistentry, + &vmbus_connection.chn_msg_list); + + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - + sizeof(*msginfo)); + if (ret != 0) + goto cleanup; + + if (msgcount > 1) { + list_for_each(curr, &msginfo->submsglist) { + + submsginfo = (struct vmbus_channel_msginfo *)curr; + gpadl_body = + (struct vmbus_channel_gpadl_body *)submsginfo->msg; + + gpadl_body->header.msgtype = + CHANNELMSG_GPADL_BODY; + gpadl_body->gpadl = next_gpadl_handle; + + ret = vmbus_post_msg(gpadl_body, + submsginfo->msgsize - + sizeof(*submsginfo)); + if (ret != 0) + goto cleanup; + + } + } + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + BUG_ON(t == 0); + + + /* At this point, we received the gpadl created msg */ + *gpadl_handle = gpadlmsg->gpadl; + +cleanup: + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(msginfo); + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); + +/* + * vmbus_teardown_gpadl -Teardown the specified GPADL handle + */ +int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) +{ + struct vmbus_channel_gpadl_teardown *msg; + struct vmbus_channel_msginfo *info; + unsigned long flags; + int ret, t; + + info = kmalloc(sizeof(*info) + + sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); + if (!info) + return -ENOMEM; + + init_completion(&info->waitevent); + + msg = (struct vmbus_channel_gpadl_teardown *)info->msg; + + msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; + msg->child_relid = channel->offermsg.child_relid; + msg->gpadl = gpadl_handle; + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&info->msglistentry, + &vmbus_connection.chn_msg_list); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_gpadl_teardown)); + + BUG_ON(ret != 0); + t = wait_for_completion_timeout(&info->waitevent, 5*HZ); + BUG_ON(t == 0); + + /* Received a torndown response */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&info->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + kfree(info); + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + struct vmbus_channel_close_channel *msg; + int ret; + unsigned long flags; + + /* Stop callback and cancel the timer asap */ + spin_lock_irqsave(&channel->inbound_lock, flags); + channel->onchannel_callback = NULL; + spin_unlock_irqrestore(&channel->inbound_lock, flags); + + /* Send a closing message */ + + msg = &channel->close_msg.msg; + + msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; + msg->child_relid = channel->offermsg.child_relid; + + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); + + BUG_ON(ret != 0); + /* Tear down the gpadl for the channel's ring buffer */ + if (channel->ringbuffer_gpadlhandle) + vmbus_teardown_gpadl(channel, + channel->ringbuffer_gpadlhandle); + + /* Cleanup the ring buffers for this channel */ + hv_ringbuffer_cleanup(&channel->outbound); + hv_ringbuffer_cleanup(&channel->inbound); + + free_pages((unsigned long)channel->ringbuffer_pages, + get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); + + +} +EXPORT_SYMBOL_GPL(vmbus_close); + +/** + * vmbus_sendpacket() - Send the specified buffer on the given channel + * @channel: Pointer to vmbus_channel structure. + * @buffer: Pointer to the buffer you want to receive the data into. + * @bufferlen: Maximum size of what the the buffer will hold + * @requestid: Identifier of the request + * @type: Type of packet that is being send e.g. negotiate, time + * packet etc. + * + * Sends data in @buffer directly to hyper-v via the vmbus + * This will send the data unparsed to hyper-v. + * + * Mainly used by Hyper-V drivers. + */ +int vmbus_sendpacket(struct vmbus_channel *channel, const 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]; + u64 aligned_data = 0; + int ret; + + + /* Setup the descriptor */ + desc.type = type; /* VmbusPacketTypeDataInBand; */ + desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ + /* in 8-bytes granularity */ + desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; + 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); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL(vmbus_sendpacket); + +/* + * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer + * packets using a GPADL Direct packet type. + */ +int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, void *buffer, u32 bufferlen, + u64 requestid) +{ + int ret; + int i; + struct vmbus_channel_packet_page_buffer desc; + u32 descsize; + u32 packetlen; + u32 packetlen_aligned; + struct scatterlist bufferlist[3]; + u64 aligned_data = 0; + + if (pagecount > MAX_PAGE_BUFFER_COUNT) + return -EINVAL; + + + /* + * Adjust the size down since vmbus_channel_packet_page_buffer is the + * largest size we support + */ + descsize = sizeof(struct vmbus_channel_packet_page_buffer) - + ((MAX_PAGE_BUFFER_COUNT - pagecount) * + sizeof(struct hv_page_buffer)); + packetlen = descsize + bufferlen; + packetlen_aligned = ALIGN(packetlen, sizeof(u64)); + + /* Setup the descriptor */ + desc.type = VM_PKT_DATA_USING_GPA_DIRECT; + desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ + desc.length8 = (u16)(packetlen_aligned >> 3); + desc.transactionid = requestid; + desc.rangecount = pagecount; + + for (i = 0; i < pagecount; i++) { + desc.range[i].len = pagebuffers[i].len; + desc.range[i].offset = pagebuffers[i].offset; + 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); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); + +/* + * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet + * using a GPADL Direct packet type. + */ +int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, + struct hv_multipage_buffer *multi_pagebuffer, + void *buffer, u32 bufferlen, u64 requestid) +{ + int ret; + struct vmbus_channel_packet_multipage_buffer desc; + u32 descsize; + u32 packetlen; + u32 packetlen_aligned; + struct scatterlist bufferlist[3]; + u64 aligned_data = 0; + u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, + multi_pagebuffer->len); + + + if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) + return -EINVAL; + + /* + * Adjust the size down since vmbus_channel_packet_multipage_buffer is + * the largest size we support + */ + descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - + ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * + sizeof(u64)); + packetlen = descsize + bufferlen; + packetlen_aligned = ALIGN(packetlen, sizeof(u64)); + + + /* Setup the descriptor */ + desc.type = VM_PKT_DATA_USING_GPA_DIRECT; + desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; + desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ + desc.length8 = (u16)(packetlen_aligned >> 3); + desc.transactionid = requestid; + desc.rangecount = 1; + + desc.range.len = multi_pagebuffer->len; + desc.range.offset = multi_pagebuffer->offset; + + 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); + + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + + if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + vmbus_setevent(channel); + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); + +/** + * vmbus_recvpacket() - Retrieve the user packet on the specified channel + * @channel: Pointer to vmbus_channel structure. + * @buffer: Pointer to the buffer you want to receive the data into. + * @bufferlen: Maximum size of what the the buffer will hold + * @buffer_actual_len: The actual size of the data after it was received + * @requestid: Identifier of the request + * + * Receives directly from the hyper-v vmbus and puts the data it received + * into Buffer. This will receive the data unparsed from hyper-v. + * + * Mainly used by Hyper-V drivers. + */ +int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) +{ + struct vmpacket_descriptor desc; + u32 packetlen; + u32 userlen; + int ret; + + *buffer_actual_len = 0; + *requestid = 0; + + + ret = hv_ringbuffer_peek(&channel->inbound, &desc, + sizeof(struct vmpacket_descriptor)); + if (ret != 0) + return 0; + + packetlen = desc.len8 << 3; + userlen = packetlen - (desc.offset8 << 3); + + *buffer_actual_len = userlen; + + if (userlen > bufferlen) { + + pr_err("Buffer too small - got %d needs %d\n", + bufferlen, userlen); + return -ETOOSMALL; + } + + *requestid = desc.trans_id; + + /* Copy over the packet to the user buffer */ + ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, + (desc.offset8 << 3)); + + + return 0; +} +EXPORT_SYMBOL(vmbus_recvpacket); + +/* + * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel + */ +int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, + u32 bufferlen, u32 *buffer_actual_len, + u64 *requestid) +{ + struct vmpacket_descriptor desc; + u32 packetlen; + u32 userlen; + int ret; + + *buffer_actual_len = 0; + *requestid = 0; + + + ret = hv_ringbuffer_peek(&channel->inbound, &desc, + sizeof(struct vmpacket_descriptor)); + if (ret != 0) + return 0; + + + packetlen = desc.len8 << 3; + userlen = packetlen - (desc.offset8 << 3); + + *buffer_actual_len = packetlen; + + if (packetlen > bufferlen) { + pr_err("Buffer too small - needed %d bytes but " + "got space for only %d bytes\n", + packetlen, bufferlen); + return -ENOBUFS; + } + + *requestid = desc.trans_id; + + /* Copy over the entire packet to the user buffer */ + ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c new file mode 100644 index 000000000000..41bf287baa1c --- /dev/null +++ b/drivers/hv/channel_mgmt.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hyperv_vmbus.h" + +struct vmbus_channel_message_table_entry { + enum vmbus_channel_message_type message_type; + void (*message_handler)(struct vmbus_channel_message_header *msg); +}; + +#define MAX_MSG_TYPES 4 +#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 + +static const uuid_le + supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { + /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ + /* Storage - SCSI */ + { + .b = { + 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f + } + }, + + /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ + /* Network */ + { + .b = { + 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, + 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E + } + }, + + /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ + /* Input */ + { + .b = { + 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A + } + }, + + /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ + /* IDE */ + { + .b = { + 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 + } + }, + /* 0E0B6031-5213-4934-818B-38D90CED39DB */ + /* Shutdown */ + { + .b = { + 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB + } + }, + /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ + /* TimeSync */ + { + .b = { + 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf + } + }, + /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ + /* Heartbeat */ + { + .b = { + 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d + } + }, + /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ + /* KVP */ + { + .b = { + 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, + 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 + } + }, + +}; + + +/** + * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message + * @icmsghdrp: Pointer to msg header structure + * @icmsg_negotiate: Pointer to negotiate message structure + * @buf: Raw buffer channel data + * + * @icmsghdrp is of type &struct icmsg_hdr. + * @negop is of type &struct icmsg_negotiate. + * Set up and fill in default negotiate response message. This response can + * come from both the vmbus driver and the hv_utils driver. The current api + * will respond properly to both Windows 2008 and Windows 2008-R2 operating + * systems. + * + * Mainly used by Hyper-V drivers. + */ +void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, + struct icmsg_negotiate *negop, + u8 *buf) +{ + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + icmsghdrp->icmsgsize = 0x10; + + negop = (struct icmsg_negotiate *)&buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + if (negop->icframe_vercnt == 2 && + negop->icversion_data[1].major == 3) { + negop->icversion_data[0].major = 3; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 3; + negop->icversion_data[1].minor = 0; + } else { + negop->icversion_data[0].major = 1; + negop->icversion_data[0].minor = 0; + negop->icversion_data[1].major = 1; + negop->icversion_data[1].minor = 0; + } + + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } +} +EXPORT_SYMBOL(prep_negotiate_resp); + +/* + * alloc_channel - Allocate and initialize a vmbus channel object + */ +static struct vmbus_channel *alloc_channel(void) +{ + struct vmbus_channel *channel; + + channel = kzalloc(sizeof(*channel), GFP_ATOMIC); + if (!channel) + return NULL; + + spin_lock_init(&channel->inbound_lock); + + channel->controlwq = create_workqueue("hv_vmbus_ctl"); + if (!channel->controlwq) { + kfree(channel); + return NULL; + } + + return channel; +} + +/* + * release_hannel - Release the vmbus channel object itself + */ +static void release_channel(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + destroy_workqueue(channel->controlwq); + + kfree(channel); +} + +/* + * free_channel - Release the resources used by the vmbus channel object + */ +void free_channel(struct vmbus_channel *channel) +{ + + /* + * We have to release the channel's workqueue/thread in the vmbus's + * workqueue/thread context + * ie we can't destroy ourselves. + */ + INIT_WORK(&channel->work, release_channel); + queue_work(vmbus_connection.work_queue, &channel->work); +} + + + +/* + * vmbus_process_rescind_offer - + * Rescind the offer by initiating a device removal + */ +static void vmbus_process_rescind_offer(struct work_struct *work) +{ + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + vmbus_device_unregister(channel->device_obj); +} + +/* + * vmbus_process_offer - Process the offer by creating a channel/device + * associated with this offer + */ +static void vmbus_process_offer(struct work_struct *work) +{ + struct vmbus_channel *newchannel = container_of(work, + struct vmbus_channel, + work); + struct vmbus_channel *channel; + bool fnew = true; + int ret; + unsigned long flags; + + /* The next possible work is rescind handling */ + INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); + + /* Make sure this is a new offer */ + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { + if (!uuid_le_cmp(channel->offermsg.offer.if_type, + newchannel->offermsg.offer.if_type) && + !uuid_le_cmp(channel->offermsg.offer.if_instance, + newchannel->offermsg.offer.if_instance)) { + fnew = false; + break; + } + } + + if (fnew) + list_add_tail(&newchannel->listentry, + &vmbus_connection.chn_list); + + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + if (!fnew) { + free_channel(newchannel); + return; + } + + /* + * Start the process of binding this offer to the driver + * We need to set the DeviceObject field before calling + * vmbus_child_dev_add() + */ + newchannel->device_obj = vmbus_device_create( + &newchannel->offermsg.offer.if_type, + &newchannel->offermsg.offer.if_instance, + newchannel); + + /* + * Add the new device to the bus. This will kick off device-driver + * binding which eventually invokes the device driver's AddDevice() + * method. + */ + ret = vmbus_device_register(newchannel->device_obj); + if (ret != 0) { + pr_err("unable to add child device object (relid %d)\n", + newchannel->offermsg.child_relid); + + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&newchannel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + free_channel(newchannel); + } else { + /* + * This state is used to indicate a successful open + * so that when we do close the channel normally, we + * can cleanup properly + */ + newchannel->state = CHANNEL_OPEN_STATE; + } +} + +/* + * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. + * + */ +static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_offer_channel *offer; + struct vmbus_channel *newchannel; + uuid_le *guidtype; + uuid_le *guidinstance; + int i; + int fsupported = 0; + + offer = (struct vmbus_channel_offer_channel *)hdr; + for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { + if (!uuid_le_cmp(offer->offer.if_type, + supported_device_classes[i])) { + fsupported = 1; + break; + } + } + + if (!fsupported) + return; + + guidtype = &offer->offer.if_type; + guidinstance = &offer->offer.if_instance; + + /* Allocate the channel object and save this offer. */ + newchannel = alloc_channel(); + if (!newchannel) { + pr_err("Unable to allocate channel object\n"); + return; + } + + memcpy(&newchannel->offermsg, offer, + sizeof(struct vmbus_channel_offer_channel)); + newchannel->monitor_grp = (u8)offer->monitorid / 32; + newchannel->monitor_bit = (u8)offer->monitorid % 32; + + INIT_WORK(&newchannel->work, vmbus_process_offer); + queue_work(newchannel->controlwq, &newchannel->work); +} + +/* + * vmbus_onoffer_rescind - Rescind offer handler. + * + * We queue a work item to process this offer synchronously + */ +static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_rescind_offer *rescind; + struct vmbus_channel *channel; + + rescind = (struct vmbus_channel_rescind_offer *)hdr; + channel = relid2channel(rescind->child_relid); + + if (channel == NULL) + /* Just return here, no channel found */ + return; + + /* work is initialized for vmbus_process_rescind_offer() from + * vmbus_process_offer() where the channel got created */ + queue_work(channel->controlwq, &channel->work); +} + +/* + * vmbus_onoffers_delivered - + * This is invoked when all offers have been delivered. + * + * Nothing to do here. + */ +static void vmbus_onoffers_delivered( + struct vmbus_channel_message_header *hdr) +{ +} + +/* + * vmbus_onopen_result - Open result handler. + * + * This is invoked when we received a response to our channel open request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_open_result *result; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_open_channel *openmsg; + unsigned long flags; + + result = (struct vmbus_channel_open_result *)hdr; + + /* + * Find the open msg, copy the result and signal/unblock the wait event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { + openmsg = + (struct vmbus_channel_open_channel *)msginfo->msg; + if (openmsg->child_relid == result->child_relid && + openmsg->openid == result->openid) { + memcpy(&msginfo->response.open_result, + result, + sizeof( + struct vmbus_channel_open_result)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_ongpadl_created - GPADL created handler. + * + * This is invoked when we received a response to our gpadl create request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_gpadl_created *gpadlcreated; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_gpadl_header *gpadlheader; + unsigned long flags; + + gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; + + /* + * Find the establish msg, copy the result and signal/unblock the wait + * event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { + gpadlheader = + (struct vmbus_channel_gpadl_header *)requestheader; + + if ((gpadlcreated->child_relid == + gpadlheader->child_relid) && + (gpadlcreated->gpadl == gpadlheader->gpadl)) { + memcpy(&msginfo->response.gpadl_created, + gpadlcreated, + sizeof( + struct vmbus_channel_gpadl_created)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_ongpadl_torndown - GPADL torndown handler. + * + * This is invoked when we received a response to our gpadl teardown request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_ongpadl_torndown( + struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_gpadl_torndown *gpadl_torndown; + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_gpadl_teardown *gpadl_teardown; + unsigned long flags; + + gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; + + /* + * Find the open msg, copy the result and signal/unblock the wait event + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { + gpadl_teardown = + (struct vmbus_channel_gpadl_teardown *)requestheader; + + if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { + memcpy(&msginfo->response.gpadl_torndown, + gpadl_torndown, + sizeof( + struct vmbus_channel_gpadl_torndown)); + complete(&msginfo->waitevent); + break; + } + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* + * vmbus_onversion_response - Version response handler + * + * This is invoked when we received a response to our initiate contact request. + * Find the matching request, copy the response and signal the requesting + * thread. + */ +static void vmbus_onversion_response( + struct vmbus_channel_message_header *hdr) +{ + struct vmbus_channel_msginfo *msginfo; + struct vmbus_channel_message_header *requestheader; + struct vmbus_channel_initiate_contact *initiate; + struct vmbus_channel_version_response *version_response; + unsigned long flags; + + version_response = (struct vmbus_channel_version_response *)hdr; + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + + list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, + msglistentry) { + requestheader = + (struct vmbus_channel_message_header *)msginfo->msg; + + if (requestheader->msgtype == + CHANNELMSG_INITIATE_CONTACT) { + initiate = + (struct vmbus_channel_initiate_contact *)requestheader; + memcpy(&msginfo->response.version_response, + version_response, + sizeof(struct vmbus_channel_version_response)); + complete(&msginfo->waitevent); + } + } + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +} + +/* Channel message dispatch table */ +static struct vmbus_channel_message_table_entry + channel_message_table[CHANNELMSG_COUNT] = { + {CHANNELMSG_INVALID, NULL}, + {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, + {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, + {CHANNELMSG_REQUESTOFFERS, NULL}, + {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, + {CHANNELMSG_OPENCHANNEL, NULL}, + {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, + {CHANNELMSG_CLOSECHANNEL, NULL}, + {CHANNELMSG_GPADL_HEADER, NULL}, + {CHANNELMSG_GPADL_BODY, NULL}, + {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, + {CHANNELMSG_GPADL_TEARDOWN, NULL}, + {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, + {CHANNELMSG_RELID_RELEASED, NULL}, + {CHANNELMSG_INITIATE_CONTACT, NULL}, + {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, + {CHANNELMSG_UNLOAD, NULL}, +}; + +/* + * vmbus_onmessage - Handler for channel protocol messages. + * + * This is invoked in the vmbus worker thread context. + */ +void vmbus_onmessage(void *context) +{ + struct hv_message *msg = context; + struct vmbus_channel_message_header *hdr; + int size; + + hdr = (struct vmbus_channel_message_header *)msg->u.payload; + size = msg->header.payload_size; + + if (hdr->msgtype >= CHANNELMSG_COUNT) { + pr_err("Received invalid channel message type %d size %d\n", + hdr->msgtype, size); + print_hex_dump_bytes("", DUMP_PREFIX_NONE, + (unsigned char *)msg->u.payload, size); + return; + } + + if (channel_message_table[hdr->msgtype].message_handler) + channel_message_table[hdr->msgtype].message_handler(hdr); + else + pr_err("Unhandled channel message type %d\n", hdr->msgtype); +} + +/* + * vmbus_request_offers - Send a request to get all our pending offers. + */ +int vmbus_request_offers(void) +{ + struct vmbus_channel_message_header *msg; + struct vmbus_channel_msginfo *msginfo; + int ret, t; + + msginfo = kmalloc(sizeof(*msginfo) + + sizeof(struct vmbus_channel_message_header), + GFP_KERNEL); + if (!msginfo) + return -ENOMEM; + + init_completion(&msginfo->waitevent); + + msg = (struct vmbus_channel_message_header *)msginfo->msg; + + msg->msgtype = CHANNELMSG_REQUESTOFFERS; + + + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_message_header)); + if (ret != 0) { + pr_err("Unable to request offers - %d\n", ret); + + goto cleanup; + } + + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + + +cleanup: + kfree(msginfo); + + return ret; +} + +/* eof */ diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c new file mode 100644 index 000000000000..5f438b650068 --- /dev/null +++ b/drivers/hv/connection.c @@ -0,0 +1,318 @@ +/* + * + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hyperv_vmbus.h" + + +struct vmbus_connection vmbus_connection = { + .conn_state = DISCONNECTED, + .next_gpadl_handle = ATOMIC_INIT(0xE1E10), +}; + +/* + * vmbus_connect - Sends a connect request on the partition service connection + */ +int vmbus_connect(void) +{ + int ret = 0; + int t; + struct vmbus_channel_msginfo *msginfo = NULL; + struct vmbus_channel_initiate_contact *msg; + unsigned long flags; + + /* Initialize the vmbus connection */ + vmbus_connection.conn_state = CONNECTING; + vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); + if (!vmbus_connection.work_queue) { + ret = -ENOMEM; + goto cleanup; + } + + INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); + spin_lock_init(&vmbus_connection.channelmsg_lock); + + INIT_LIST_HEAD(&vmbus_connection.chn_list); + spin_lock_init(&vmbus_connection.channel_lock); + + /* + * Setup the vmbus event connection for channel interrupt + * abstraction stuff + */ + vmbus_connection.int_page = + (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); + if (vmbus_connection.int_page == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + vmbus_connection.recv_int_page = vmbus_connection.int_page; + vmbus_connection.send_int_page = + (void *)((unsigned long)vmbus_connection.int_page + + (PAGE_SIZE >> 1)); + + /* + * Setup the monitor notification facility. The 1st page for + * parent->child and the 2nd page for child->parent + */ + vmbus_connection.monitor_pages = + (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); + if (vmbus_connection.monitor_pages == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + msginfo = kzalloc(sizeof(*msginfo) + + sizeof(struct vmbus_channel_initiate_contact), + GFP_KERNEL); + if (msginfo == NULL) { + ret = -ENOMEM; + goto cleanup; + } + + init_completion(&msginfo->waitevent); + + msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; + + msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; + msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; + msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); + msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); + msg->monitor_page2 = virt_to_phys( + (void *)((unsigned long)vmbus_connection.monitor_pages + + PAGE_SIZE)); + + /* + * Add to list before we send the request since we may + * receive the response before returning from this routine + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&msginfo->msglistentry, + &vmbus_connection.chn_msg_list); + + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_initiate_contact)); + if (ret != 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + goto cleanup; + } + + /* Wait for the connection response */ + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + if (t == 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, + flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + ret = -ETIMEDOUT; + goto cleanup; + } + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + /* Check if successful */ + if (msginfo->response.version_response.version_supported) { + vmbus_connection.conn_state = CONNECTED; + } else { + pr_err("Unable to connect, " + "Version %d not supported by Hyper-V\n", + VMBUS_REVISION_NUMBER); + ret = -ECONNREFUSED; + goto cleanup; + } + + kfree(msginfo); + return 0; + +cleanup: + vmbus_connection.conn_state = DISCONNECTED; + + if (vmbus_connection.work_queue) + destroy_workqueue(vmbus_connection.work_queue); + + if (vmbus_connection.int_page) { + free_pages((unsigned long)vmbus_connection.int_page, 0); + vmbus_connection.int_page = NULL; + } + + if (vmbus_connection.monitor_pages) { + free_pages((unsigned long)vmbus_connection.monitor_pages, 1); + vmbus_connection.monitor_pages = NULL; + } + + kfree(msginfo); + + return ret; +} + + +/* + * relid2channel - Get the channel object given its + * child relative id (ie channel id) + */ +struct vmbus_channel *relid2channel(u32 relid) +{ + struct vmbus_channel *channel; + struct vmbus_channel *found_channel = NULL; + unsigned long flags; + + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { + if (channel->offermsg.child_relid == relid) { + found_channel = channel; + break; + } + } + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + return found_channel; +} + +/* + * process_chn_event - Process a channel event notification + */ +static void process_chn_event(u32 relid) +{ + struct vmbus_channel *channel; + unsigned long flags; + + /* + * Find the channel based on this relid and invokes the + * channel callback to process the event + */ + channel = relid2channel(relid); + + if (!channel) { + pr_err("channel not found for relid - %u\n", relid); + return; + } + + /* + * A channel once created is persistent even when there + * is no driver handling the device. An unloading driver + * sets the onchannel_callback to NULL under the + * protection of the channel inbound_lock. Thus, checking + * and invoking the driver specific callback takes care of + * orderly unloading of the driver. + */ + + spin_lock_irqsave(&channel->inbound_lock, flags); + if (channel->onchannel_callback != NULL) + channel->onchannel_callback(channel->channel_callback_context); + else + pr_err("no channel callback for relid - %u\n", relid); + + spin_unlock_irqrestore(&channel->inbound_lock, flags); +} + +/* + * vmbus_on_event - Handler for events + */ +void vmbus_on_event(unsigned long data) +{ + u32 dword; + u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + int bit; + u32 relid; + u32 *recv_int_page = vmbus_connection.recv_int_page; + + /* Check events */ + if (!recv_int_page) + return; + for (dword = 0; dword < maxdword; dword++) { + if (!recv_int_page[dword]) + continue; + for (bit = 0; bit < 32; bit++) { + if (sync_test_and_clear_bit(bit, + (unsigned long *)&recv_int_page[dword])) { + relid = (dword << 5) + bit; + + if (relid == 0) + /* + * Special case - vmbus + * channel protocol msg + */ + continue; + + process_chn_event(relid); + } + } + } +} + +/* + * vmbus_post_msg - Send a msg on the vmbus's message connection + */ +int vmbus_post_msg(void *buffer, size_t buflen) +{ + union hv_connection_id conn_id; + int ret = 0; + int retries = 0; + + conn_id.asu32 = 0; + conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; + + /* + * hv_post_message() can have transient failures because of + * insufficient resources. Retry the operation a couple of + * times before giving up. + */ + while (retries < 3) { + ret = hv_post_message(conn_id, 1, buffer, buflen); + if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) + return ret; + retries++; + msleep(100); + } + return ret; +} + +/* + * vmbus_set_event - Send an event notification to the parent + */ +int vmbus_set_event(u32 child_relid) +{ + /* Each u32 represents 32 channels */ + sync_set_bit(child_relid & 31, + (unsigned long *)vmbus_connection.send_int_page + + (child_relid >> 5)); + + return hv_signal_event(); +} diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c new file mode 100644 index 000000000000..931b7b030784 --- /dev/null +++ b/drivers/hv/hv.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "hyperv_vmbus.h" + +/* The one and only */ +struct hv_context hv_context = { + .synic_initialized = false, + .hypercall_page = NULL, + .signal_event_param = NULL, + .signal_event_buffer = NULL, +}; + +/* + * query_hypervisor_presence + * - Query the cpuid for presence of windows hypervisor + */ +static int query_hypervisor_presence(void) +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int op; + + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VERSION_FEATURES; + cpuid(op, &eax, &ebx, &ecx, &edx); + + return ecx & HV_PRESENT_BIT; +} + +/* + * query_hypervisor_info - Get version info of the windows hypervisor + */ +static int query_hypervisor_info(void) +{ + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int max_leaf; + unsigned int op; + + /* + * Its assumed that this is called after confirming that Viridian + * is present. Query id and revision. + */ + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VENDOR_MAXFUNCTION; + cpuid(op, &eax, &ebx, &ecx, &edx); + + max_leaf = eax; + + if (max_leaf >= HVCPUID_VERSION) { + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; + op = HVCPUID_VERSION; + cpuid(op, &eax, &ebx, &ecx, &edx); + pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", + eax, + ebx >> 16, + ebx & 0xFFFF, + ecx, + edx >> 24, + edx & 0xFFFFFF); + } + return max_leaf; +} + +/* + * do_hypercall- Invoke the specified hypercall + */ +static u64 do_hypercall(u64 control, void *input, void *output) +{ +#ifdef CONFIG_X86_64 + u64 hv_status = 0; + u64 input_address = (input) ? virt_to_phys(input) : 0; + u64 output_address = (output) ? virt_to_phys(output) : 0; + void *hypercall_page = hv_context.hypercall_page; + + __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); + __asm__ __volatile__("call *%3" : "=a" (hv_status) : + "c" (control), "d" (input_address), + "m" (hypercall_page)); + + return hv_status; + +#else + + u32 control_hi = control >> 32; + u32 control_lo = control & 0xFFFFFFFF; + u32 hv_status_hi = 1; + u32 hv_status_lo = 1; + u64 input_address = (input) ? virt_to_phys(input) : 0; + u32 input_address_hi = input_address >> 32; + u32 input_address_lo = input_address & 0xFFFFFFFF; + u64 output_address = (output) ? virt_to_phys(output) : 0; + u32 output_address_hi = output_address >> 32; + u32 output_address_lo = output_address & 0xFFFFFFFF; + void *hypercall_page = hv_context.hypercall_page; + + __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), + "=a"(hv_status_lo) : "d" (control_hi), + "a" (control_lo), "b" (input_address_hi), + "c" (input_address_lo), "D"(output_address_hi), + "S"(output_address_lo), "m" (hypercall_page)); + + return hv_status_lo | ((u64)hv_status_hi << 32); +#endif /* !x86_64 */ +} + +/* + * hv_init - Main initialization routine. + * + * This routine must be called before any other routines in here are called + */ +int hv_init(void) +{ + int max_leaf; + union hv_x64_msr_hypercall_contents hypercall_msr; + void *virtaddr = NULL; + + memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); + memset(hv_context.synic_message_page, 0, + sizeof(void *) * MAX_NUM_CPUS); + + if (!query_hypervisor_presence()) + goto cleanup; + + max_leaf = query_hypervisor_info(); + + rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); + + if (hv_context.guestid != 0) + goto cleanup; + + /* Write our OS info */ + wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); + hv_context.guestid = HV_LINUX_GUEST_ID; + + /* See if the hypercall page is already set */ + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + + if (!virtaddr) + goto cleanup; + + hypercall_msr.enable = 1; + + hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + /* Confirm that hypercall page did get setup. */ + hypercall_msr.as_uint64 = 0; + rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + + if (!hypercall_msr.enable) + goto cleanup; + + hv_context.hypercall_page = virtaddr; + + /* Setup the global signal event param for the signal event hypercall */ + hv_context.signal_event_buffer = + kmalloc(sizeof(struct hv_input_signal_event_buffer), + GFP_KERNEL); + if (!hv_context.signal_event_buffer) + goto cleanup; + + hv_context.signal_event_param = + (struct hv_input_signal_event *) + (ALIGN((unsigned long) + hv_context.signal_event_buffer, + HV_HYPERCALL_PARAM_ALIGN)); + hv_context.signal_event_param->connectionid.asu32 = 0; + hv_context.signal_event_param->connectionid.u.id = + VMBUS_EVENT_CONNECTION_ID; + hv_context.signal_event_param->flag_number = 0; + hv_context.signal_event_param->rsvdz = 0; + + return 0; + +cleanup: + if (virtaddr) { + if (hypercall_msr.enable) { + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + } + + vfree(virtaddr); + } + + return -ENOTSUPP; +} + +/* + * hv_cleanup - Cleanup routine. + * + * This routine is called normally during driver unloading or exiting. + */ +void hv_cleanup(void) +{ + union hv_x64_msr_hypercall_contents hypercall_msr; + + kfree(hv_context.signal_event_buffer); + hv_context.signal_event_buffer = NULL; + hv_context.signal_event_param = NULL; + + if (hv_context.hypercall_page) { + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); + vfree(hv_context.hypercall_page); + hv_context.hypercall_page = NULL; + } +} + +/* + * hv_post_message - Post a message using the hypervisor message IPC. + * + * This involves a hypercall. + */ +u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size) +{ + struct aligned_input { + u64 alignment8; + struct hv_input_post_message msg; + }; + + struct hv_input_post_message *aligned_msg; + u16 status; + unsigned long addr; + + if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) + return -EMSGSIZE; + + addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); + if (!addr) + return -ENOMEM; + + aligned_msg = (struct hv_input_post_message *) + (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); + + aligned_msg->connectionid = connection_id; + aligned_msg->message_type = message_type; + aligned_msg->payload_size = payload_size; + memcpy((void *)aligned_msg->payload, payload, payload_size); + + status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) + & 0xFFFF; + + kfree((void *)addr); + + return status; +} + + +/* + * hv_signal_event - + * Signal an event on the specified connection using the hypervisor event IPC. + * + * This involves a hypercall. + */ +u16 hv_signal_event(void) +{ + u16 status; + + status = do_hypercall(HVCALL_SIGNAL_EVENT, + hv_context.signal_event_param, + NULL) & 0xFFFF; + return status; +} + +/* + * hv_synic_init - Initialize the Synthethic Interrupt Controller. + * + * If it is already initialized by another entity (ie x2v shim), we need to + * retrieve the initialized message and event pages. Otherwise, we create and + * initialize the message and event pages. + */ +void hv_synic_init(void *irqarg) +{ + u64 version; + union hv_synic_simp simp; + union hv_synic_siefp siefp; + union hv_synic_sint shared_sint; + union hv_synic_scontrol sctrl; + + u32 irq_vector = *((u32 *)(irqarg)); + int cpu = smp_processor_id(); + + if (!hv_context.hypercall_page) + return; + + /* Check the version */ + rdmsrl(HV_X64_MSR_SVERSION, version); + + hv_context.synic_message_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_message_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC message page\n"); + goto cleanup; + } + + hv_context.synic_event_page[cpu] = + (void *)get_zeroed_page(GFP_ATOMIC); + + if (hv_context.synic_event_page[cpu] == NULL) { + pr_err("Unable to allocate SYNIC event page\n"); + goto cleanup; + } + + /* Setup the Synic's message page */ + rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + simp.simp_enabled = 1; + simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) + >> PAGE_SHIFT; + + wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + + /* Setup the Synic's event page */ + rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + siefp.siefp_enabled = 1; + siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) + >> PAGE_SHIFT; + + wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + + /* Setup the shared SINT. */ + rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + shared_sint.as_uint64 = 0; + shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ + shared_sint.masked = false; + shared_sint.auto_eoi = false; + + wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + /* Enable the global synic bit */ + rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + sctrl.enable = 1; + + wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); + + hv_context.synic_initialized = true; + return; + +cleanup: + if (hv_context.synic_event_page[cpu]) + free_page((unsigned long)hv_context.synic_event_page[cpu]); + + if (hv_context.synic_message_page[cpu]) + free_page((unsigned long)hv_context.synic_message_page[cpu]); + return; +} + +/* + * hv_synic_cleanup - Cleanup routine for hv_synic_init(). + */ +void hv_synic_cleanup(void *arg) +{ + union hv_synic_sint shared_sint; + union hv_synic_simp simp; + union hv_synic_siefp siefp; + int cpu = smp_processor_id(); + + if (!hv_context.synic_initialized) + return; + + rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + shared_sint.masked = 1; + + /* Need to correctly cleanup in the case of SMP!!! */ + /* Disable the interrupt */ + wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); + + rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + simp.simp_enabled = 0; + simp.base_simp_gpa = 0; + + wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); + + rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + siefp.siefp_enabled = 0; + siefp.base_siefp_gpa = 0; + + wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); + + free_page((unsigned long)hv_context.synic_message_page[cpu]); + free_page((unsigned long)hv_context.synic_event_page[cpu]); +} diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c new file mode 100644 index 000000000000..69c4c985daeb --- /dev/null +++ b/drivers/hv/hv_kvp.c @@ -0,0 +1,339 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "hv_kvp.h" + + + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ + int index; /* current index */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ +} kvp_transaction; + +static void kvp_send_key(struct work_struct *dummy); + +#define TIMEOUT_FIRED 1 + +static void kvp_respond_to_host(char *key, char *value, int error); +static void kvp_work_func(struct work_struct *dummy); +static void kvp_register(void); + +static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); +static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); + +static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; +static const char kvp_name[] = "kvp_kernel_module"; +static u8 *recv_buffer; +/* + * Register the kernel component with the user-level daemon. + * As part of this registration, pass the LIC version number. + */ + +static void +kvp_register(void) +{ + + struct cn_msg *msg; + + msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); + + if (msg) { + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + msg->seq = KVP_REGISTER; + strcpy(msg->data, HV_DRV_VERSION); + msg->len = strlen(HV_DRV_VERSION) + 1; + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + } +} +static void +kvp_work_func(struct work_struct *dummy) +{ + /* + * If the timer fires, the user-mode component has not responded; + * process the pending transaction. + */ + kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); +} + +/* + * Callback when data is received from user mode. + */ + +static void +kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct hv_ku_msg *message; + + message = (struct hv_ku_msg *)msg->data; + if (msg->seq == KVP_REGISTER) { + pr_info("KVP: user-mode registering done.\n"); + kvp_register(); + } + + if (msg->seq == KVP_USER_SET) { + /* + * Complete the transaction by forwarding the key value + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&kvp_work)) + kvp_respond_to_host(message->kvp_key, + message->kvp_value, + !strlen(message->kvp_key)); + } +} + +static void +kvp_send_key(struct work_struct *dummy) +{ + struct cn_msg *msg; + int index = kvp_transaction.index; + + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); + + if (msg) { + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + msg->seq = KVP_KERNEL_GET; + ((struct hv_ku_msg *)msg->data)->kvp_index = index; + msg->len = sizeof(struct hv_ku_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + } + return; +} + +/* + * Send a response back to the host. + */ + +static void +kvp_respond_to_host(char *key, char *value, int error) +{ + struct hv_kvp_msg *kvp_msg; + struct hv_kvp_msg_enumerate *kvp_data; + char *key_name; + struct icmsg_hdr *icmsghdrp; + int keylen, valuelen; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * If a transaction is not active; log and return. + */ + + if (!kvp_transaction.active) { + /* + * This is a spurious call! + */ + pr_warn("KVP: Transaction not active\n"); + return; + } + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. + */ + + buf_len = kvp_transaction.recv_len; + channel = kvp_transaction.recv_channel; + req_id = kvp_transaction.recv_req_id; + + kvp_transaction.active = false; + + if (channel->onchannel_callback == NULL) + /* + * We have raced with util driver being unloaded; + * silently return. + */ + return; + + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + kvp_msg = (struct hv_kvp_msg *) + &recv_buffer[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + kvp_data = &kvp_msg->kvp_data; + key_name = key; + + /* + * If the error parameter is set, terminate the host's enumeration. + */ + if (error) { + /* + * We don't support this index or the we have timedout; + * terminate the host-side iteration by returning an error. + */ + icmsghdrp->status = HV_E_FAIL; + goto response_done; + } + + /* + * The windows host expects the key/value pair to be encoded + * in utf16. + */ + keylen = utf8s_to_utf16s(key_name, strlen(key_name), + (wchar_t *)kvp_data->data.key); + kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ + valuelen = utf8s_to_utf16s(value, strlen(value), + (wchar_t *)kvp_data->data.value); + kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + + kvp_data->data.value_type = REG_SZ; /* all our values are strings */ + icmsghdrp->status = HV_S_OK; + +response_done: + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, + VM_PKT_DATA_INBAND, 0); + +} + +/* + * This callback is invoked when we get a KVP message from the host. + * The host ensures that only one KVP transaction can be active at a time. + * KVP implementation in Linux needs to forward the key to a user-mde + * component to retrive the corresponding value. Consequently, we cannot + * respond to the host in the conext of this callback. Since the host + * guarantees that at most only one transaction can be active at a time, + * we stash away the transaction state in a set of global variables. + */ + +void hv_kvp_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + + struct hv_kvp_msg *kvp_msg; + struct hv_kvp_msg_enumerate *kvp_data; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, negop, recv_buffer); + } else { + kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + kvp_data = &kvp_msg->kvp_data; + + /* + * We only support the "get" operation on + * "KVP_POOL_AUTO" pool. + */ + + if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || + (kvp_msg->kvp_hdr.operation != + KVP_OP_ENUMERATE)) { + icmsghdrp->status = HV_E_FAIL; + goto callback_done; + } + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + kvp_transaction.recv_len = recvlen; + kvp_transaction.recv_channel = channel; + kvp_transaction.recv_req_id = requestid; + kvp_transaction.active = true; + kvp_transaction.index = kvp_data->index; + + /* + * Get the information from the + * user-mode component. + * component. This transaction will be + * completed when we get the value from + * the user-mode component. + * Set a timeout to deal with + * user-mode not responding. + */ + schedule_work(&kvp_sendkey_work); + schedule_delayed_work(&kvp_work, 5*HZ); + + return; + + } + +callback_done: + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + +} + +int +hv_kvp_init(struct hv_util_service *srv) +{ + int err; + + err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); + if (err) + return err; + recv_buffer = srv->recv_buffer; + + return 0; +} + +void hv_kvp_deinit(void) +{ + cn_del_callback(&kvp_id); + cancel_delayed_work_sync(&kvp_work); + cancel_work_sync(&kvp_sendkey_work); +} diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h new file mode 100644 index 000000000000..9b765d7df838 --- /dev/null +++ b/drivers/hv/hv_kvp.h @@ -0,0 +1,184 @@ +/* + * An implementation of HyperV key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _KVP_H +#define _KVP_H_ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In Linux, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + +/* + * + * The following definitions are shared with the user-mode component; do not + * change any of this without making the corresponding changes in + * the KVP user-mode component. + */ + +#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ +#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ + +enum hv_ku_op { + KVP_REGISTER = 0, /* Register the user mode component */ + KVP_KERNEL_GET, /* Kernel is requesting the value */ + KVP_KERNEL_SET, /* Kernel is providing the value */ + KVP_USER_GET, /* User is requesting the value */ + KVP_USER_SET /* User is providing the value */ +}; + +struct hv_ku_msg { + __u32 kvp_index; /* Key index */ + __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ + __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ +}; + + + + +#ifdef __KERNEL__ + +/* + * Registry value types. + */ + +#define REG_SZ 1 + +enum hv_kvp_exchg_op { + KVP_OP_GET = 0, + KVP_OP_SET, + KVP_OP_DELETE, + KVP_OP_ENUMERATE, + KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + KVP_POOL_EXTERNAL = 0, + KVP_POOL_GUEST, + KVP_POOL_AUTO, + KVP_POOL_AUTO_EXTERNAL, + KVP_POOL_AUTO_INTERNAL, + KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +struct hv_kvp_hdr { + u8 operation; + u8 pool; +}; + +struct hv_kvp_exchg_msg_value { + u32 value_type; + u32 key_size; + u32 value_size; + u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct hv_kvp_msg_enumerate { + u32 index; + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + struct hv_kvp_msg_enumerate kvp_data; +}; + +int hv_kvp_init(struct hv_util_service *); +void hv_kvp_deinit(void); +void hv_kvp_onchannelcallback(void *); + +#endif /* __KERNEL__ */ +#endif /* _KVP_H */ + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c new file mode 100644 index 000000000000..e0e3a6d0244d --- /dev/null +++ b/drivers/hv/hv_util.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2010, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "hv_kvp.h" + + +static void shutdown_onchannelcallback(void *context); +static struct hv_util_service util_shutdown = { + .util_cb = shutdown_onchannelcallback, +}; + +static void timesync_onchannelcallback(void *context); +static struct hv_util_service util_timesynch = { + .util_cb = timesync_onchannelcallback, +}; + +static void heartbeat_onchannelcallback(void *context); +static struct hv_util_service util_heartbeat = { + .util_cb = heartbeat_onchannelcallback, +}; + +static struct hv_util_service util_kvp = { + .util_cb = hv_kvp_onchannelcallback, + .util_init = hv_kvp_init, + .util_deinit = hv_kvp_deinit, +}; + +static void shutdown_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + u8 execute_shutdown = false; + u8 *shut_txf_buf = util_shutdown.recv_buffer; + + struct shutdown_msg_data *shutdown_msg; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + vmbus_recvpacket(channel, shut_txf_buf, + PAGE_SIZE, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); + } else { + shutdown_msg = + (struct shutdown_msg_data *)&shut_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + switch (shutdown_msg->flags) { + case 0: + case 1: + icmsghdrp->status = HV_S_OK; + execute_shutdown = true; + + pr_info("Shutdown request received -" + " graceful shutdown initiated\n"); + break; + default: + icmsghdrp->status = HV_E_FAIL; + execute_shutdown = false; + + pr_info("Shutdown request received -" + " Invalid request\n"); + break; + } + } + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, shut_txf_buf, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + + if (execute_shutdown == true) + orderly_poweroff(true); +} + +/* + * Set guest time to host UTC time. + */ +static inline void do_adj_guesttime(u64 hosttime) +{ + s64 host_tns; + struct timespec host_ts; + + host_tns = (hosttime - WLTIMEDELTA) * 100; + host_ts = ns_to_timespec(host_tns); + + do_settimeofday(&host_ts); +} + +/* + * Set the host time in a process context. + */ + +struct adj_time_work { + struct work_struct work; + u64 host_time; +}; + +static void hv_set_host_time(struct work_struct *work) +{ + struct adj_time_work *wrk; + + wrk = container_of(work, struct adj_time_work, work); + do_adj_guesttime(wrk->host_time); + kfree(wrk); +} + +/* + * Synchronize time with host after reboot, restore, etc. + * + * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. + * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time + * message after the timesync channel is opened. Since the hv_utils module is + * loaded after hv_vmbus, the first message is usually missed. The other + * thing is, systime is automatically set to emulated hardware clock which may + * not be UTC time or in the same time zone. So, to override these effects, we + * use the first 50 time samples for initial system time setting. + */ +static inline void adj_guesttime(u64 hosttime, u8 flags) +{ + struct adj_time_work *wrk; + static s32 scnt = 50; + + wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC); + if (wrk == NULL) + return; + + wrk->host_time = hosttime; + if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { + INIT_WORK(&wrk->work, hv_set_host_time); + schedule_work(&wrk->work); + return; + } + + if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { + scnt--; + INIT_WORK(&wrk->work, hv_set_host_time); + schedule_work(&wrk->work); + } else + kfree(wrk); +} + +/* + * Time Sync Channel message handler. + */ +static void timesync_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + struct icmsg_hdr *icmsghdrp; + struct ictimesync_data *timedatap; + u8 *time_txf_buf = util_timesynch.recv_buffer; + + vmbus_recvpacket(channel, time_txf_buf, + PAGE_SIZE, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); + } else { + timedatap = (struct ictimesync_data *)&time_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + adj_guesttime(timedatap->parenttime, timedatap->flags); + } + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, time_txf_buf, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } +} + +/* + * Heartbeat functionality. + * Every two seconds, Hyper-V send us a heartbeat request message. + * we respond to this message, and Hyper-V knows we are alive. + */ +static void heartbeat_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + struct icmsg_hdr *icmsghdrp; + struct heartbeat_msg_data *heartbeat_msg; + u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; + + vmbus_recvpacket(channel, hbeat_txf_buf, + PAGE_SIZE, &recvlen, &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); + } else { + heartbeat_msg = + (struct heartbeat_msg_data *)&hbeat_txf_buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + heartbeat_msg->seq_num += 1; + } + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, hbeat_txf_buf, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } +} + +static int util_probe(struct hv_device *dev, + const struct hv_vmbus_device_id *dev_id) +{ + struct hv_util_service *srv = + (struct hv_util_service *)dev_id->driver_data; + int ret; + + srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!srv->recv_buffer) + return -ENOMEM; + if (srv->util_init) { + ret = srv->util_init(srv); + if (ret) { + ret = -ENODEV; + goto error1; + } + } + + ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0, + srv->util_cb, dev->channel); + if (ret) + goto error; + + hv_set_drvdata(dev, srv); + return 0; + +error: + if (srv->util_deinit) + srv->util_deinit(); +error1: + kfree(srv->recv_buffer); + return ret; +} + +static int util_remove(struct hv_device *dev) +{ + struct hv_util_service *srv = hv_get_drvdata(dev); + + vmbus_close(dev->channel); + if (srv->util_deinit) + srv->util_deinit(); + kfree(srv->recv_buffer); + + return 0; +} + +static const struct hv_vmbus_device_id id_table[] = { + /* Shutdown guid */ + { VMBUS_DEVICE(0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, + 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB) + .driver_data = (unsigned long)&util_shutdown }, + /* Time synch guid */ + { VMBUS_DEVICE(0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, + 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf) + .driver_data = (unsigned long)&util_timesynch }, + /* Heartbeat guid */ + { VMBUS_DEVICE(0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, + 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d) + .driver_data = (unsigned long)&util_heartbeat }, + /* KVP guid */ + { VMBUS_DEVICE(0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, + 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6) + .driver_data = (unsigned long)&util_kvp }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +/* The one and only one */ +static struct hv_driver util_drv = { + .name = "hv_util", + .id_table = id_table, + .probe = util_probe, + .remove = util_remove, +}; + +static int __init init_hyperv_utils(void) +{ + pr_info("Registering HyperV Utility Driver\n"); + + return vmbus_driver_register(&util_drv); +} + +static void exit_hyperv_utils(void) +{ + pr_info("De-Registered HyperV Utility Driver\n"); + + vmbus_driver_unregister(&util_drv); +} + +module_init(init_hyperv_utils); +module_exit(exit_hyperv_utils); + +MODULE_DESCRIPTION("Hyper-V Utilities"); +MODULE_VERSION(HV_DRV_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h new file mode 100644 index 000000000000..8261cb64931b --- /dev/null +++ b/drivers/hv/hyperv_vmbus.h @@ -0,0 +1,628 @@ +/* + * + * Copyright (c) 2011, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * K. Y. Srinivasan + * + */ + +#ifndef _HYPERV_VMBUS_H +#define _HYPERV_VMBUS_H + +#include +#include +#include +#include + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HVCPUID_VERSION_FEATURES). + */ +enum hv_cpuid_function { + HVCPUID_VERSION_FEATURES = 0x00000001, + HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, + HVCPUID_INTERFACE = 0x40000001, + + /* + * The remaining functions depend on the value of + * HVCPUID_INTERFACE + */ + HVCPUID_VERSION = 0x40000002, + HVCPUID_FEATURES = 0x40000003, + HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, + HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, +}; + +/* Define version of the synthetic interrupt controller. */ +#define HV_SYNIC_VERSION (1) + +/* Define the expected SynIC version. */ +#define HV_SYNIC_VERSION_1 (0x1) + +/* Define synthetic interrupt controller message constants. */ +#define HV_MESSAGE_SIZE (256) +#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) +#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF) + +/* Define synthetic interrupt controller flag constants. */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256) +#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) + +/* Define hypervisor message types. */ +enum hv_message_type { + HVMSG_NONE = 0x00000000, + + /* Memory access messages. */ + HVMSG_UNMAPPED_GPA = 0x80000000, + HVMSG_GPA_INTERCEPT = 0x80000001, + + /* Timer notification messages. */ + HVMSG_TIMER_EXPIRED = 0x80000010, + + /* Error messages. */ + HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, + HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, + HVMSG_UNSUPPORTED_FEATURE = 0x80000022, + + /* Trace buffer complete messages. */ + HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, + + /* Platform-specific processor intercept messages. */ + HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, + HVMSG_X64_MSR_INTERCEPT = 0x80010001, + HVMSG_X64_CPUID_INTERCEPT = 0x80010002, + HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, + HVMSG_X64_APIC_EOI = 0x80010004, + HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 +}; + +/* Define the number of synthetic interrupt sources. */ +#define HV_SYNIC_SINT_COUNT (16) +#define HV_SYNIC_STIMER_COUNT (4) + +/* Define invalid partition identifier. */ +#define HV_PARTITION_ID_INVALID ((u64)0x0) + +/* Define connection identifier type. */ +union hv_connection_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u; +}; + +/* Define port identifier type. */ +union hv_port_id { + u32 asu32; + struct { + u32 id:24; + u32 reserved:8; + } u ; +}; + +/* Define port type. */ +enum hv_port_type { + HVPORT_MSG = 1, + HVPORT_EVENT = 2, + HVPORT_MONITOR = 3 +}; + +/* Define port information structure. */ +struct hv_port_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u32 target_sint; + u32 target_vp; + u64 rsvdz; + } message_port_info; + struct { + u32 target_sint; + u32 target_vp; + u16 base_flag_bumber; + u16 flag_count; + u32 rsvdz; + } event_port_info; + struct { + u64 monitor_address; + u64 rsvdz; + } monitor_port_info; + }; +}; + +struct hv_connection_info { + enum hv_port_type port_type; + u32 padding; + union { + struct { + u64 rsvdz; + } message_connection_info; + struct { + u64 rsvdz; + } event_connection_info; + struct { + u64 monitor_address; + } monitor_connection_info; + }; +}; + +/* Define synthetic interrupt controller message flags. */ +union hv_message_flags { + u8 asu8; + struct { + u8 msg_pending:1; + u8 reserved:7; + }; +}; + +/* Define synthetic interrupt controller message header. */ +struct hv_message_header { + enum hv_message_type message_type; + u8 payload_size; + union hv_message_flags message_flags; + u8 reserved[2]; + union { + u64 sender; + union hv_port_id port; + }; +}; + +/* Define timer message payload structure. */ +struct hv_timer_message_payload { + u32 timer_index; + u32 reserved; + u64 expiration_time; /* When the timer expired */ + u64 delivery_time; /* When the message was delivered */ +}; + +/* Define synthetic interrupt controller message format. */ +struct hv_message { + struct hv_message_header header; + union { + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; + } u ; +}; + +/* Define the number of message buffers associated with each port. */ +#define HV_PORT_MESSAGE_BUFFER_COUNT (16) + +/* Define the synthetic interrupt message page layout. */ +struct hv_message_page { + struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; +}; + +/* Define the synthetic interrupt controller event flags format. */ +union hv_synic_event_flags { + u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; + u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; +}; + +/* Define the synthetic interrupt flags page layout. */ +struct hv_synic_event_flags_page { + union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; +}; + +/* Define SynIC control register. */ +union hv_synic_scontrol { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:63; + }; +}; + +/* Define synthetic interrupt source. */ +union hv_synic_sint { + u64 as_uint64; + struct { + u64 vector:8; + u64 reserved1:8; + u64 masked:1; + u64 auto_eoi:1; + u64 reserved2:46; + }; +}; + +/* Define the format of the SIMP register */ +union hv_synic_simp { + u64 as_uint64; + struct { + u64 simp_enabled:1; + u64 preserved:11; + u64 base_simp_gpa:52; + }; +}; + +/* Define the format of the SIEFP register */ +union hv_synic_siefp { + u64 as_uint64; + struct { + u64 siefp_enabled:1; + u64 preserved:11; + u64 base_siefp_gpa:52; + }; +}; + +/* Definitions for the monitored notification facility */ +union hv_monitor_trigger_group { + u64 as_uint64; + struct { + u32 pending; + u32 armed; + }; +}; + +struct hv_monitor_parameter { + union hv_connection_id connectionid; + u16 flagnumber; + u16 rsvdz; +}; + +union hv_monitor_trigger_state { + u32 asu32; + + struct { + u32 group_enable:4; + u32 rsvdz:28; + }; +}; + +/* struct hv_monitor_page Layout */ +/* ------------------------------------------------------ */ +/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ +/* | 8 | TriggerGroup[0] | */ +/* | 10 | TriggerGroup[1] | */ +/* | 18 | TriggerGroup[2] | */ +/* | 20 | TriggerGroup[3] | */ +/* | 28 | Rsvd2[0] | */ +/* | 30 | Rsvd2[1] | */ +/* | 38 | Rsvd2[2] | */ +/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ +/* | ... | */ +/* | 240 | Latency[0][0..3] | */ +/* | 340 | Rsvz3[0] | */ +/* | 440 | Parameter[0][0] | */ +/* | 448 | Parameter[0][1] | */ +/* | ... | */ +/* | 840 | Rsvd4[0] | */ +/* ------------------------------------------------------ */ +struct hv_monitor_page { + union hv_monitor_trigger_state trigger_state; + u32 rsvdz1; + + union hv_monitor_trigger_group trigger_group[4]; + u64 rsvdz2[3]; + + s32 next_checktime[4][32]; + + u16 latency[4][32]; + u64 rsvdz3[32]; + + struct hv_monitor_parameter parameter[4][32]; + + u8 rsvdz4[1984]; +}; + +/* Declare the various hypercall operations. */ +enum hv_call_code { + HVCALL_POST_MESSAGE = 0x005c, + HVCALL_SIGNAL_EVENT = 0x005d, +}; + +/* Definition of the hv_post_message hypercall input structure. */ +struct hv_input_post_message { + union hv_connection_id connectionid; + u32 reserved; + enum hv_message_type message_type; + u32 payload_size; + u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; +}; + +/* Definition of the hv_signal_event hypercall input structure. */ +struct hv_input_signal_event { + union hv_connection_id connectionid; + u16 flag_number; + u16 rsvdz; +}; + +/* + * Versioning definitions used for guests reporting themselves to the + * hypervisor, and visa versa. + */ + +/* Version info reported by guest OS's */ +enum hv_guest_os_vendor { + HVGUESTOS_VENDOR_MICROSOFT = 0x0001 +}; + +enum hv_guest_os_microsoft_ids { + HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, + HVGUESTOS_MICROSOFT_MSDOS = 0x01, + HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, + HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, + HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, + HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 +}; + +/* + * Declare the MSR used to identify the guest OS. + */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +union hv_x64_msr_guest_os_id_contents { + u64 as_uint64; + struct { + u64 build_number:16; + u64 service_version:8; /* Service Pack, etc. */ + u64 minor_version:8; + u64 major_version:8; + u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ + u64 vendor_id:16; /* enum hv_guest_os_vendor */ + }; +}; + +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor. + */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +union hv_x64_msr_hypercall_contents { + u64 as_uint64; + struct { + u64 enable:1; + u64 reserved:11; + u64 guest_physical_address:52; + }; +}; + + +enum { + VMBUS_MESSAGE_CONNECTION_ID = 1, + VMBUS_MESSAGE_PORT_ID = 1, + VMBUS_EVENT_CONNECTION_ID = 2, + VMBUS_EVENT_PORT_ID = 2, + VMBUS_MONITOR_CONNECTION_ID = 3, + VMBUS_MONITOR_PORT_ID = 3, + VMBUS_MESSAGE_SINT = 2, +}; + +/* #defines */ + +#define HV_PRESENT_BIT 0x80000000 + +#define HV_LINUX_GUEST_ID_LO 0x00000000 +#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 +#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ + HV_LINUX_GUEST_ID_LO) + +#define HV_CPU_POWER_MANAGEMENT (1 << 0) +#define HV_RECOMMENDATIONS_MAX 4 + +#define HV_X64_MAX 5 +#define HV_CAPS_MAX 8 + + +#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) + + +/* Service definitions */ + +#define HV_SERVICE_PARENT_PORT (0) +#define HV_SERVICE_PARENT_CONNECTION (0) + +#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) +#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) +#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) +#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) + +#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) +#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) +#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) +#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) +#define HV_SERVICE_MAX_MESSAGE_ID (4) + +#define HV_SERVICE_PROTOCOL_VERSION (0x0010) +#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 + +/* #define VMBUS_REVISION_NUMBER 6 */ + +/* Our local vmbus's port and connection id. Anything >0 is fine */ +/* #define VMBUS_PORT_ID 11 */ + +/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ +static const uuid_le VMBUS_SERVICE_ID = { + .b = { + 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, + 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 + }, +}; + +#define MAX_NUM_CPUS 32 + + +struct hv_input_signal_event_buffer { + u64 align8; + struct hv_input_signal_event event; +}; + +struct hv_context { + /* We only support running on top of Hyper-V + * So at this point this really can only contain the Hyper-V ID + */ + u64 guestid; + + void *hypercall_page; + + bool synic_initialized; + + /* + * This is used as an input param to HvCallSignalEvent hypercall. The + * input param is immutable in our usage and must be dynamic mem (vs + * stack or global). */ + struct hv_input_signal_event_buffer *signal_event_buffer; + /* 8-bytes aligned of the buffer above */ + struct hv_input_signal_event *signal_event_param; + + void *synic_message_page[MAX_NUM_CPUS]; + void *synic_event_page[MAX_NUM_CPUS]; +}; + +extern struct hv_context hv_context; + + +/* Hv Interface */ + +extern int hv_init(void); + +extern void hv_cleanup(void); + +extern u16 hv_post_message(union hv_connection_id connection_id, + enum hv_message_type message_type, + void *payload, size_t payload_size); + +extern u16 hv_signal_event(void); + +extern void hv_synic_init(void *irqarg); + +extern void hv_synic_cleanup(void *arg); + + +/* Interface */ + + +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +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); + +int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, + u32 buflen); + +int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, + void *buffer, + u32 buflen, + u32 offset); + +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); + +void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); + +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, + struct hv_ring_buffer_debug_info *debug_info); + +/* + * Maximum channels is determined by the size of the interrupt page + * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt + * and the other is receive endpoint interrupt + */ +#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ + +/* The value here must be in multiple of 32 */ +/* TODO: Need to make this configurable */ +#define MAX_NUM_CHANNELS_SUPPORTED 256 + + +enum vmbus_connect_state { + DISCONNECTED, + CONNECTING, + CONNECTED, + DISCONNECTING +}; + +#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT + +struct vmbus_connection { + enum vmbus_connect_state conn_state; + + atomic_t next_gpadl_handle; + + /* + * Represents channel interrupts. Each bit position represents a + * channel. When a channel sends an interrupt via VMBUS, it finds its + * bit in the sendInterruptPage, set it and calls Hv to generate a port + * event. The other end receives the port event and parse the + * recvInterruptPage to see which bit is set + */ + void *int_page; + void *send_int_page; + void *recv_int_page; + + /* + * 2 pages - 1st page for parent->child notification and 2nd + * is child->parent notification + */ + void *monitor_pages; + struct list_head chn_msg_list; + spinlock_t channelmsg_lock; + + /* List of channels */ + struct list_head chn_list; + spinlock_t channel_lock; + + struct workqueue_struct *work_queue; +}; + + +struct vmbus_msginfo { + /* Bookkeeping stuff */ + struct list_head msglist_entry; + + /* The message itself */ + unsigned char msg[0]; +}; + + +extern struct vmbus_connection vmbus_connection; + +/* General vmbus interface */ + +struct hv_device *vmbus_device_create(uuid_le *type, + uuid_le *instance, + struct vmbus_channel *channel); + +int vmbus_device_register(struct hv_device *child_device_obj); +void vmbus_device_unregister(struct hv_device *device_obj); + +/* static void */ +/* VmbusChildDeviceDestroy( */ +/* struct hv_device *); */ + +struct vmbus_channel *relid2channel(u32 relid); + + +/* Connection interface */ + +int vmbus_connect(void); + +int vmbus_post_msg(void *buffer, size_t buflen); + +int vmbus_set_event(u32 child_relid); + +void vmbus_on_event(unsigned long data); + + +#endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c new file mode 100644 index 000000000000..f594ed09d7e0 --- /dev/null +++ b/drivers/hv/ring_buffer.c @@ -0,0 +1,527 @@ +/* + * + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * K. Y. Srinivasan + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#include "hyperv_vmbus.h" + + +/* #defines */ + + +/* Amount of space to write to */ +#define BYTES_AVAIL_TO_WRITE(r, w, z) \ + ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) + + +/* + * + * hv_get_ringbuffer_availbytes() + * + * Get number of bytes available to read and to write to + * for the specified ring buffer + */ +static inline void +hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, + u32 *read, u32 *write) +{ + u32 read_loc, write_loc; + + smp_read_barrier_depends(); + + /* Capture the read/write indices before they changed */ + read_loc = rbi->ring_buffer->read_index; + write_loc = rbi->ring_buffer->write_index; + + *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize); + *read = rbi->ring_datasize - *write; +} + +/* + * hv_get_next_write_location() + * + * Get the next write location for the specified ring buffer + * + */ +static inline u32 +hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) +{ + u32 next = ring_info->ring_buffer->write_index; + + return next; +} + +/* + * hv_set_next_write_location() + * + * Set the next write location for the specified ring buffer + * + */ +static inline void +hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, + u32 next_write_location) +{ + ring_info->ring_buffer->write_index = next_write_location; +} + +/* + * hv_get_next_read_location() + * + * Get the next read location for the specified ring buffer + */ +static inline u32 +hv_get_next_read_location(struct hv_ring_buffer_info *ring_info) +{ + u32 next = ring_info->ring_buffer->read_index; + + return next; +} + +/* + * hv_get_next_readlocation_withoffset() + * + * Get the next read location + offset for the specified ring buffer. + * This allows the caller to skip + */ +static inline u32 +hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, + u32 offset) +{ + u32 next = ring_info->ring_buffer->read_index; + + next += offset; + next %= ring_info->ring_datasize; + + return next; +} + +/* + * + * hv_set_next_read_location() + * + * Set the next read location for the specified ring buffer + * + */ +static inline void +hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, + u32 next_read_location) +{ + ring_info->ring_buffer->read_index = next_read_location; +} + + +/* + * + * hv_get_ring_buffer() + * + * Get the start of the ring buffer + */ +static inline void * +hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) +{ + return (void *)ring_info->ring_buffer->buffer; +} + + +/* + * + * hv_get_ring_buffersize() + * + * Get the size of the ring buffer + */ +static inline u32 +hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info) +{ + return ring_info->ring_datasize; +} + +/* + * + * hv_get_ring_bufferindices() + * + * Get the read and write indices as u64 of the specified ring buffer + * + */ +static inline u64 +hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) +{ + return (u64)ring_info->ring_buffer->write_index << 32; +} + + +/* + * + * hv_dump_ring_info() + * + * Dump out to console the ring buffer info + * + */ +void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) +{ + u32 bytes_avail_towrite; + u32 bytes_avail_toread; + + hv_get_ringbuffer_availbytes(ring_info, + &bytes_avail_toread, + &bytes_avail_towrite); + + DPRINT(VMBUS, + DEBUG_RING_LVL, + "%s <>", + prefix, + ring_info, + ring_info->ring_buffer->buffer, + bytes_avail_towrite, + bytes_avail_toread, + ring_info->ring_buffer->read_index, + ring_info->ring_buffer->write_index); +} + + +/* + * + * hv_copyfrom_ringbuffer() + * + * Helper routine to copy to source from ring buffer. + * Assume there is enough room. Handles wrap-around in src case only!! + * + */ +static u32 hv_copyfrom_ringbuffer( + struct hv_ring_buffer_info *ring_info, + void *dest, + u32 destlen, + u32 start_read_offset) +{ + void *ring_buffer = hv_get_ring_buffer(ring_info); + u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); + + u32 frag_len; + + /* wrap-around detected at the src */ + if (destlen > ring_buffer_size - start_read_offset) { + frag_len = ring_buffer_size - start_read_offset; + + memcpy(dest, ring_buffer + start_read_offset, frag_len); + memcpy(dest + frag_len, ring_buffer, destlen - frag_len); + } else + + memcpy(dest, ring_buffer + start_read_offset, destlen); + + + start_read_offset += destlen; + start_read_offset %= ring_buffer_size; + + return start_read_offset; +} + + +/* + * + * hv_copyto_ringbuffer() + * + * Helper routine to copy from source to ring buffer. + * Assume there is enough room. Handles wrap-around in dest case only!! + * + */ +static u32 hv_copyto_ringbuffer( + struct hv_ring_buffer_info *ring_info, + u32 start_write_offset, + void *src, + u32 srclen) +{ + void *ring_buffer = hv_get_ring_buffer(ring_info); + u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); + u32 frag_len; + + /* wrap-around detected! */ + if (srclen > ring_buffer_size - start_write_offset) { + frag_len = ring_buffer_size - start_write_offset; + memcpy(ring_buffer + start_write_offset, src, frag_len); + memcpy(ring_buffer, src + frag_len, srclen - frag_len); + } else + memcpy(ring_buffer + start_write_offset, src, srclen); + + start_write_offset += srclen; + start_write_offset %= ring_buffer_size; + + return start_write_offset; +} + +/* + * + * hv_ringbuffer_get_debuginfo() + * + * Get various debug metrics for the specified ring buffer + * + */ +void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, + struct hv_ring_buffer_debug_info *debug_info) +{ + u32 bytes_avail_towrite; + u32 bytes_avail_toread; + + if (ring_info->ring_buffer) { + hv_get_ringbuffer_availbytes(ring_info, + &bytes_avail_toread, + &bytes_avail_towrite); + + debug_info->bytes_avail_toread = bytes_avail_toread; + debug_info->bytes_avail_towrite = bytes_avail_towrite; + debug_info->current_read_index = + ring_info->ring_buffer->read_index; + debug_info->current_write_index = + ring_info->ring_buffer->write_index; + debug_info->current_interrupt_mask = + ring_info->ring_buffer->interrupt_mask; + } +} + + +/* + * + * hv_get_ringbuffer_interrupt_mask() + * + * Get the interrupt mask for the specified ring buffer + * + */ +u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) +{ + return rbi->ring_buffer->interrupt_mask; +} + +/* + * + * hv_ringbuffer_init() + * + *Initialize the ring buffer + * + */ +int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, + void *buffer, u32 buflen) +{ + if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) + return -EINVAL; + + memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); + + ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; + ring_info->ring_buffer->read_index = + ring_info->ring_buffer->write_index = 0; + + ring_info->ring_size = buflen; + ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); + + spin_lock_init(&ring_info->ring_lock); + + return 0; +} + +/* + * + * hv_ringbuffer_cleanup() + * + * Cleanup the ring buffer + * + */ +void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) +{ +} + +/* + * + * hv_ringbuffer_write() + * + * Write to the ring buffer + * + */ +int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, + struct scatterlist *sglist, u32 sgcount) +{ + int i = 0; + u32 bytes_avail_towrite; + u32 bytes_avail_toread; + u32 totalbytes_towrite = 0; + + struct scatterlist *sg; + u32 next_write_location; + u64 prev_indices = 0; + unsigned long flags; + + for_each_sg(sglist, sg, sgcount, i) + { + totalbytes_towrite += sg->length; + } + + totalbytes_towrite += sizeof(u64); + + spin_lock_irqsave(&outring_info->ring_lock, flags); + + hv_get_ringbuffer_availbytes(outring_info, + &bytes_avail_toread, + &bytes_avail_towrite); + + + /* If there is only room for the packet, assume it is full. */ + /* Otherwise, the next time around, we think the ring buffer */ + /* is empty since the read index == write index */ + if (bytes_avail_towrite <= totalbytes_towrite) { + spin_unlock_irqrestore(&outring_info->ring_lock, flags); + return -EAGAIN; + } + + /* Write to the ring buffer */ + next_write_location = hv_get_next_write_location(outring_info); + + for_each_sg(sglist, sg, sgcount, i) + { + next_write_location = hv_copyto_ringbuffer(outring_info, + next_write_location, + sg_virt(sg), + sg->length); + } + + /* Set previous packet start */ + prev_indices = hv_get_ring_bufferindices(outring_info); + + next_write_location = hv_copyto_ringbuffer(outring_info, + next_write_location, + &prev_indices, + sizeof(u64)); + + /* Make sure we flush all writes before updating the writeIndex */ + smp_wmb(); + + /* Now, update the write location */ + hv_set_next_write_location(outring_info, next_write_location); + + + spin_unlock_irqrestore(&outring_info->ring_lock, flags); + return 0; +} + + +/* + * + * hv_ringbuffer_peek() + * + * Read without advancing the read index + * + */ +int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, + void *Buffer, u32 buflen) +{ + u32 bytes_avail_towrite; + u32 bytes_avail_toread; + u32 next_read_location = 0; + unsigned long flags; + + spin_lock_irqsave(&Inring_info->ring_lock, flags); + + hv_get_ringbuffer_availbytes(Inring_info, + &bytes_avail_toread, + &bytes_avail_towrite); + + /* Make sure there is something to read */ + if (bytes_avail_toread < buflen) { + + spin_unlock_irqrestore(&Inring_info->ring_lock, flags); + + return -EAGAIN; + } + + /* Convert to byte offset */ + next_read_location = hv_get_next_read_location(Inring_info); + + next_read_location = hv_copyfrom_ringbuffer(Inring_info, + Buffer, + buflen, + next_read_location); + + spin_unlock_irqrestore(&Inring_info->ring_lock, flags); + + return 0; +} + + +/* + * + * hv_ringbuffer_read() + * + * Read and advance the read index + * + */ +int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, + u32 buflen, u32 offset) +{ + u32 bytes_avail_towrite; + u32 bytes_avail_toread; + u32 next_read_location = 0; + u64 prev_indices = 0; + unsigned long flags; + + if (buflen <= 0) + return -EINVAL; + + spin_lock_irqsave(&inring_info->ring_lock, flags); + + hv_get_ringbuffer_availbytes(inring_info, + &bytes_avail_toread, + &bytes_avail_towrite); + + /* Make sure there is something to read */ + if (bytes_avail_toread < buflen) { + spin_unlock_irqrestore(&inring_info->ring_lock, flags); + + return -EAGAIN; + } + + next_read_location = + hv_get_next_readlocation_withoffset(inring_info, offset); + + next_read_location = hv_copyfrom_ringbuffer(inring_info, + buffer, + buflen, + next_read_location); + + next_read_location = hv_copyfrom_ringbuffer(inring_info, + &prev_indices, + sizeof(u64), + next_read_location); + + /* Make sure all reads are done before we update the read index since */ + /* the writer may start writing to the read area once the read index */ + /*is updated */ + smp_mb(); + + /* Update the read index */ + hv_set_next_read_location(inring_info, next_read_location); + + spin_unlock_irqrestore(&inring_info->ring_lock, flags); + + return 0; +} diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c new file mode 100644 index 000000000000..b0d08f980de1 --- /dev/null +++ b/drivers/hv/vmbus_drv.c @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * K. Y. Srinivasan + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hyperv_vmbus.h" + + +static struct acpi_device *hv_acpi_dev; + +static struct tasklet_struct msg_dpc; +static struct tasklet_struct event_dpc; + +unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); +EXPORT_SYMBOL(vmbus_loglevel); + +static struct completion probe_event; +static int irq; + +static void get_channel_info(struct hv_device *device, + struct hv_device_info *info) +{ + struct vmbus_channel_debug_info debug_info; + + if (!device->channel) + return; + + vmbus_get_debug_info(device->channel, &debug_info); + + info->chn_id = debug_info.relid; + info->chn_state = debug_info.state; + memcpy(&info->chn_type, &debug_info.interfacetype, + sizeof(uuid_le)); + memcpy(&info->chn_instance, &debug_info.interface_instance, + sizeof(uuid_le)); + + info->monitor_id = debug_info.monitorid; + + info->server_monitor_pending = debug_info.servermonitor_pending; + info->server_monitor_latency = debug_info.servermonitor_latency; + info->server_monitor_conn_id = debug_info.servermonitor_connectionid; + + info->client_monitor_pending = debug_info.clientmonitor_pending; + info->client_monitor_latency = debug_info.clientmonitor_latency; + info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; + + info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; + info->inbound.read_idx = debug_info.inbound.current_read_index; + info->inbound.write_idx = debug_info.inbound.current_write_index; + info->inbound.bytes_avail_toread = + debug_info.inbound.bytes_avail_toread; + info->inbound.bytes_avail_towrite = + debug_info.inbound.bytes_avail_towrite; + + info->outbound.int_mask = + debug_info.outbound.current_interrupt_mask; + info->outbound.read_idx = debug_info.outbound.current_read_index; + info->outbound.write_idx = debug_info.outbound.current_write_index; + info->outbound.bytes_avail_toread = + debug_info.outbound.bytes_avail_toread; + info->outbound.bytes_avail_towrite = + debug_info.outbound.bytes_avail_towrite; +} + +#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) +static void print_alias_name(struct hv_device *hv_dev, char *alias_name) +{ + int i; + for (i = 0; i < VMBUS_ALIAS_LEN; i += 2) + sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); +} + +/* + * vmbus_show_device_attr - Show the device attribute in sysfs. + * + * This is invoked when user does a + * "cat /sys/bus/vmbus/devices//" + */ +static ssize_t vmbus_show_device_attr(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_device_info *device_info; + char alias_name[VMBUS_ALIAS_LEN + 1]; + int ret = 0; + + device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL); + if (!device_info) + return ret; + + get_channel_info(hv_dev, device_info); + + if (!strcmp(dev_attr->attr.name, "class_id")) { + ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}\n", + device_info->chn_type.b[3], + device_info->chn_type.b[2], + device_info->chn_type.b[1], + device_info->chn_type.b[0], + device_info->chn_type.b[5], + device_info->chn_type.b[4], + device_info->chn_type.b[7], + device_info->chn_type.b[6], + device_info->chn_type.b[8], + device_info->chn_type.b[9], + device_info->chn_type.b[10], + device_info->chn_type.b[11], + device_info->chn_type.b[12], + device_info->chn_type.b[13], + device_info->chn_type.b[14], + device_info->chn_type.b[15]); + } else if (!strcmp(dev_attr->attr.name, "device_id")) { + ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x%02x%02x}\n", + device_info->chn_instance.b[3], + device_info->chn_instance.b[2], + device_info->chn_instance.b[1], + device_info->chn_instance.b[0], + device_info->chn_instance.b[5], + device_info->chn_instance.b[4], + device_info->chn_instance.b[7], + device_info->chn_instance.b[6], + device_info->chn_instance.b[8], + device_info->chn_instance.b[9], + device_info->chn_instance.b[10], + device_info->chn_instance.b[11], + device_info->chn_instance.b[12], + device_info->chn_instance.b[13], + device_info->chn_instance.b[14], + device_info->chn_instance.b[15]); + } else if (!strcmp(dev_attr->attr.name, "modalias")) { + print_alias_name(hv_dev, alias_name); + ret = sprintf(buf, "vmbus:%s\n", alias_name); + } else if (!strcmp(dev_attr->attr.name, "state")) { + ret = sprintf(buf, "%d\n", device_info->chn_state); + } else if (!strcmp(dev_attr->attr.name, "id")) { + ret = sprintf(buf, "%d\n", device_info->chn_id); + } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { + ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); + } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { + ret = sprintf(buf, "%d\n", device_info->outbound.read_idx); + } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { + ret = sprintf(buf, "%d\n", device_info->outbound.write_idx); + } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { + ret = sprintf(buf, "%d\n", + device_info->outbound.bytes_avail_toread); + } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { + ret = sprintf(buf, "%d\n", + device_info->outbound.bytes_avail_towrite); + } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { + ret = sprintf(buf, "%d\n", device_info->inbound.int_mask); + } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { + ret = sprintf(buf, "%d\n", device_info->inbound.read_idx); + } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { + ret = sprintf(buf, "%d\n", device_info->inbound.write_idx); + } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { + ret = sprintf(buf, "%d\n", + device_info->inbound.bytes_avail_toread); + } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { + ret = sprintf(buf, "%d\n", + device_info->inbound.bytes_avail_towrite); + } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { + ret = sprintf(buf, "%d\n", device_info->monitor_id); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { + ret = sprintf(buf, "%d\n", device_info->server_monitor_pending); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { + ret = sprintf(buf, "%d\n", device_info->server_monitor_latency); + } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { + ret = sprintf(buf, "%d\n", + device_info->server_monitor_conn_id); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { + ret = sprintf(buf, "%d\n", device_info->client_monitor_pending); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { + ret = sprintf(buf, "%d\n", device_info->client_monitor_latency); + } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { + ret = sprintf(buf, "%d\n", + device_info->client_monitor_conn_id); + } + + kfree(device_info); + return ret; +} + +/* Set up per device attributes in /sys/bus/vmbus/devices/ */ +static struct device_attribute vmbus_device_attrs[] = { + __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL), + + __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), + + __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), + + __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), + + __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), + __ATTR_NULL +}; + + +/* + * vmbus_uevent - add uevent for our device + * + * This routine is invoked when a device is added or removed on the vmbus to + * generate a uevent to udev in the userspace. The udev will then look at its + * rule and the uevent generated here to load the appropriate driver + * + * The alias string will be of the form vmbus:guid where guid is the string + * representation of the device guid (each byte of the guid will be + * represented with two hex characters. + */ +static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) +{ + struct hv_device *dev = device_to_hv_device(device); + int ret; + char alias_name[VMBUS_ALIAS_LEN + 1]; + + print_alias_name(dev, alias_name); + ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name); + return ret; +} + +static uuid_le null_guid; + +static inline bool is_null_guid(const __u8 *guid) +{ + if (memcmp(guid, &null_guid, sizeof(uuid_le))) + return false; + return true; +} + +/* + * Return a matching hv_vmbus_device_id pointer. + * If there is no match, return NULL. + */ +static const struct hv_vmbus_device_id *hv_vmbus_get_id( + const struct hv_vmbus_device_id *id, + __u8 *guid) +{ + for (; !is_null_guid(id->guid); id++) + if (!memcmp(&id->guid, guid, sizeof(uuid_le))) + return id; + + return NULL; +} + + + +/* + * vmbus_match - Attempt to match the specified device to the specified driver + */ +static int vmbus_match(struct device *device, struct device_driver *driver) +{ + struct hv_driver *drv = drv_to_hv_drv(driver); + struct hv_device *hv_dev = device_to_hv_device(device); + + if (hv_vmbus_get_id(drv->id_table, hv_dev->dev_type.b)) + return 1; + + return 0; +} + +/* + * vmbus_probe - Add the new vmbus's child device + */ +static int vmbus_probe(struct device *child_device) +{ + int ret = 0; + struct hv_driver *drv = + drv_to_hv_drv(child_device->driver); + struct hv_device *dev = device_to_hv_device(child_device); + const struct hv_vmbus_device_id *dev_id; + + dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b); + if (drv->probe) { + ret = drv->probe(dev, dev_id); + if (ret != 0) + pr_err("probe failed for device %s (%d)\n", + dev_name(child_device), ret); + + } else { + pr_err("probe not set for driver %s\n", + dev_name(child_device)); + ret = -ENODEV; + } + return ret; +} + +/* + * vmbus_remove - Remove a vmbus device + */ +static int vmbus_remove(struct device *child_device) +{ + struct hv_driver *drv = drv_to_hv_drv(child_device->driver); + struct hv_device *dev = device_to_hv_device(child_device); + + if (drv->remove) + drv->remove(dev); + else + pr_err("remove not set for driver %s\n", + dev_name(child_device)); + + return 0; +} + + +/* + * vmbus_shutdown - Shutdown a vmbus device + */ +static void vmbus_shutdown(struct device *child_device) +{ + struct hv_driver *drv; + struct hv_device *dev = device_to_hv_device(child_device); + + + /* The device may not be attached yet */ + if (!child_device->driver) + return; + + drv = drv_to_hv_drv(child_device->driver); + + if (drv->shutdown) + drv->shutdown(dev); + + return; +} + + +/* + * vmbus_device_release - Final callback release of the vmbus child device + */ +static void vmbus_device_release(struct device *device) +{ + struct hv_device *hv_dev = device_to_hv_device(device); + + kfree(hv_dev); + +} + +/* The one and only one */ +static struct bus_type hv_bus = { + .name = "vmbus", + .match = vmbus_match, + .shutdown = vmbus_shutdown, + .remove = vmbus_remove, + .probe = vmbus_probe, + .uevent = vmbus_uevent, + .dev_attrs = vmbus_device_attrs, +}; + +static const char *driver_name = "hyperv"; + + +struct onmessage_work_context { + struct work_struct work; + struct hv_message msg; +}; + +static void vmbus_onmessage_work(struct work_struct *work) +{ + struct onmessage_work_context *ctx; + + ctx = container_of(work, struct onmessage_work_context, + work); + vmbus_onmessage(&ctx->msg); + kfree(ctx); +} + +static void vmbus_on_msg_dpc(unsigned long data) +{ + int cpu = smp_processor_id(); + void *page_addr = hv_context.synic_message_page[cpu]; + struct hv_message *msg = (struct hv_message *)page_addr + + VMBUS_MESSAGE_SINT; + struct onmessage_work_context *ctx; + + while (1) { + if (msg->header.message_type == HVMSG_NONE) { + /* no msg */ + break; + } else { + ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); + if (ctx == NULL) + continue; + INIT_WORK(&ctx->work, vmbus_onmessage_work); + memcpy(&ctx->msg, msg, sizeof(*msg)); + queue_work(vmbus_connection.work_queue, &ctx->work); + } + + msg->header.message_type = HVMSG_NONE; + + /* + * Make sure the write to MessageType (ie set to + * HVMSG_NONE) happens before we read the + * MessagePending and EOMing. Otherwise, the EOMing + * will not deliver any more messages since there is + * no empty slot + */ + smp_mb(); + + if (msg->header.message_flags.msg_pending) { + /* + * This will cause message queue rescan to + * possibly deliver another msg from the + * hypervisor + */ + wrmsrl(HV_X64_MSR_EOM, 0); + } + } +} + +static irqreturn_t vmbus_isr(int irq, void *dev_id) +{ + int cpu = smp_processor_id(); + void *page_addr; + struct hv_message *msg; + union hv_synic_event_flags *event; + bool handled = false; + + /* + * Check for events before checking for messages. This is the order + * in which events and messages are checked in Windows guests on + * Hyper-V, and the Windows team suggested we do the same. + */ + + page_addr = hv_context.synic_event_page[cpu]; + event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; + + /* Since we are a child, we only need to check bit 0 */ + if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { + handled = true; + tasklet_schedule(&event_dpc); + } + + page_addr = hv_context.synic_message_page[cpu]; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + + /* Check if there are actual msgs to be processed */ + if (msg->header.message_type != HVMSG_NONE) { + handled = true; + tasklet_schedule(&msg_dpc); + } + + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +/* + * vmbus_bus_init -Main vmbus driver initialization routine. + * + * Here, we + * - initialize the vmbus driver context + * - invoke the vmbus hv main init routine + * - get the irq resource + * - retrieve the channel offers + */ +static int vmbus_bus_init(int irq) +{ + int ret; + unsigned int vector; + + /* Hypervisor initialization...setup hypercall page..etc */ + ret = hv_init(); + if (ret != 0) { + pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); + return ret; + } + + tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); + tasklet_init(&event_dpc, vmbus_on_event, 0); + + ret = bus_register(&hv_bus); + if (ret) + goto err_cleanup; + + ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM, + driver_name, hv_acpi_dev); + + if (ret != 0) { + pr_err("Unable to request IRQ %d\n", + irq); + goto err_unregister; + } + + vector = IRQ0_VECTOR + irq; + + /* + * Notify the hypervisor of our irq and + * connect to the host. + */ + on_each_cpu(hv_synic_init, (void *)&vector, 1); + ret = vmbus_connect(); + if (ret) + goto err_irq; + + vmbus_request_offers(); + + return 0; + +err_irq: + free_irq(irq, hv_acpi_dev); + +err_unregister: + bus_unregister(&hv_bus); + +err_cleanup: + hv_cleanup(); + + return ret; +} + +/** + * __vmbus_child_driver_register - Register a vmbus's driver + * @drv: Pointer to driver structure you want to register + * @owner: owner module of the drv + * @mod_name: module name string + * + * Registers the given driver with Linux through the 'driver_register()' call + * and sets up the hyper-v vmbus handling for this driver. + * It will return the state of the 'driver_register()' call. + * + */ +int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name) +{ + int ret; + + pr_info("registering driver %s\n", hv_driver->name); + + hv_driver->driver.name = hv_driver->name; + hv_driver->driver.owner = owner; + hv_driver->driver.mod_name = mod_name; + hv_driver->driver.bus = &hv_bus; + + ret = driver_register(&hv_driver->driver); + + vmbus_request_offers(); + + return ret; +} +EXPORT_SYMBOL_GPL(__vmbus_driver_register); + +/** + * vmbus_driver_unregister() - Unregister a vmbus's driver + * @drv: Pointer to driver structure you want to un-register + * + * Un-register the given driver that was previous registered with a call to + * vmbus_driver_register() + */ +void vmbus_driver_unregister(struct hv_driver *hv_driver) +{ + pr_info("unregistering driver %s\n", hv_driver->name); + + driver_unregister(&hv_driver->driver); + +} +EXPORT_SYMBOL_GPL(vmbus_driver_unregister); + +/* + * vmbus_device_create - Creates and registers a new child device + * on the vmbus. + */ +struct hv_device *vmbus_device_create(uuid_le *type, + uuid_le *instance, + struct vmbus_channel *channel) +{ + struct hv_device *child_device_obj; + + child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); + if (!child_device_obj) { + pr_err("Unable to allocate device object for child device\n"); + return NULL; + } + + child_device_obj->channel = channel; + memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le)); + memcpy(&child_device_obj->dev_instance, instance, + sizeof(uuid_le)); + + + return child_device_obj; +} + +/* + * vmbus_device_register - Register the child device + */ +int vmbus_device_register(struct hv_device *child_device_obj) +{ + int ret = 0; + + static atomic_t device_num = ATOMIC_INIT(0); + + dev_set_name(&child_device_obj->device, "vmbus_0_%d", + atomic_inc_return(&device_num)); + + child_device_obj->device.bus = &hv_bus; + child_device_obj->device.parent = &hv_acpi_dev->dev; + child_device_obj->device.release = vmbus_device_release; + + /* + * Register with the LDM. This will kick off the driver/device + * binding...which will eventually call vmbus_match() and vmbus_probe() + */ + ret = device_register(&child_device_obj->device); + + if (ret) + pr_err("Unable to register child device\n"); + else + pr_info("child device %s registered\n", + dev_name(&child_device_obj->device)); + + return ret; +} + +/* + * vmbus_device_unregister - Remove the specified child device + * from the vmbus. + */ +void vmbus_device_unregister(struct hv_device *device_obj) +{ + /* + * Kick off the process of unregistering the device. + * This will call vmbus_remove() and eventually vmbus_device_release() + */ + device_unregister(&device_obj->device); + + pr_info("child device %s unregistered\n", + dev_name(&device_obj->device)); +} + + +/* + * VMBUS is an acpi enumerated device. Get the the IRQ information + * from DSDT. + */ + +static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) +{ + + if (res->type == ACPI_RESOURCE_TYPE_IRQ) { + struct acpi_resource_irq *irqp; + irqp = &res->data.irq; + + *((unsigned int *)irq) = irqp->interrupts[0]; + } + + return AE_OK; +} + +static int vmbus_acpi_add(struct acpi_device *device) +{ + acpi_status result; + + hv_acpi_dev = device; + + result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + vmbus_walk_resources, &irq); + + if (ACPI_FAILURE(result)) { + complete(&probe_event); + return -ENODEV; + } + complete(&probe_event); + return 0; +} + +static const struct acpi_device_id vmbus_acpi_device_ids[] = { + {"VMBUS", 0}, + {"VMBus", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); + +static struct acpi_driver vmbus_acpi_driver = { + .name = "vmbus", + .ids = vmbus_acpi_device_ids, + .ops = { + .add = vmbus_acpi_add, + }, +}; + +static int __init hv_acpi_init(void) +{ + int ret, t; + + init_completion(&probe_event); + + /* + * Get irq resources first. + */ + + ret = acpi_bus_register_driver(&vmbus_acpi_driver); + + if (ret) + return ret; + + t = wait_for_completion_timeout(&probe_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (irq <= 0) { + ret = -ENODEV; + goto cleanup; + } + + ret = vmbus_bus_init(irq); + if (ret) + goto cleanup; + + return 0; + +cleanup: + acpi_bus_unregister_driver(&vmbus_acpi_driver); + return ret; +} + + +MODULE_LICENSE("GPL"); +MODULE_VERSION(HV_DRV_VERSION); +module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); + +module_init(hv_acpi_init); diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig index 815f8c2f7cdc..072185ebe95b 100644 --- a/drivers/staging/hv/Kconfig +++ b/drivers/staging/hv/Kconfig @@ -1,39 +1,17 @@ -config HYPERV - tristate "Microsoft Hyper-V client drivers" - depends on X86 && ACPI && PCI - default n - help - Select this option to run Linux as a Hyper-V client operating - system. - -if HYPERV - config HYPERV_STORAGE tristate "Microsoft Hyper-V virtual storage driver" - depends on SCSI - default HYPERV + depends on HYPERV && SCSI help Select this option to enable the Hyper-V virtual storage driver. config HYPERV_NET tristate "Microsoft Hyper-V virtual network driver" - depends on NET - default HYPERV + depends on HYPERV && NET help Select this option to enable the Hyper-V virtual network driver. -config HYPERV_UTILS - tristate "Microsoft Hyper-V Utilities driver" - depends on CONNECTOR && NLS - default HYPERV - help - Select this option to enable the Hyper-V Utilities. - config HYPERV_MOUSE tristate "Microsoft Hyper-V mouse driver" - depends on HID - default HYPERV + depends on HYPERV && HID help Select this option to enable the Hyper-V mouse driver. - -endif diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index bd176b1f231e..e071c12c8f69 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -1,12 +1,7 @@ -obj-$(CONFIG_HYPERV) += hv_vmbus.o hv_timesource.o +obj-$(CONFIG_HYPERV) += hv_timesource.o obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o -obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o -hv_vmbus-y := vmbus_drv.o \ - hv.o connection.o channel.o \ - channel_mgmt.o ring_buffer.o hv_storvsc-y := storvsc_drv.o hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o -hv_utils-y := hv_util.o hv_kvp.o diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c deleted file mode 100644 index b6f3d38a6dbb..000000000000 --- a/drivers/staging/hv/channel.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - -#define NUM_PAGES_SPANNED(addr, len) \ -((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) - -/* Internal routines */ -static int create_gpadl_header( - void *kbuffer, /* must be phys and virt contiguous */ - u32 size, /* page-size multiple */ - struct vmbus_channel_msginfo **msginfo, - u32 *messagecount); -static void vmbus_setevent(struct vmbus_channel *channel); - -/* - * vmbus_setevent- Trigger an event notification on the specified - * channel. - */ -static void vmbus_setevent(struct vmbus_channel *channel) -{ - struct hv_monitor_page *monitorpage; - - if (channel->offermsg.monitor_allocated) { - /* Each u32 represents 32 channels */ - sync_set_bit(channel->offermsg.child_relid & 31, - (unsigned long *) vmbus_connection.send_int_page + - (channel->offermsg.child_relid >> 5)); - - monitorpage = vmbus_connection.monitor_pages; - monitorpage++; /* Get the child to parent monitor page */ - - sync_set_bit(channel->monitor_bit, - (unsigned long *)&monitorpage->trigger_group - [channel->monitor_grp].pending); - - } else { - vmbus_set_event(channel->offermsg.child_relid); - } -} - -/* - * vmbus_get_debug_info -Retrieve various channel debug info - */ -void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debuginfo) -{ - struct hv_monitor_page *monitorpage; - u8 monitor_group = (u8)channel->offermsg.monitorid / 32; - u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - - debuginfo->relid = channel->offermsg.child_relid; - debuginfo->state = channel->state; - memcpy(&debuginfo->interfacetype, - &channel->offermsg.offer.if_type, sizeof(uuid_le)); - memcpy(&debuginfo->interface_instance, - &channel->offermsg.offer.if_instance, - sizeof(uuid_le)); - - monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; - - debuginfo->monitorid = channel->offermsg.monitorid; - - debuginfo->servermonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->servermonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->servermonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - monitorpage++; - - debuginfo->clientmonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->clientmonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->clientmonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); - hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); -} - -/* - * vmbus_open - Open the specified channel. - */ -int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, - u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, - void (*onchannelcallback)(void *context), void *context) -{ - struct vmbus_channel_open_channel *open_msg; - struct vmbus_channel_msginfo *open_info = NULL; - void *in, *out; - unsigned long flags; - int ret, t, err = 0; - - newchannel->onchannel_callback = onchannelcallback; - newchannel->channel_callback_context = context; - - /* Allocate the ring buffer */ - out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, - get_order(send_ringbuffer_size + recv_ringbuffer_size)); - - if (!out) - return -ENOMEM; - - - in = (void *)((unsigned long)out + send_ringbuffer_size); - - newchannel->ringbuffer_pages = out; - newchannel->ringbuffer_pagecount = (send_ringbuffer_size + - recv_ringbuffer_size) >> PAGE_SHIFT; - - ret = hv_ringbuffer_init( - &newchannel->outbound, out, send_ringbuffer_size); - - if (ret != 0) { - err = ret; - goto errorout; - } - - ret = hv_ringbuffer_init( - &newchannel->inbound, in, recv_ringbuffer_size); - if (ret != 0) { - err = ret; - goto errorout; - } - - - /* Establish the gpadl for the ring buffer */ - newchannel->ringbuffer_gpadlhandle = 0; - - ret = vmbus_establish_gpadl(newchannel, - newchannel->outbound.ring_buffer, - send_ringbuffer_size + - recv_ringbuffer_size, - &newchannel->ringbuffer_gpadlhandle); - - if (ret != 0) { - err = ret; - goto errorout; - } - - /* Create and init the channel open message */ - open_info = kmalloc(sizeof(*open_info) + - sizeof(struct vmbus_channel_open_channel), - GFP_KERNEL); - if (!open_info) { - err = -ENOMEM; - goto errorout; - } - - init_completion(&open_info->waitevent); - - open_msg = (struct vmbus_channel_open_channel *)open_info->msg; - open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; - open_msg->openid = newchannel->offermsg.child_relid; - open_msg->child_relid = newchannel->offermsg.child_relid; - open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; - open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> - PAGE_SHIFT; - open_msg->server_contextarea_gpadlhandle = 0; - - if (userdatalen > MAX_USER_DEFINED_BYTES) { - err = -EINVAL; - goto errorout; - } - - if (userdatalen) - memcpy(open_msg->userdata, userdata, userdatalen); - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&open_info->msglistentry, - &vmbus_connection.chn_msg_list); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(open_msg, - sizeof(struct vmbus_channel_open_channel)); - - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); - if (t == 0) { - err = -ETIMEDOUT; - goto errorout; - } - - - if (open_info->response.open_result.status) - err = open_info->response.open_result.status; - -cleanup: - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&open_info->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(open_info); - return err; - -errorout: - hv_ringbuffer_cleanup(&newchannel->outbound); - hv_ringbuffer_cleanup(&newchannel->inbound); - free_pages((unsigned long)out, - get_order(send_ringbuffer_size + recv_ringbuffer_size)); - kfree(open_info); - return err; -} -EXPORT_SYMBOL_GPL(vmbus_open); - -/* - * create_gpadl_header - Creates a gpadl for the specified buffer - */ -static int create_gpadl_header(void *kbuffer, u32 size, - struct vmbus_channel_msginfo **msginfo, - u32 *messagecount) -{ - int i; - int pagecount; - unsigned long long pfn; - struct vmbus_channel_gpadl_header *gpadl_header; - struct vmbus_channel_gpadl_body *gpadl_body; - struct vmbus_channel_msginfo *msgheader; - struct vmbus_channel_msginfo *msgbody = NULL; - u32 msgsize; - - int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; - - pagecount = size >> PAGE_SHIFT; - pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; - - /* do we need a gpadl body msg */ - pfnsize = MAX_SIZE_CHANNEL_MESSAGE - - sizeof(struct vmbus_channel_gpadl_header) - - sizeof(struct gpa_range); - pfncount = pfnsize / sizeof(u64); - - if (pagecount > pfncount) { - /* we need a gpadl body */ - /* fill in the header */ - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_header) + - sizeof(struct gpa_range) + pfncount * sizeof(u64); - msgheader = kzalloc(msgsize, GFP_KERNEL); - if (!msgheader) - goto nomem; - - INIT_LIST_HEAD(&msgheader->submsglist); - msgheader->msgsize = msgsize; - - gpadl_header = (struct vmbus_channel_gpadl_header *) - msgheader->msg; - gpadl_header->rangecount = 1; - gpadl_header->range_buflen = sizeof(struct gpa_range) + - pagecount * sizeof(u64); - gpadl_header->range[0].byte_offset = 0; - gpadl_header->range[0].byte_count = size; - for (i = 0; i < pfncount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; - *msginfo = msgheader; - *messagecount = 1; - - pfnsum = pfncount; - pfnleft = pagecount - pfncount; - - /* how many pfns can we fit */ - pfnsize = MAX_SIZE_CHANNEL_MESSAGE - - sizeof(struct vmbus_channel_gpadl_body); - pfncount = pfnsize / sizeof(u64); - - /* fill in the body */ - while (pfnleft) { - if (pfnleft > pfncount) - pfncurr = pfncount; - else - pfncurr = pfnleft; - - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_body) + - pfncurr * sizeof(u64); - msgbody = kzalloc(msgsize, GFP_KERNEL); - - if (!msgbody) { - struct vmbus_channel_msginfo *pos = NULL; - struct vmbus_channel_msginfo *tmp = NULL; - /* - * Free up all the allocated messages. - */ - list_for_each_entry_safe(pos, tmp, - &msgheader->submsglist, - msglistentry) { - - list_del(&pos->msglistentry); - kfree(pos); - } - - goto nomem; - } - - msgbody->msgsize = msgsize; - (*messagecount)++; - gpadl_body = - (struct vmbus_channel_gpadl_body *)msgbody->msg; - - /* - * Gpadl is u32 and we are using a pointer which could - * be 64-bit - * This is governed by the guest/host protocol and - * so the hypervisor gurantees that this is ok. - */ - for (i = 0; i < pfncurr; i++) - gpadl_body->pfn[i] = pfn + pfnsum + i; - - /* add to msg header */ - list_add_tail(&msgbody->msglistentry, - &msgheader->submsglist); - pfnsum += pfncurr; - pfnleft -= pfncurr; - } - } else { - /* everything fits in a header */ - msgsize = sizeof(struct vmbus_channel_msginfo) + - sizeof(struct vmbus_channel_gpadl_header) + - sizeof(struct gpa_range) + pagecount * sizeof(u64); - msgheader = kzalloc(msgsize, GFP_KERNEL); - if (msgheader == NULL) - goto nomem; - msgheader->msgsize = msgsize; - - gpadl_header = (struct vmbus_channel_gpadl_header *) - msgheader->msg; - gpadl_header->rangecount = 1; - gpadl_header->range_buflen = sizeof(struct gpa_range) + - pagecount * sizeof(u64); - gpadl_header->range[0].byte_offset = 0; - gpadl_header->range[0].byte_count = size; - for (i = 0; i < pagecount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; - - *msginfo = msgheader; - *messagecount = 1; - } - - return 0; -nomem: - kfree(msgheader); - kfree(msgbody); - return -ENOMEM; -} - -/* - * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer - * - * @channel: a channel - * @kbuffer: from kmalloc - * @size: page-size multiple - * @gpadl_handle: some funky thing - */ -int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, - u32 size, u32 *gpadl_handle) -{ - struct vmbus_channel_gpadl_header *gpadlmsg; - struct vmbus_channel_gpadl_body *gpadl_body; - struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_msginfo *submsginfo; - u32 msgcount; - struct list_head *curr; - u32 next_gpadl_handle; - unsigned long flags; - int ret = 0; - int t; - - next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); - atomic_inc(&vmbus_connection.next_gpadl_handle); - - ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); - if (ret) - return ret; - - init_completion(&msginfo->waitevent); - - gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; - gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; - gpadlmsg->child_relid = channel->offermsg.child_relid; - gpadlmsg->gpadl = next_gpadl_handle; - - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&msginfo->msglistentry, - &vmbus_connection.chn_msg_list); - - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - - sizeof(*msginfo)); - if (ret != 0) - goto cleanup; - - if (msgcount > 1) { - list_for_each(curr, &msginfo->submsglist) { - - submsginfo = (struct vmbus_channel_msginfo *)curr; - gpadl_body = - (struct vmbus_channel_gpadl_body *)submsginfo->msg; - - gpadl_body->header.msgtype = - CHANNELMSG_GPADL_BODY; - gpadl_body->gpadl = next_gpadl_handle; - - ret = vmbus_post_msg(gpadl_body, - submsginfo->msgsize - - sizeof(*submsginfo)); - if (ret != 0) - goto cleanup; - - } - } - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - BUG_ON(t == 0); - - - /* At this point, we received the gpadl created msg */ - *gpadl_handle = gpadlmsg->gpadl; - -cleanup: - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(msginfo); - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); - -/* - * vmbus_teardown_gpadl -Teardown the specified GPADL handle - */ -int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) -{ - struct vmbus_channel_gpadl_teardown *msg; - struct vmbus_channel_msginfo *info; - unsigned long flags; - int ret, t; - - info = kmalloc(sizeof(*info) + - sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); - if (!info) - return -ENOMEM; - - init_completion(&info->waitevent); - - msg = (struct vmbus_channel_gpadl_teardown *)info->msg; - - msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; - msg->child_relid = channel->offermsg.child_relid; - msg->gpadl = gpadl_handle; - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&info->msglistentry, - &vmbus_connection.chn_msg_list); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_gpadl_teardown)); - - BUG_ON(ret != 0); - t = wait_for_completion_timeout(&info->waitevent, 5*HZ); - BUG_ON(t == 0); - - /* Received a torndown response */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&info->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - kfree(info); - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); - -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) -{ - struct vmbus_channel_close_channel *msg; - int ret; - unsigned long flags; - - /* Stop callback and cancel the timer asap */ - spin_lock_irqsave(&channel->inbound_lock, flags); - channel->onchannel_callback = NULL; - spin_unlock_irqrestore(&channel->inbound_lock, flags); - - /* Send a closing message */ - - msg = &channel->close_msg.msg; - - msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; - msg->child_relid = channel->offermsg.child_relid; - - ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); - - BUG_ON(ret != 0); - /* Tear down the gpadl for the channel's ring buffer */ - if (channel->ringbuffer_gpadlhandle) - vmbus_teardown_gpadl(channel, - channel->ringbuffer_gpadlhandle); - - /* Cleanup the ring buffers for this channel */ - hv_ringbuffer_cleanup(&channel->outbound); - hv_ringbuffer_cleanup(&channel->inbound); - - free_pages((unsigned long)channel->ringbuffer_pages, - get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); - - -} -EXPORT_SYMBOL_GPL(vmbus_close); - -/** - * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. - * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_sendpacket(struct vmbus_channel *channel, const 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]; - u64 aligned_data = 0; - int ret; - - - /* Setup the descriptor */ - desc.type = type; /* VmbusPacketTypeDataInBand; */ - desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ - /* in 8-bytes granularity */ - desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; - 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); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL(vmbus_sendpacket); - -/* - * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer - * packets using a GPADL Direct packet type. - */ -int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, void *buffer, u32 bufferlen, - u64 requestid) -{ - int ret; - int i; - struct vmbus_channel_packet_page_buffer desc; - u32 descsize; - u32 packetlen; - u32 packetlen_aligned; - struct scatterlist bufferlist[3]; - u64 aligned_data = 0; - - if (pagecount > MAX_PAGE_BUFFER_COUNT) - return -EINVAL; - - - /* - * Adjust the size down since vmbus_channel_packet_page_buffer is the - * largest size we support - */ - descsize = sizeof(struct vmbus_channel_packet_page_buffer) - - ((MAX_PAGE_BUFFER_COUNT - pagecount) * - sizeof(struct hv_page_buffer)); - packetlen = descsize + bufferlen; - packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - - /* Setup the descriptor */ - desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; - desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ - desc.length8 = (u16)(packetlen_aligned >> 3); - desc.transactionid = requestid; - desc.rangecount = pagecount; - - for (i = 0; i < pagecount; i++) { - desc.range[i].len = pagebuffers[i].len; - desc.range[i].offset = pagebuffers[i].offset; - 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); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); - -/* - * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet - * using a GPADL Direct packet type. - */ -int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, - struct hv_multipage_buffer *multi_pagebuffer, - void *buffer, u32 bufferlen, u64 requestid) -{ - int ret; - struct vmbus_channel_packet_multipage_buffer desc; - u32 descsize; - u32 packetlen; - u32 packetlen_aligned; - struct scatterlist bufferlist[3]; - u64 aligned_data = 0; - u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, - multi_pagebuffer->len); - - - if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) - return -EINVAL; - - /* - * Adjust the size down since vmbus_channel_packet_multipage_buffer is - * the largest size we support - */ - descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - - ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * - sizeof(u64)); - packetlen = descsize + bufferlen; - packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - - - /* Setup the descriptor */ - desc.type = VM_PKT_DATA_USING_GPA_DIRECT; - desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; - desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ - desc.length8 = (u16)(packetlen_aligned >> 3); - desc.transactionid = requestid; - desc.rangecount = 1; - - desc.range.len = multi_pagebuffer->len; - desc.range.offset = multi_pagebuffer->offset; - - 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); - - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); - - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) - vmbus_setevent(channel); - - return ret; -} -EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); - -/** - * vmbus_recvpacket() - Retrieve the user packet on the specified channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @buffer_actual_len: The actual size of the data after it was received - * @requestid: Identifier of the request - * - * Receives directly from the hyper-v vmbus and puts the data it received - * into Buffer. This will receive the data unparsed from hyper-v. - * - * Mainly used by Hyper-V drivers. - */ -int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) -{ - struct vmpacket_descriptor desc; - u32 packetlen; - u32 userlen; - int ret; - - *buffer_actual_len = 0; - *requestid = 0; - - - ret = hv_ringbuffer_peek(&channel->inbound, &desc, - sizeof(struct vmpacket_descriptor)); - if (ret != 0) - return 0; - - packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); - - *buffer_actual_len = userlen; - - if (userlen > bufferlen) { - - pr_err("Buffer too small - got %d needs %d\n", - bufferlen, userlen); - return -ETOOSMALL; - } - - *requestid = desc.trans_id; - - /* Copy over the packet to the user buffer */ - ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, - (desc.offset8 << 3)); - - - return 0; -} -EXPORT_SYMBOL(vmbus_recvpacket); - -/* - * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel - */ -int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, - u32 bufferlen, u32 *buffer_actual_len, - u64 *requestid) -{ - struct vmpacket_descriptor desc; - u32 packetlen; - u32 userlen; - int ret; - - *buffer_actual_len = 0; - *requestid = 0; - - - ret = hv_ringbuffer_peek(&channel->inbound, &desc, - sizeof(struct vmpacket_descriptor)); - if (ret != 0) - return 0; - - - packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); - - *buffer_actual_len = packetlen; - - if (packetlen > bufferlen) { - pr_err("Buffer too small - needed %d bytes but " - "got space for only %d bytes\n", - packetlen, bufferlen); - return -ENOBUFS; - } - - *requestid = desc.trans_id; - - /* Copy over the entire packet to the user buffer */ - ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); - - return 0; -} -EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c deleted file mode 100644 index 9f007522a9d5..000000000000 --- a/drivers/staging/hv/channel_mgmt.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - -struct vmbus_channel_message_table_entry { - enum vmbus_channel_message_type message_type; - void (*message_handler)(struct vmbus_channel_message_header *msg); -}; - -#define MAX_MSG_TYPES 4 -#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 - -static const uuid_le - supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { - /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ - /* Storage - SCSI */ - { - .b = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } - }, - - /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ - /* Network */ - { - .b = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } - }, - - /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ - /* Input */ - { - .b = { - 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A - } - }, - - /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ - /* IDE */ - { - .b = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } - }, - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .b = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - } - }, - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .b = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - } - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .b = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - } - }, - /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ - /* KVP */ - { - .b = { - 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 - } - }, - -}; - - -/** - * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message - * @icmsghdrp: Pointer to msg header structure - * @icmsg_negotiate: Pointer to negotiate message structure - * @buf: Raw buffer channel data - * - * @icmsghdrp is of type &struct icmsg_hdr. - * @negop is of type &struct icmsg_negotiate. - * Set up and fill in default negotiate response message. This response can - * come from both the vmbus driver and the hv_utils driver. The current api - * will respond properly to both Windows 2008 and Windows 2008-R2 operating - * systems. - * - * Mainly used by Hyper-V drivers. - */ -void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - struct icmsg_negotiate *negop, - u8 *buf) -{ - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - icmsghdrp->icmsgsize = 0x10; - - negop = (struct icmsg_negotiate *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icframe_vercnt == 2 && - negop->icversion_data[1].major == 3) { - negop->icversion_data[0].major = 3; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 3; - negop->icversion_data[1].minor = 0; - } else { - negop->icversion_data[0].major = 1; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = 1; - negop->icversion_data[1].minor = 0; - } - - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - } -} -EXPORT_SYMBOL(prep_negotiate_resp); - -/* - * alloc_channel - Allocate and initialize a vmbus channel object - */ -static struct vmbus_channel *alloc_channel(void) -{ - struct vmbus_channel *channel; - - channel = kzalloc(sizeof(*channel), GFP_ATOMIC); - if (!channel) - return NULL; - - spin_lock_init(&channel->inbound_lock); - - channel->controlwq = create_workqueue("hv_vmbus_ctl"); - if (!channel->controlwq) { - kfree(channel); - return NULL; - } - - return channel; -} - -/* - * release_hannel - Release the vmbus channel object itself - */ -static void release_channel(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - destroy_workqueue(channel->controlwq); - - kfree(channel); -} - -/* - * free_channel - Release the resources used by the vmbus channel object - */ -void free_channel(struct vmbus_channel *channel) -{ - - /* - * We have to release the channel's workqueue/thread in the vmbus's - * workqueue/thread context - * ie we can't destroy ourselves. - */ - INIT_WORK(&channel->work, release_channel); - queue_work(vmbus_connection.work_queue, &channel->work); -} - - - -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - - vmbus_device_unregister(channel->device_obj); -} - -/* - * vmbus_process_offer - Process the offer by creating a channel/device - * associated with this offer - */ -static void vmbus_process_offer(struct work_struct *work) -{ - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - struct vmbus_channel *channel; - bool fnew = true; - int ret; - unsigned long flags; - - /* The next possible work is rescind handling */ - INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); - - /* Make sure this is a new offer */ - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - - list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { - if (!uuid_le_cmp(channel->offermsg.offer.if_type, - newchannel->offermsg.offer.if_type) && - !uuid_le_cmp(channel->offermsg.offer.if_instance, - newchannel->offermsg.offer.if_instance)) { - fnew = false; - break; - } - } - - if (fnew) - list_add_tail(&newchannel->listentry, - &vmbus_connection.chn_list); - - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - if (!fnew) { - free_channel(newchannel); - return; - } - - /* - * Start the process of binding this offer to the driver - * We need to set the DeviceObject field before calling - * vmbus_child_dev_add() - */ - newchannel->device_obj = vmbus_device_create( - &newchannel->offermsg.offer.if_type, - &newchannel->offermsg.offer.if_instance, - newchannel); - - /* - * Add the new device to the bus. This will kick off device-driver - * binding which eventually invokes the device driver's AddDevice() - * method. - */ - ret = vmbus_device_register(newchannel->device_obj); - if (ret != 0) { - pr_err("unable to add child device object (relid %d)\n", - newchannel->offermsg.child_relid); - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&newchannel->listentry); - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - free_channel(newchannel); - } else { - /* - * This state is used to indicate a successful open - * so that when we do close the channel normally, we - * can cleanup properly - */ - newchannel->state = CHANNEL_OPEN_STATE; - } -} - -/* - * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. - * - */ -static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_offer_channel *offer; - struct vmbus_channel *newchannel; - uuid_le *guidtype; - uuid_le *guidinstance; - int i; - int fsupported = 0; - - offer = (struct vmbus_channel_offer_channel *)hdr; - for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { - if (!uuid_le_cmp(offer->offer.if_type, - supported_device_classes[i])) { - fsupported = 1; - break; - } - } - - if (!fsupported) - return; - - guidtype = &offer->offer.if_type; - guidinstance = &offer->offer.if_instance; - - /* Allocate the channel object and save this offer. */ - newchannel = alloc_channel(); - if (!newchannel) { - pr_err("Unable to allocate channel object\n"); - return; - } - - memcpy(&newchannel->offermsg, offer, - sizeof(struct vmbus_channel_offer_channel)); - newchannel->monitor_grp = (u8)offer->monitorid / 32; - newchannel->monitor_bit = (u8)offer->monitorid % 32; - - INIT_WORK(&newchannel->work, vmbus_process_offer); - queue_work(newchannel->controlwq, &newchannel->work); -} - -/* - * vmbus_onoffer_rescind - Rescind offer handler. - * - * We queue a work item to process this offer synchronously - */ -static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_rescind_offer *rescind; - struct vmbus_channel *channel; - - rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = relid2channel(rescind->child_relid); - - if (channel == NULL) - /* Just return here, no channel found */ - return; - - /* work is initialized for vmbus_process_rescind_offer() from - * vmbus_process_offer() where the channel got created */ - queue_work(channel->controlwq, &channel->work); -} - -/* - * vmbus_onoffers_delivered - - * This is invoked when all offers have been delivered. - * - * Nothing to do here. - */ -static void vmbus_onoffers_delivered( - struct vmbus_channel_message_header *hdr) -{ -} - -/* - * vmbus_onopen_result - Open result handler. - * - * This is invoked when we received a response to our channel open request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_open_result *result; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_open_channel *openmsg; - unsigned long flags; - - result = (struct vmbus_channel_open_result *)hdr; - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { - openmsg = - (struct vmbus_channel_open_channel *)msginfo->msg; - if (openmsg->child_relid == result->child_relid && - openmsg->openid == result->openid) { - memcpy(&msginfo->response.open_result, - result, - sizeof( - struct vmbus_channel_open_result)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_ongpadl_created - GPADL created handler. - * - * This is invoked when we received a response to our gpadl create request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_created *gpadlcreated; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_gpadl_header *gpadlheader; - unsigned long flags; - - gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; - - /* - * Find the establish msg, copy the result and signal/unblock the wait - * event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { - gpadlheader = - (struct vmbus_channel_gpadl_header *)requestheader; - - if ((gpadlcreated->child_relid == - gpadlheader->child_relid) && - (gpadlcreated->gpadl == gpadlheader->gpadl)) { - memcpy(&msginfo->response.gpadl_created, - gpadlcreated, - sizeof( - struct vmbus_channel_gpadl_created)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_ongpadl_torndown - GPADL torndown handler. - * - * This is invoked when we received a response to our gpadl teardown request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_ongpadl_torndown( - struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_gpadl_torndown *gpadl_torndown; - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_gpadl_teardown *gpadl_teardown; - unsigned long flags; - - gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; - - /* - * Find the open msg, copy the result and signal/unblock the wait event - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { - gpadl_teardown = - (struct vmbus_channel_gpadl_teardown *)requestheader; - - if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { - memcpy(&msginfo->response.gpadl_torndown, - gpadl_torndown, - sizeof( - struct vmbus_channel_gpadl_torndown)); - complete(&msginfo->waitevent); - break; - } - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* - * vmbus_onversion_response - Version response handler - * - * This is invoked when we received a response to our initiate contact request. - * Find the matching request, copy the response and signal the requesting - * thread. - */ -static void vmbus_onversion_response( - struct vmbus_channel_message_header *hdr) -{ - struct vmbus_channel_msginfo *msginfo; - struct vmbus_channel_message_header *requestheader; - struct vmbus_channel_initiate_contact *initiate; - struct vmbus_channel_version_response *version_response; - unsigned long flags; - - version_response = (struct vmbus_channel_version_response *)hdr; - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - - list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, - msglistentry) { - requestheader = - (struct vmbus_channel_message_header *)msginfo->msg; - - if (requestheader->msgtype == - CHANNELMSG_INITIATE_CONTACT) { - initiate = - (struct vmbus_channel_initiate_contact *)requestheader; - memcpy(&msginfo->response.version_response, - version_response, - sizeof(struct vmbus_channel_version_response)); - complete(&msginfo->waitevent); - } - } - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); -} - -/* Channel message dispatch table */ -static struct vmbus_channel_message_table_entry - channel_message_table[CHANNELMSG_COUNT] = { - {CHANNELMSG_INVALID, NULL}, - {CHANNELMSG_OFFERCHANNEL, vmbus_onoffer}, - {CHANNELMSG_RESCIND_CHANNELOFFER, vmbus_onoffer_rescind}, - {CHANNELMSG_REQUESTOFFERS, NULL}, - {CHANNELMSG_ALLOFFERS_DELIVERED, vmbus_onoffers_delivered}, - {CHANNELMSG_OPENCHANNEL, NULL}, - {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result}, - {CHANNELMSG_CLOSECHANNEL, NULL}, - {CHANNELMSG_GPADL_HEADER, NULL}, - {CHANNELMSG_GPADL_BODY, NULL}, - {CHANNELMSG_GPADL_CREATED, vmbus_ongpadl_created}, - {CHANNELMSG_GPADL_TEARDOWN, NULL}, - {CHANNELMSG_GPADL_TORNDOWN, vmbus_ongpadl_torndown}, - {CHANNELMSG_RELID_RELEASED, NULL}, - {CHANNELMSG_INITIATE_CONTACT, NULL}, - {CHANNELMSG_VERSION_RESPONSE, vmbus_onversion_response}, - {CHANNELMSG_UNLOAD, NULL}, -}; - -/* - * vmbus_onmessage - Handler for channel protocol messages. - * - * This is invoked in the vmbus worker thread context. - */ -void vmbus_onmessage(void *context) -{ - struct hv_message *msg = context; - struct vmbus_channel_message_header *hdr; - int size; - - hdr = (struct vmbus_channel_message_header *)msg->u.payload; - size = msg->header.payload_size; - - if (hdr->msgtype >= CHANNELMSG_COUNT) { - pr_err("Received invalid channel message type %d size %d\n", - hdr->msgtype, size); - print_hex_dump_bytes("", DUMP_PREFIX_NONE, - (unsigned char *)msg->u.payload, size); - return; - } - - if (channel_message_table[hdr->msgtype].message_handler) - channel_message_table[hdr->msgtype].message_handler(hdr); - else - pr_err("Unhandled channel message type %d\n", hdr->msgtype); -} - -/* - * vmbus_request_offers - Send a request to get all our pending offers. - */ -int vmbus_request_offers(void) -{ - struct vmbus_channel_message_header *msg; - struct vmbus_channel_msginfo *msginfo; - int ret, t; - - msginfo = kmalloc(sizeof(*msginfo) + - sizeof(struct vmbus_channel_message_header), - GFP_KERNEL); - if (!msginfo) - return -ENOMEM; - - init_completion(&msginfo->waitevent); - - msg = (struct vmbus_channel_message_header *)msginfo->msg; - - msg->msgtype = CHANNELMSG_REQUESTOFFERS; - - - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_message_header)); - if (ret != 0) { - pr_err("Unable to request offers - %d\n", ret); - - goto cleanup; - } - - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - - -cleanup: - kfree(msginfo); - - return ret; -} - -/* eof */ diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c deleted file mode 100644 index 649b91bcd8c1..000000000000 --- a/drivers/staging/hv/connection.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - - -struct vmbus_connection vmbus_connection = { - .conn_state = DISCONNECTED, - .next_gpadl_handle = ATOMIC_INIT(0xE1E10), -}; - -/* - * vmbus_connect - Sends a connect request on the partition service connection - */ -int vmbus_connect(void) -{ - int ret = 0; - int t; - struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_initiate_contact *msg; - unsigned long flags; - - /* Initialize the vmbus connection */ - vmbus_connection.conn_state = CONNECTING; - vmbus_connection.work_queue = create_workqueue("hv_vmbus_con"); - if (!vmbus_connection.work_queue) { - ret = -ENOMEM; - goto cleanup; - } - - INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); - spin_lock_init(&vmbus_connection.channelmsg_lock); - - INIT_LIST_HEAD(&vmbus_connection.chn_list); - spin_lock_init(&vmbus_connection.channel_lock); - - /* - * Setup the vmbus event connection for channel interrupt - * abstraction stuff - */ - vmbus_connection.int_page = - (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); - if (vmbus_connection.int_page == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - vmbus_connection.recv_int_page = vmbus_connection.int_page; - vmbus_connection.send_int_page = - (void *)((unsigned long)vmbus_connection.int_page + - (PAGE_SIZE >> 1)); - - /* - * Setup the monitor notification facility. The 1st page for - * parent->child and the 2nd page for child->parent - */ - vmbus_connection.monitor_pages = - (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); - if (vmbus_connection.monitor_pages == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - msginfo = kzalloc(sizeof(*msginfo) + - sizeof(struct vmbus_channel_initiate_contact), - GFP_KERNEL); - if (msginfo == NULL) { - ret = -ENOMEM; - goto cleanup; - } - - init_completion(&msginfo->waitevent); - - msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; - - msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; - msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; - msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); - msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); - msg->monitor_page2 = virt_to_phys( - (void *)((unsigned long)vmbus_connection.monitor_pages + - PAGE_SIZE)); - - /* - * Add to list before we send the request since we may - * receive the response before returning from this routine - */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&msginfo->msglistentry, - &vmbus_connection.chn_msg_list); - - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_initiate_contact)); - if (ret != 0) { - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, - flags); - goto cleanup; - } - - /* Wait for the connection response */ - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - if (t == 0) { - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, - flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, - flags); - ret = -ETIMEDOUT; - goto cleanup; - } - - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - - /* Check if successful */ - if (msginfo->response.version_response.version_supported) { - vmbus_connection.conn_state = CONNECTED; - } else { - pr_err("Unable to connect, " - "Version %d not supported by Hyper-V\n", - VMBUS_REVISION_NUMBER); - ret = -ECONNREFUSED; - goto cleanup; - } - - kfree(msginfo); - return 0; - -cleanup: - vmbus_connection.conn_state = DISCONNECTED; - - if (vmbus_connection.work_queue) - destroy_workqueue(vmbus_connection.work_queue); - - if (vmbus_connection.int_page) { - free_pages((unsigned long)vmbus_connection.int_page, 0); - vmbus_connection.int_page = NULL; - } - - if (vmbus_connection.monitor_pages) { - free_pages((unsigned long)vmbus_connection.monitor_pages, 1); - vmbus_connection.monitor_pages = NULL; - } - - kfree(msginfo); - - return ret; -} - - -/* - * relid2channel - Get the channel object given its - * child relative id (ie channel id) - */ -struct vmbus_channel *relid2channel(u32 relid) -{ - struct vmbus_channel *channel; - struct vmbus_channel *found_channel = NULL; - unsigned long flags; - - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { - if (channel->offermsg.child_relid == relid) { - found_channel = channel; - break; - } - } - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - return found_channel; -} - -/* - * process_chn_event - Process a channel event notification - */ -static void process_chn_event(u32 relid) -{ - struct vmbus_channel *channel; - unsigned long flags; - - /* - * Find the channel based on this relid and invokes the - * channel callback to process the event - */ - channel = relid2channel(relid); - - if (!channel) { - pr_err("channel not found for relid - %u\n", relid); - return; - } - - /* - * A channel once created is persistent even when there - * is no driver handling the device. An unloading driver - * sets the onchannel_callback to NULL under the - * protection of the channel inbound_lock. Thus, checking - * and invoking the driver specific callback takes care of - * orderly unloading of the driver. - */ - - spin_lock_irqsave(&channel->inbound_lock, flags); - if (channel->onchannel_callback != NULL) - channel->onchannel_callback(channel->channel_callback_context); - else - pr_err("no channel callback for relid - %u\n", relid); - - spin_unlock_irqrestore(&channel->inbound_lock, flags); -} - -/* - * vmbus_on_event - Handler for events - */ -void vmbus_on_event(unsigned long data) -{ - u32 dword; - u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; - int bit; - u32 relid; - u32 *recv_int_page = vmbus_connection.recv_int_page; - - /* Check events */ - if (!recv_int_page) - return; - for (dword = 0; dword < maxdword; dword++) { - if (!recv_int_page[dword]) - continue; - for (bit = 0; bit < 32; bit++) { - if (sync_test_and_clear_bit(bit, - (unsigned long *)&recv_int_page[dword])) { - relid = (dword << 5) + bit; - - if (relid == 0) - /* - * Special case - vmbus - * channel protocol msg - */ - continue; - - process_chn_event(relid); - } - } - } -} - -/* - * vmbus_post_msg - Send a msg on the vmbus's message connection - */ -int vmbus_post_msg(void *buffer, size_t buflen) -{ - union hv_connection_id conn_id; - int ret = 0; - int retries = 0; - - conn_id.asu32 = 0; - conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; - - /* - * hv_post_message() can have transient failures because of - * insufficient resources. Retry the operation a couple of - * times before giving up. - */ - while (retries < 3) { - ret = hv_post_message(conn_id, 1, buffer, buflen); - if (ret != HV_STATUS_INSUFFICIENT_BUFFERS) - return ret; - retries++; - msleep(100); - } - return ret; -} - -/* - * vmbus_set_event - Send an event notification to the parent - */ -int vmbus_set_event(u32 child_relid) -{ - /* Each u32 represents 32 channels */ - sync_set_bit(child_relid & 31, - (unsigned long *)vmbus_connection.send_int_page + - (child_relid >> 5)); - - return hv_signal_event(); -} diff --git a/drivers/staging/hv/hv.c b/drivers/staging/hv/hv.c deleted file mode 100644 index 06f1e158c27c..000000000000 --- a/drivers/staging/hv/hv.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - -/* The one and only */ -struct hv_context hv_context = { - .synic_initialized = false, - .hypercall_page = NULL, - .signal_event_param = NULL, - .signal_event_buffer = NULL, -}; - -/* - * query_hypervisor_presence - * - Query the cpuid for presence of windows hypervisor - */ -static int query_hypervisor_presence(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int op; - - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VERSION_FEATURES; - cpuid(op, &eax, &ebx, &ecx, &edx); - - return ecx & HV_PRESENT_BIT; -} - -/* - * query_hypervisor_info - Get version info of the windows hypervisor - */ -static int query_hypervisor_info(void) -{ - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; - unsigned int max_leaf; - unsigned int op; - - /* - * Its assumed that this is called after confirming that Viridian - * is present. Query id and revision. - */ - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VENDOR_MAXFUNCTION; - cpuid(op, &eax, &ebx, &ecx, &edx); - - max_leaf = eax; - - if (max_leaf >= HVCPUID_VERSION) { - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; - op = HVCPUID_VERSION; - cpuid(op, &eax, &ebx, &ecx, &edx); - pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", - eax, - ebx >> 16, - ebx & 0xFFFF, - ecx, - edx >> 24, - edx & 0xFFFFFF); - } - return max_leaf; -} - -/* - * do_hypercall- Invoke the specified hypercall - */ -static u64 do_hypercall(u64 control, void *input, void *output) -{ -#ifdef CONFIG_X86_64 - u64 hv_status = 0; - u64 input_address = (input) ? virt_to_phys(input) : 0; - u64 output_address = (output) ? virt_to_phys(output) : 0; - void *hypercall_page = hv_context.hypercall_page; - - __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); - __asm__ __volatile__("call *%3" : "=a" (hv_status) : - "c" (control), "d" (input_address), - "m" (hypercall_page)); - - return hv_status; - -#else - - u32 control_hi = control >> 32; - u32 control_lo = control & 0xFFFFFFFF; - u32 hv_status_hi = 1; - u32 hv_status_lo = 1; - u64 input_address = (input) ? virt_to_phys(input) : 0; - u32 input_address_hi = input_address >> 32; - u32 input_address_lo = input_address & 0xFFFFFFFF; - u64 output_address = (output) ? virt_to_phys(output) : 0; - u32 output_address_hi = output_address >> 32; - u32 output_address_lo = output_address & 0xFFFFFFFF; - void *hypercall_page = hv_context.hypercall_page; - - __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), - "=a"(hv_status_lo) : "d" (control_hi), - "a" (control_lo), "b" (input_address_hi), - "c" (input_address_lo), "D"(output_address_hi), - "S"(output_address_lo), "m" (hypercall_page)); - - return hv_status_lo | ((u64)hv_status_hi << 32); -#endif /* !x86_64 */ -} - -/* - * hv_init - Main initialization routine. - * - * This routine must be called before any other routines in here are called - */ -int hv_init(void) -{ - int max_leaf; - union hv_x64_msr_hypercall_contents hypercall_msr; - void *virtaddr = NULL; - - memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); - memset(hv_context.synic_message_page, 0, - sizeof(void *) * MAX_NUM_CPUS); - - if (!query_hypervisor_presence()) - goto cleanup; - - max_leaf = query_hypervisor_info(); - - rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid); - - if (hv_context.guestid != 0) - goto cleanup; - - /* Write our OS info */ - wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); - hv_context.guestid = HV_LINUX_GUEST_ID; - - /* See if the hypercall page is already set */ - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); - - if (!virtaddr) - goto cleanup; - - hypercall_msr.enable = 1; - - hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr); - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - /* Confirm that hypercall page did get setup. */ - hypercall_msr.as_uint64 = 0; - rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - - if (!hypercall_msr.enable) - goto cleanup; - - hv_context.hypercall_page = virtaddr; - - /* Setup the global signal event param for the signal event hypercall */ - hv_context.signal_event_buffer = - kmalloc(sizeof(struct hv_input_signal_event_buffer), - GFP_KERNEL); - if (!hv_context.signal_event_buffer) - goto cleanup; - - hv_context.signal_event_param = - (struct hv_input_signal_event *) - (ALIGN((unsigned long) - hv_context.signal_event_buffer, - HV_HYPERCALL_PARAM_ALIGN)); - hv_context.signal_event_param->connectionid.asu32 = 0; - hv_context.signal_event_param->connectionid.u.id = - VMBUS_EVENT_CONNECTION_ID; - hv_context.signal_event_param->flag_number = 0; - hv_context.signal_event_param->rsvdz = 0; - - return 0; - -cleanup: - if (virtaddr) { - if (hypercall_msr.enable) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - } - - vfree(virtaddr); - } - - return -ENOTSUPP; -} - -/* - * hv_cleanup - Cleanup routine. - * - * This routine is called normally during driver unloading or exiting. - */ -void hv_cleanup(void) -{ - union hv_x64_msr_hypercall_contents hypercall_msr; - - kfree(hv_context.signal_event_buffer); - hv_context.signal_event_buffer = NULL; - hv_context.signal_event_param = NULL; - - if (hv_context.hypercall_page) { - hypercall_msr.as_uint64 = 0; - wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - vfree(hv_context.hypercall_page); - hv_context.hypercall_page = NULL; - } -} - -/* - * hv_post_message - Post a message using the hypervisor message IPC. - * - * This involves a hypercall. - */ -u16 hv_post_message(union hv_connection_id connection_id, - enum hv_message_type message_type, - void *payload, size_t payload_size) -{ - struct aligned_input { - u64 alignment8; - struct hv_input_post_message msg; - }; - - struct hv_input_post_message *aligned_msg; - u16 status; - unsigned long addr; - - if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) - return -EMSGSIZE; - - addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC); - if (!addr) - return -ENOMEM; - - aligned_msg = (struct hv_input_post_message *) - (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN)); - - aligned_msg->connectionid = connection_id; - aligned_msg->message_type = message_type; - aligned_msg->payload_size = payload_size; - memcpy((void *)aligned_msg->payload, payload, payload_size); - - status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL) - & 0xFFFF; - - kfree((void *)addr); - - return status; -} - - -/* - * hv_signal_event - - * Signal an event on the specified connection using the hypervisor event IPC. - * - * This involves a hypercall. - */ -u16 hv_signal_event(void) -{ - u16 status; - - status = do_hypercall(HVCALL_SIGNAL_EVENT, - hv_context.signal_event_param, - NULL) & 0xFFFF; - return status; -} - -/* - * hv_synic_init - Initialize the Synthethic Interrupt Controller. - * - * If it is already initialized by another entity (ie x2v shim), we need to - * retrieve the initialized message and event pages. Otherwise, we create and - * initialize the message and event pages. - */ -void hv_synic_init(void *irqarg) -{ - u64 version; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - union hv_synic_sint shared_sint; - union hv_synic_scontrol sctrl; - - u32 irq_vector = *((u32 *)(irqarg)); - int cpu = smp_processor_id(); - - if (!hv_context.hypercall_page) - return; - - /* Check the version */ - rdmsrl(HV_X64_MSR_SVERSION, version); - - hv_context.synic_message_page[cpu] = - (void *)get_zeroed_page(GFP_ATOMIC); - - if (hv_context.synic_message_page[cpu] == NULL) { - pr_err("Unable to allocate SYNIC message page\n"); - goto cleanup; - } - - hv_context.synic_event_page[cpu] = - (void *)get_zeroed_page(GFP_ATOMIC); - - if (hv_context.synic_event_page[cpu] == NULL) { - pr_err("Unable to allocate SYNIC event page\n"); - goto cleanup; - } - - /* Setup the Synic's message page */ - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - simp.simp_enabled = 1; - simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu]) - >> PAGE_SHIFT; - - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - - /* Setup the Synic's event page */ - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - siefp.siefp_enabled = 1; - siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu]) - >> PAGE_SHIFT; - - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - - /* Setup the shared SINT. */ - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - shared_sint.as_uint64 = 0; - shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ - shared_sint.masked = false; - shared_sint.auto_eoi = false; - - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - /* Enable the global synic bit */ - rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); - sctrl.enable = 1; - - wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); - - hv_context.synic_initialized = true; - return; - -cleanup: - if (hv_context.synic_event_page[cpu]) - free_page((unsigned long)hv_context.synic_event_page[cpu]); - - if (hv_context.synic_message_page[cpu]) - free_page((unsigned long)hv_context.synic_message_page[cpu]); - return; -} - -/* - * hv_synic_cleanup - Cleanup routine for hv_synic_init(). - */ -void hv_synic_cleanup(void *arg) -{ - union hv_synic_sint shared_sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; - int cpu = smp_processor_id(); - - if (!hv_context.synic_initialized) - return; - - rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - shared_sint.masked = 1; - - /* Need to correctly cleanup in the case of SMP!!! */ - /* Disable the interrupt */ - wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); - - rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - simp.simp_enabled = 0; - simp.base_simp_gpa = 0; - - wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64); - - rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - siefp.siefp_enabled = 0; - siefp.base_siefp_gpa = 0; - - wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64); - - free_page((unsigned long)hv_context.synic_message_page[cpu]); - free_page((unsigned long)hv_context.synic_event_page[cpu]); -} diff --git a/drivers/staging/hv/hv_kvp.c b/drivers/staging/hv/hv_kvp.c deleted file mode 100644 index 1e9515cc6094..000000000000 --- a/drivers/staging/hv/hv_kvp.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * An implementation of key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include - -#include "hyperv.h" -#include "hv_kvp.h" - - - -/* - * Global state maintained for transaction that is being processed. - * Note that only one transaction can be active at any point in time. - * - * This state is set when we receive a request from the host; we - * cleanup this state when the transaction is completed - when we respond - * to the host with the key value. - */ - -static struct { - bool active; /* transaction status - active or not */ - int recv_len; /* number of bytes received. */ - int index; /* current index */ - struct vmbus_channel *recv_channel; /* chn we got the request */ - u64 recv_req_id; /* request ID. */ -} kvp_transaction; - -static void kvp_send_key(struct work_struct *dummy); - -#define TIMEOUT_FIRED 1 - -static void kvp_respond_to_host(char *key, char *value, int error); -static void kvp_work_func(struct work_struct *dummy); -static void kvp_register(void); - -static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); -static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); - -static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; -static const char kvp_name[] = "kvp_kernel_module"; -static u8 *recv_buffer; -/* - * Register the kernel component with the user-level daemon. - * As part of this registration, pass the LIC version number. - */ - -static void -kvp_register(void) -{ - - struct cn_msg *msg; - - msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); - - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; - msg->seq = KVP_REGISTER; - strcpy(msg->data, HV_DRV_VERSION); - msg->len = strlen(HV_DRV_VERSION) + 1; - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); - } -} -static void -kvp_work_func(struct work_struct *dummy) -{ - /* - * If the timer fires, the user-mode component has not responded; - * process the pending transaction. - */ - kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); -} - -/* - * Callback when data is received from user mode. - */ - -static void -kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) -{ - struct hv_ku_msg *message; - - message = (struct hv_ku_msg *)msg->data; - if (msg->seq == KVP_REGISTER) { - pr_info("KVP: user-mode registering done.\n"); - kvp_register(); - } - - if (msg->seq == KVP_USER_SET) { - /* - * Complete the transaction by forwarding the key value - * to the host. But first, cancel the timeout. - */ - if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(message->kvp_key, - message->kvp_value, - !strlen(message->kvp_key)); - } -} - -static void -kvp_send_key(struct work_struct *dummy) -{ - struct cn_msg *msg; - int index = kvp_transaction.index; - - msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); - - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; - msg->seq = KVP_KERNEL_GET; - ((struct hv_ku_msg *)msg->data)->kvp_index = index; - msg->len = sizeof(struct hv_ku_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); - } - return; -} - -/* - * Send a response back to the host. - */ - -static void -kvp_respond_to_host(char *key, char *value, int error) -{ - struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; - char *key_name; - struct icmsg_hdr *icmsghdrp; - int keylen, valuelen; - u32 buf_len; - struct vmbus_channel *channel; - u64 req_id; - - /* - * If a transaction is not active; log and return. - */ - - if (!kvp_transaction.active) { - /* - * This is a spurious call! - */ - pr_warn("KVP: Transaction not active\n"); - return; - } - /* - * Copy the global state for completing the transaction. Note that - * only one transaction can be active at a time. - */ - - buf_len = kvp_transaction.recv_len; - channel = kvp_transaction.recv_channel; - req_id = kvp_transaction.recv_req_id; - - kvp_transaction.active = false; - - if (channel->onchannel_callback == NULL) - /* - * We have raced with util driver being unloaded; - * silently return. - */ - return; - - icmsghdrp = (struct icmsg_hdr *) - &recv_buffer[sizeof(struct vmbuspipe_hdr)]; - kvp_msg = (struct hv_kvp_msg *) - &recv_buffer[sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; - key_name = key; - - /* - * If the error parameter is set, terminate the host's enumeration. - */ - if (error) { - /* - * We don't support this index or the we have timedout; - * terminate the host-side iteration by returning an error. - */ - icmsghdrp->status = HV_E_FAIL; - goto response_done; - } - - /* - * The windows host expects the key/value pair to be encoded - * in utf16. - */ - keylen = utf8s_to_utf16s(key_name, strlen(key_name), - (wchar_t *)kvp_data->data.key); - kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ - valuelen = utf8s_to_utf16s(value, strlen(value), - (wchar_t *)kvp_data->data.value); - kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ - - kvp_data->data.value_type = REG_SZ; /* all our values are strings */ - icmsghdrp->status = HV_S_OK; - -response_done: - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, - VM_PKT_DATA_INBAND, 0); - -} - -/* - * This callback is invoked when we get a KVP message from the host. - * The host ensures that only one KVP transaction can be active at a time. - * KVP implementation in Linux needs to forward the key to a user-mde - * component to retrive the corresponding value. Consequently, we cannot - * respond to the host in the conext of this callback. Since the host - * guarantees that at most only one transaction can be active at a time, - * we stash away the transaction state in a set of global variables. - */ - -void hv_kvp_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - - struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; - - struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - - - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, recv_buffer); - } else { - kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - kvp_data = &kvp_msg->kvp_data; - - /* - * We only support the "get" operation on - * "KVP_POOL_AUTO" pool. - */ - - if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || - (kvp_msg->kvp_hdr.operation != - KVP_OP_ENUMERATE)) { - icmsghdrp->status = HV_E_FAIL; - goto callback_done; - } - - /* - * Stash away this global state for completing the - * transaction; note transactions are serialized. - */ - kvp_transaction.recv_len = recvlen; - kvp_transaction.recv_channel = channel; - kvp_transaction.recv_req_id = requestid; - kvp_transaction.active = true; - kvp_transaction.index = kvp_data->index; - - /* - * Get the information from the - * user-mode component. - * component. This transaction will be - * completed when we get the value from - * the user-mode component. - * Set a timeout to deal with - * user-mode not responding. - */ - schedule_work(&kvp_sendkey_work); - schedule_delayed_work(&kvp_work, 5*HZ); - - return; - - } - -callback_done: - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, recv_buffer, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } - -} - -int -hv_kvp_init(struct hv_util_service *srv) -{ - int err; - - err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); - if (err) - return err; - recv_buffer = srv->recv_buffer; - - return 0; -} - -void hv_kvp_deinit(void) -{ - cn_del_callback(&kvp_id); - cancel_delayed_work_sync(&kvp_work); - cancel_work_sync(&kvp_sendkey_work); -} diff --git a/drivers/staging/hv/hv_kvp.h b/drivers/staging/hv/hv_kvp.h deleted file mode 100644 index 9b765d7df838..000000000000 --- a/drivers/staging/hv/hv_kvp.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * An implementation of HyperV key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#ifndef _KVP_H -#define _KVP_H_ - -/* - * Maximum value size - used for both key names and value data, and includes - * any applicable NULL terminators. - * - * Note: This limit is somewhat arbitrary, but falls easily within what is - * supported for all native guests (back to Win 2000) and what is reasonable - * for the IC KVP exchange functionality. Note that Windows Me/98/95 are - * limited to 255 character key names. - * - * MSDN recommends not storing data values larger than 2048 bytes in the - * registry. - * - * Note: This value is used in defining the KVP exchange message - this value - * cannot be modified without affecting the message size and compatibility. - */ - -/* - * bytes, including any null terminators - */ -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) - - -/* - * Maximum key size - the registry limit for the length of an entry name - * is 256 characters, including the null terminator - */ - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) - -/* - * In Linux, we implement the KVP functionality in two components: - * 1) The kernel component which is packaged as part of the hv_utils driver - * is responsible for communicating with the host and responsible for - * implementing the host/guest protocol. 2) A user level daemon that is - * responsible for data gathering. - * - * Host/Guest Protocol: The host iterates over an index and expects the guest - * to assign a key name to the index and also return the value corresponding to - * the key. The host will have atmost one KVP transaction outstanding at any - * given point in time. The host side iteration stops when the guest returns - * an error. Microsoft has specified the following mapping of key names to - * host specified index: - * - * Index Key Name - * 0 FullyQualifiedDomainName - * 1 IntegrationServicesVersion - * 2 NetworkAddressIPv4 - * 3 NetworkAddressIPv6 - * 4 OSBuildNumber - * 5 OSName - * 6 OSMajorVersion - * 7 OSMinorVersion - * 8 OSVersion - * 9 ProcessorArchitecture - * - * The Windows host expects the Key Name and Key Value to be encoded in utf16. - * - * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the - * data gathering functionality in a user mode daemon. The user level daemon - * is also responsible for binding the key name to the index as well. The - * kernel and user-level daemon communicate using a connector channel. - * - * The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * - * The kernel component simply acts as a conduit for communication between the - * Windows host and the user-level daemon. The kernel component passes up the - * index received from the Host to the user-level daemon. If the index is - * valid (supported), the corresponding key as well as its - * value (both are strings) is returned. If the index is invalid - * (not supported), a NULL key string is returned. - */ - -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - -#ifdef __KERNEL__ - -/* - * Registry value types. - */ - -#define REG_SZ 1 - -enum hv_kvp_exchg_op { - KVP_OP_GET = 0, - KVP_OP_SET, - KVP_OP_DELETE, - KVP_OP_ENUMERATE, - KVP_OP_COUNT /* Number of operations, must be last. */ -}; - -enum hv_kvp_exchg_pool { - KVP_POOL_EXTERNAL = 0, - KVP_POOL_GUEST, - KVP_POOL_AUTO, - KVP_POOL_AUTO_EXTERNAL, - KVP_POOL_AUTO_INTERNAL, - KVP_POOL_COUNT /* Number of pools, must be last. */ -}; - -struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; - -struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; - -struct hv_kvp_msg_enumerate { - u32 index; - struct hv_kvp_exchg_msg_value data; -}; - -struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; -}; - -int hv_kvp_init(struct hv_util_service *); -void hv_kvp_deinit(void); -void hv_kvp_onchannelcallback(void *); - -#endif /* __KERNEL__ */ -#endif /* _KVP_H */ - diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c index edbb4797db75..c354ade76ef5 100644 --- a/drivers/staging/hv/hv_mouse.c +++ b/drivers/staging/hv/hv_mouse.c @@ -22,8 +22,7 @@ #include #include #include - -#include "hyperv.h" +#include struct hv_input_dev_info { diff --git a/drivers/staging/hv/hv_util.c b/drivers/staging/hv/hv_util.c deleted file mode 100644 index faa66074cc21..000000000000 --- a/drivers/staging/hv/hv_util.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2010, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "hyperv.h" -#include "hv_kvp.h" - - -static void shutdown_onchannelcallback(void *context); -static struct hv_util_service util_shutdown = { - .util_cb = shutdown_onchannelcallback, -}; - -static void timesync_onchannelcallback(void *context); -static struct hv_util_service util_timesynch = { - .util_cb = timesync_onchannelcallback, -}; - -static void heartbeat_onchannelcallback(void *context); -static struct hv_util_service util_heartbeat = { - .util_cb = heartbeat_onchannelcallback, -}; - -static struct hv_util_service util_kvp = { - .util_cb = hv_kvp_onchannelcallback, - .util_init = hv_kvp_init, - .util_deinit = hv_kvp_deinit, -}; - -static void shutdown_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - u8 execute_shutdown = false; - u8 *shut_txf_buf = util_shutdown.recv_buffer; - - struct shutdown_msg_data *shutdown_msg; - - struct icmsg_hdr *icmsghdrp; - struct icmsg_negotiate *negop = NULL; - - vmbus_recvpacket(channel, shut_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); - } else { - shutdown_msg = - (struct shutdown_msg_data *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - switch (shutdown_msg->flags) { - case 0: - case 1: - icmsghdrp->status = HV_S_OK; - execute_shutdown = true; - - pr_info("Shutdown request received -" - " graceful shutdown initiated\n"); - break; - default: - icmsghdrp->status = HV_E_FAIL; - execute_shutdown = false; - - pr_info("Shutdown request received -" - " Invalid request\n"); - break; - } - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, shut_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } - - if (execute_shutdown == true) - orderly_poweroff(true); -} - -/* - * Set guest time to host UTC time. - */ -static inline void do_adj_guesttime(u64 hosttime) -{ - s64 host_tns; - struct timespec host_ts; - - host_tns = (hosttime - WLTIMEDELTA) * 100; - host_ts = ns_to_timespec(host_tns); - - do_settimeofday(&host_ts); -} - -/* - * Set the host time in a process context. - */ - -struct adj_time_work { - struct work_struct work; - u64 host_time; -}; - -static void hv_set_host_time(struct work_struct *work) -{ - struct adj_time_work *wrk; - - wrk = container_of(work, struct adj_time_work, work); - do_adj_guesttime(wrk->host_time); - kfree(wrk); -} - -/* - * Synchronize time with host after reboot, restore, etc. - * - * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. - * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time - * message after the timesync channel is opened. Since the hv_utils module is - * loaded after hv_vmbus, the first message is usually missed. The other - * thing is, systime is automatically set to emulated hardware clock which may - * not be UTC time or in the same time zone. So, to override these effects, we - * use the first 50 time samples for initial system time setting. - */ -static inline void adj_guesttime(u64 hosttime, u8 flags) -{ - struct adj_time_work *wrk; - static s32 scnt = 50; - - wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC); - if (wrk == NULL) - return; - - wrk->host_time = hosttime; - if ((flags & ICTIMESYNCFLAG_SYNC) != 0) { - INIT_WORK(&wrk->work, hv_set_host_time); - schedule_work(&wrk->work); - return; - } - - if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) { - scnt--; - INIT_WORK(&wrk->work, hv_set_host_time); - schedule_work(&wrk->work); - } else - kfree(wrk); -} - -/* - * Time Sync Channel message handler. - */ -static void timesync_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - struct icmsg_hdr *icmsghdrp; - struct ictimesync_data *timedatap; - u8 *time_txf_buf = util_timesynch.recv_buffer; - - vmbus_recvpacket(channel, time_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); - } else { - timedatap = (struct ictimesync_data *)&time_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - adj_guesttime(timedatap->parenttime, timedatap->flags); - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, time_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } -} - -/* - * Heartbeat functionality. - * Every two seconds, Hyper-V send us a heartbeat request message. - * we respond to this message, and Hyper-V knows we are alive. - */ -static void heartbeat_onchannelcallback(void *context) -{ - struct vmbus_channel *channel = context; - u32 recvlen; - u64 requestid; - struct icmsg_hdr *icmsghdrp; - struct heartbeat_msg_data *heartbeat_msg; - u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; - - vmbus_recvpacket(channel, hbeat_txf_buf, - PAGE_SIZE, &recvlen, &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); - } else { - heartbeat_msg = - (struct heartbeat_msg_data *)&hbeat_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - heartbeat_msg->seq_num += 1; - } - - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, hbeat_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); - } -} - -static int util_probe(struct hv_device *dev, - const struct hv_vmbus_device_id *dev_id) -{ - struct hv_util_service *srv = - (struct hv_util_service *)dev_id->driver_data; - int ret; - - srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!srv->recv_buffer) - return -ENOMEM; - if (srv->util_init) { - ret = srv->util_init(srv); - if (ret) { - ret = -ENODEV; - goto error1; - } - } - - ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0, - srv->util_cb, dev->channel); - if (ret) - goto error; - - hv_set_drvdata(dev, srv); - return 0; - -error: - if (srv->util_deinit) - srv->util_deinit(); -error1: - kfree(srv->recv_buffer); - return ret; -} - -static int util_remove(struct hv_device *dev) -{ - struct hv_util_service *srv = hv_get_drvdata(dev); - - vmbus_close(dev->channel); - if (srv->util_deinit) - srv->util_deinit(); - kfree(srv->recv_buffer); - - return 0; -} - -static const struct hv_vmbus_device_id id_table[] = { - /* Shutdown guid */ - { VMBUS_DEVICE(0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB) - .driver_data = (unsigned long)&util_shutdown }, - /* Time synch guid */ - { VMBUS_DEVICE(0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf) - .driver_data = (unsigned long)&util_timesynch }, - /* Heartbeat guid */ - { VMBUS_DEVICE(0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d) - .driver_data = (unsigned long)&util_heartbeat }, - /* KVP guid */ - { VMBUS_DEVICE(0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6) - .driver_data = (unsigned long)&util_kvp }, - { }, -}; - -MODULE_DEVICE_TABLE(vmbus, id_table); - -/* The one and only one */ -static struct hv_driver util_drv = { - .name = "hv_util", - .id_table = id_table, - .probe = util_probe, - .remove = util_remove, -}; - -static int __init init_hyperv_utils(void) -{ - pr_info("Registering HyperV Utility Driver\n"); - - return vmbus_driver_register(&util_drv); -} - -static void exit_hyperv_utils(void) -{ - pr_info("De-Registered HyperV Utility Driver\n"); - - vmbus_driver_unregister(&util_drv); -} - -module_init(init_hyperv_utils); -module_exit(exit_hyperv_utils); - -MODULE_DESCRIPTION("Hyper-V Utilities"); -MODULE_VERSION(HV_DRV_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h deleted file mode 100644 index edaa9e2f58ec..000000000000 --- a/drivers/staging/hv/hyperv.h +++ /dev/null @@ -1,969 +0,0 @@ -/* - * - * Copyright (c) 2011, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * K. Y. Srinivasan - * - */ - -#ifndef _HYPERV_H -#define _HYPERV_H - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include - - -#define MAX_PAGE_BUFFER_COUNT 16 -#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ - -#pragma pack(push, 1) - -/* Single-page buffer */ -struct hv_page_buffer { - u32 len; - u32 offset; - u64 pfn; -}; - -/* Multiple-page buffer */ -struct hv_multipage_buffer { - /* Length and Offset determines the # of pfns in the array */ - u32 len; - u32 offset; - u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; -}; - -/* 0x18 includes the proprietary packet header */ -#define MAX_PAGE_BUFFER_PACKET (0x18 + \ - (sizeof(struct hv_page_buffer) * \ - MAX_PAGE_BUFFER_COUNT)) -#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ - sizeof(struct hv_multipage_buffer)) - - -#pragma pack(pop) - -struct hv_ring_buffer { - /* Offset in bytes from the start of ring data below */ - u32 write_index; - - /* Offset in bytes from the start of ring data below */ - u32 read_index; - - u32 interrupt_mask; - - /* Pad it to PAGE_SIZE so that data starts on page boundary */ - u8 reserved[4084]; - - /* NOTE: - * The interrupt_mask field is used only for channels but since our - * vmbus connection also uses this data structure and its data starts - * here, we commented out this field. - */ - - /* - * Ring data starts here + RingDataStartOffset - * !!! DO NOT place any fields below this !!! - */ - u8 buffer[0]; -} __packed; - -struct hv_ring_buffer_info { - struct hv_ring_buffer *ring_buffer; - u32 ring_size; /* Include the shared header */ - spinlock_t ring_lock; - - u32 ring_datasize; /* < ring_size */ - u32 ring_data_startoffset; -}; - -struct hv_ring_buffer_debug_info { - u32 current_interrupt_mask; - u32 current_read_index; - u32 current_write_index; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - -/* - * We use the same version numbering for all Hyper-V modules. - * - * Definition of versioning is as follows; - * - * Major Number Changes for these scenarios; - * 1. When a new version of Windows Hyper-V - * is released. - * 2. A Major change has occurred in the - * Linux IC's. - * (For example the merge for the first time - * into the kernel) Every time the Major Number - * changes, the Revision number is reset to 0. - * Minor Number Changes when new functionality is added - * to the Linux IC's that is not a bug fix. - * - * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync - */ -#define HV_DRV_VERSION "3.1" - - -/* - * A revision number of vmbus that is used for ensuring both ends on a - * partition are using compatible versions. - */ -#define VMBUS_REVISION_NUMBER 13 - -/* Make maximum size of pipe payload of 16K */ -#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) - -/* Define PipeMode values. */ -#define VMBUS_PIPE_TYPE_BYTE 0x00000000 -#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 - -/* The size of the user defined data buffer for non-pipe offers. */ -#define MAX_USER_DEFINED_BYTES 120 - -/* The size of the user defined data buffer for pipe offers. */ -#define MAX_PIPE_USER_DEFINED_BYTES 116 - -/* - * At the center of the Channel Management library is the Channel Offer. This - * struct contains the fundamental information about an offer. - */ -struct vmbus_channel_offer { - uuid_le if_type; - uuid_le if_instance; - u64 int_latency; /* in 100ns units */ - u32 if_revision; - u32 server_ctx_size; /* in bytes */ - u16 chn_flags; - u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ - - union { - /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ - struct { - unsigned char user_def[MAX_USER_DEFINED_BYTES]; - } std; - - /* - * Pipes: - * The following sructure is an integrated pipe protocol, which - * is implemented on top of standard user-defined data. Pipe - * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own - * use. - */ - struct { - u32 pipe_mode; - unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; - } pipe; - } u; - u32 padding; -} __packed; - -/* Server Flags */ -#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 -#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 -#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 -#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 -#define VMBUS_CHANNEL_PARENT_OFFER 0x200 -#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 - -struct vmpacket_descriptor { - u16 type; - u16 offset8; - u16 len8; - u16 flags; - u64 trans_id; -} __packed; - -struct vmpacket_header { - u32 prev_pkt_start_offset; - struct vmpacket_descriptor descriptor; -} __packed; - -struct vmtransfer_page_range { - u32 byte_count; - u32 byte_offset; -} __packed; - -struct vmtransfer_page_packet_header { - struct vmpacket_descriptor d; - u16 xfer_pageset_id; - bool sender_owns_set; - u8 reserved; - u32 range_cnt; - struct vmtransfer_page_range ranges[1]; -} __packed; - -struct vmgpadl_packet_header { - struct vmpacket_descriptor d; - u32 gpadl; - u32 reserved; -} __packed; - -struct vmadd_remove_transfer_page_set { - struct vmpacket_descriptor d; - u32 gpadl; - u16 xfer_pageset_id; - u16 reserved; -} __packed; - -/* - * This structure defines a range in guest physical space that can be made to - * look virtually contiguous. - */ -struct gpa_range { - u32 byte_count; - u32 byte_offset; - u64 pfn_array[0]; -}; - -/* - * This is the format for an Establish Gpadl packet, which contains a handle by - * which this GPADL will be known and a set of GPA ranges associated with it. - * This can be converted to a MDL by the guest OS. If there are multiple GPA - * ranges, then the resulting MDL will be "chained," representing multiple VA - * ranges. - */ -struct vmestablish_gpadl { - struct vmpacket_descriptor d; - u32 gpadl; - u32 range_cnt; - struct gpa_range range[1]; -} __packed; - -/* - * This is the format for a Teardown Gpadl packet, which indicates that the - * GPADL handle in the Establish Gpadl packet will never be referenced again. - */ -struct vmteardown_gpadl { - struct vmpacket_descriptor d; - u32 gpadl; - u32 reserved; /* for alignment to a 8-byte boundary */ -} __packed; - -/* - * This is the format for a GPA-Direct packet, which contains a set of GPA - * ranges, in addition to commands and/or data. - */ -struct vmdata_gpa_direct { - struct vmpacket_descriptor d; - u32 reserved; - u32 range_cnt; - struct gpa_range range[1]; -} __packed; - -/* This is the format for a Additional Data Packet. */ -struct vmadditional_data { - struct vmpacket_descriptor d; - u64 total_bytes; - u32 offset; - u32 byte_cnt; - unsigned char data[1]; -} __packed; - -union vmpacket_largest_possible_header { - struct vmpacket_descriptor simple_hdr; - struct vmtransfer_page_packet_header xfer_page_hdr; - struct vmgpadl_packet_header gpadl_hdr; - struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; - struct vmestablish_gpadl establish_gpadl_hdr; - struct vmteardown_gpadl teardown_gpadl_hdr; - struct vmdata_gpa_direct data_gpa_direct_hdr; -}; - -#define VMPACKET_DATA_START_ADDRESS(__packet) \ - (void *)(((unsigned char *)__packet) + \ - ((struct vmpacket_descriptor)__packet)->offset8 * 8) - -#define VMPACKET_DATA_LENGTH(__packet) \ - ((((struct vmpacket_descriptor)__packet)->len8 - \ - ((struct vmpacket_descriptor)__packet)->offset8) * 8) - -#define VMPACKET_TRANSFER_MODE(__packet) \ - (((struct IMPACT)__packet)->type) - -enum vmbus_packet_type { - VM_PKT_INVALID = 0x0, - VM_PKT_SYNCH = 0x1, - VM_PKT_ADD_XFER_PAGESET = 0x2, - VM_PKT_RM_XFER_PAGESET = 0x3, - VM_PKT_ESTABLISH_GPADL = 0x4, - VM_PKT_TEARDOWN_GPADL = 0x5, - VM_PKT_DATA_INBAND = 0x6, - VM_PKT_DATA_USING_XFER_PAGES = 0x7, - VM_PKT_DATA_USING_GPADL = 0x8, - VM_PKT_DATA_USING_GPA_DIRECT = 0x9, - VM_PKT_CANCEL_REQUEST = 0xa, - VM_PKT_COMP = 0xb, - VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, - VM_PKT_ADDITIONAL_DATA = 0xd -}; - -#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 - - -/* Version 1 messages */ -enum vmbus_channel_message_type { - CHANNELMSG_INVALID = 0, - CHANNELMSG_OFFERCHANNEL = 1, - CHANNELMSG_RESCIND_CHANNELOFFER = 2, - CHANNELMSG_REQUESTOFFERS = 3, - CHANNELMSG_ALLOFFERS_DELIVERED = 4, - CHANNELMSG_OPENCHANNEL = 5, - CHANNELMSG_OPENCHANNEL_RESULT = 6, - CHANNELMSG_CLOSECHANNEL = 7, - CHANNELMSG_GPADL_HEADER = 8, - CHANNELMSG_GPADL_BODY = 9, - CHANNELMSG_GPADL_CREATED = 10, - CHANNELMSG_GPADL_TEARDOWN = 11, - CHANNELMSG_GPADL_TORNDOWN = 12, - CHANNELMSG_RELID_RELEASED = 13, - CHANNELMSG_INITIATE_CONTACT = 14, - CHANNELMSG_VERSION_RESPONSE = 15, - CHANNELMSG_UNLOAD = 16, -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD - CHANNELMSG_VIEWRANGE_ADD = 17, - CHANNELMSG_VIEWRANGE_REMOVE = 18, -#endif - CHANNELMSG_COUNT -}; - -struct vmbus_channel_message_header { - enum vmbus_channel_message_type msgtype; - u32 padding; -} __packed; - -/* Query VMBus Version parameters */ -struct vmbus_channel_query_vmbus_version { - struct vmbus_channel_message_header header; - u32 version; -} __packed; - -/* VMBus Version Supported parameters */ -struct vmbus_channel_version_supported { - struct vmbus_channel_message_header header; - bool version_supported; -} __packed; - -/* Offer Channel parameters */ -struct vmbus_channel_offer_channel { - struct vmbus_channel_message_header header; - struct vmbus_channel_offer offer; - u32 child_relid; - u8 monitorid; - bool monitor_allocated; -} __packed; - -/* Rescind Offer parameters */ -struct vmbus_channel_rescind_offer { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -/* - * Request Offer -- no parameters, SynIC message contains the partition ID - * Set Snoop -- no parameters, SynIC message contains the partition ID - * Clear Snoop -- no parameters, SynIC message contains the partition ID - * All Offers Delivered -- no parameters, SynIC message contains the partition - * ID - * Flush Client -- no parameters, SynIC message contains the partition ID - */ - -/* Open Channel parameters */ -struct vmbus_channel_open_channel { - struct vmbus_channel_message_header header; - - /* Identifies the specific VMBus channel that is being opened. */ - u32 child_relid; - - /* ID making a particular open request at a channel offer unique. */ - u32 openid; - - /* GPADL for the channel's ring buffer. */ - u32 ringbuffer_gpadlhandle; - - /* GPADL for the channel's server context save area. */ - u32 server_contextarea_gpadlhandle; - - /* - * The upstream ring buffer begins at offset zero in the memory - * described by RingBufferGpadlHandle. The downstream ring buffer - * follows it at this offset (in pages). - */ - u32 downstream_ringbuffer_pageoffset; - - /* User-specific data to be passed along to the server endpoint. */ - unsigned char userdata[MAX_USER_DEFINED_BYTES]; -} __packed; - -/* Open Channel Result parameters */ -struct vmbus_channel_open_result { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 openid; - u32 status; -} __packed; - -/* Close channel parameters; */ -struct vmbus_channel_close_channel { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -/* Channel Message GPADL */ -#define GPADL_TYPE_RING_BUFFER 1 -#define GPADL_TYPE_SERVER_SAVE_AREA 2 -#define GPADL_TYPE_TRANSACTION 8 - -/* - * The number of PFNs in a GPADL message is defined by the number of - * pages that would be spanned by ByteCount and ByteOffset. If the - * implied number of PFNs won't fit in this packet, there will be a - * follow-up packet that contains more. - */ -struct vmbus_channel_gpadl_header { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; - u16 range_buflen; - u16 rangecount; - struct gpa_range range[0]; -} __packed; - -/* This is the followup packet that contains more PFNs. */ -struct vmbus_channel_gpadl_body { - struct vmbus_channel_message_header header; - u32 msgnumber; - u32 gpadl; - u64 pfn[0]; -} __packed; - -struct vmbus_channel_gpadl_created { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; - u32 creation_status; -} __packed; - -struct vmbus_channel_gpadl_teardown { - struct vmbus_channel_message_header header; - u32 child_relid; - u32 gpadl; -} __packed; - -struct vmbus_channel_gpadl_torndown { - struct vmbus_channel_message_header header; - u32 gpadl; -} __packed; - -#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD -struct vmbus_channel_view_range_add { - struct vmbus_channel_message_header header; - PHYSICAL_ADDRESS viewrange_base; - u64 viewrange_length; - u32 child_relid; -} __packed; - -struct vmbus_channel_view_range_remove { - struct vmbus_channel_message_header header; - PHYSICAL_ADDRESS viewrange_base; - u32 child_relid; -} __packed; -#endif - -struct vmbus_channel_relid_released { - struct vmbus_channel_message_header header; - u32 child_relid; -} __packed; - -struct vmbus_channel_initiate_contact { - struct vmbus_channel_message_header header; - u32 vmbus_version_requested; - u32 padding2; - u64 interrupt_page; - u64 monitor_page1; - u64 monitor_page2; -} __packed; - -struct vmbus_channel_version_response { - struct vmbus_channel_message_header header; - bool version_supported; -} __packed; - -enum vmbus_channel_state { - CHANNEL_OFFER_STATE, - CHANNEL_OPENING_STATE, - CHANNEL_OPEN_STATE, -}; - -struct vmbus_channel_debug_info { - u32 relid; - enum vmbus_channel_state state; - uuid_le interfacetype; - uuid_le interface_instance; - u32 monitorid; - u32 servermonitor_pending; - u32 servermonitor_latency; - u32 servermonitor_connectionid; - u32 clientmonitor_pending; - u32 clientmonitor_latency; - u32 clientmonitor_connectionid; - - struct hv_ring_buffer_debug_info inbound; - struct hv_ring_buffer_debug_info outbound; -}; - -/* - * Represents each channel msg on the vmbus connection This is a - * variable-size data structure depending on the msg type itself - */ -struct vmbus_channel_msginfo { - /* Bookkeeping stuff */ - struct list_head msglistentry; - - /* So far, this is only used to handle gpadl body message */ - struct list_head submsglist; - - /* Synchronize the request/response if needed */ - struct completion waitevent; - union { - struct vmbus_channel_version_supported version_supported; - struct vmbus_channel_open_result open_result; - struct vmbus_channel_gpadl_torndown gpadl_torndown; - struct vmbus_channel_gpadl_created gpadl_created; - struct vmbus_channel_version_response version_response; - } response; - - u32 msgsize; - /* - * The channel message that goes out on the "wire". - * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header - */ - unsigned char msg[0]; -}; - -struct vmbus_close_msg { - struct vmbus_channel_msginfo info; - struct vmbus_channel_close_channel msg; -}; - -struct vmbus_channel { - struct list_head listentry; - - struct hv_device *device_obj; - - struct work_struct work; - - enum vmbus_channel_state state; - - struct vmbus_channel_offer_channel offermsg; - /* - * These are based on the OfferMsg.MonitorId. - * Save it here for easy access. - */ - u8 monitor_grp; - u8 monitor_bit; - - u32 ringbuffer_gpadlhandle; - - /* Allocated memory for ring buffer */ - void *ringbuffer_pages; - u32 ringbuffer_pagecount; - struct hv_ring_buffer_info outbound; /* send to parent */ - struct hv_ring_buffer_info inbound; /* receive from parent */ - spinlock_t inbound_lock; - struct workqueue_struct *controlwq; - - struct vmbus_close_msg close_msg; - - /* Channel callback are invoked in this workqueue context */ - /* HANDLE dataWorkQueue; */ - - void (*onchannel_callback)(void *context); - void *channel_callback_context; -}; - -void free_channel(struct vmbus_channel *channel); - -void vmbus_onmessage(void *context); - -int vmbus_request_offers(void); - -/* The format must be the same as struct vmdata_gpa_direct */ -struct vmbus_channel_packet_page_buffer { - u16 type; - u16 dataoffset8; - u16 length8; - u16 flags; - u64 transactionid; - u32 reserved; - u32 rangecount; - struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; -} __packed; - -/* The format must be the same as struct vmdata_gpa_direct */ -struct vmbus_channel_packet_multipage_buffer { - u16 type; - u16 dataoffset8; - u16 length8; - u16 flags; - u64 transactionid; - u32 reserved; - u32 rangecount; /* Always 1 in this case */ - struct hv_multipage_buffer range; -} __packed; - - -extern int vmbus_open(struct vmbus_channel *channel, - u32 send_ringbuffersize, - u32 recv_ringbuffersize, - void *userdata, - u32 userdatalen, - void(*onchannel_callback)(void *context), - void *context); - -extern void vmbus_close(struct vmbus_channel *channel); - -extern int vmbus_sendpacket(struct vmbus_channel *channel, - const void *buffer, - u32 bufferLen, - u64 requestid, - enum vmbus_packet_type type, - u32 flags); - -extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, - struct hv_page_buffer pagebuffers[], - u32 pagecount, - void *buffer, - u32 bufferlen, - u64 requestid); - -extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, - struct hv_multipage_buffer *mpb, - void *buffer, - u32 bufferlen, - u64 requestid); - -extern int vmbus_establish_gpadl(struct vmbus_channel *channel, - void *kbuffer, - u32 size, - u32 *gpadl_handle); - -extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, - u32 gpadl_handle); - -extern int vmbus_recvpacket(struct vmbus_channel *channel, - void *buffer, - u32 bufferlen, - u32 *buffer_actual_len, - u64 *requestid); - -extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, - void *buffer, - u32 bufferlen, - u32 *buffer_actual_len, - u64 *requestid); - - -extern void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debug); - -extern void vmbus_ontimer(unsigned long data); - - -#define LOWORD(dw) ((unsigned short)(dw)) -#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) - - -#define VMBUS 0x0001 -#define STORVSC 0x0002 -#define NETVSC 0x0004 -#define INPUTVSC 0x0008 -#define BLKVSC 0x0010 -#define VMBUS_DRV 0x0100 -#define STORVSC_DRV 0x0200 -#define NETVSC_DRV 0x0400 -#define INPUTVSC_DRV 0x0800 -#define BLKVSC_DRV 0x1000 - -#define ALL_MODULES (VMBUS |\ - STORVSC |\ - NETVSC |\ - INPUTVSC |\ - BLKVSC |\ - VMBUS_DRV |\ - STORVSC_DRV |\ - NETVSC_DRV |\ - INPUTVSC_DRV|\ - BLKVSC_DRV) - -/* Logging Level */ -#define ERROR_LVL 3 -#define WARNING_LVL 4 -#define INFO_LVL 6 -#define DEBUG_LVL 7 -#define DEBUG_LVL_ENTEREXIT 8 -#define DEBUG_RING_LVL 9 - -extern unsigned int vmbus_loglevel; - -#define DPRINT(mod, lvl, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (lvl <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_DBG(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_INFO(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (INFO_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_INFO #mod": " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_WARN(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ - } while (0) - -#define DPRINT_ERR(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ - __func__, ## args);\ - } while (0) - - - -struct hv_driver; -struct hv_device; - -struct hv_dev_port_info { - u32 int_mask; - u32 read_idx; - u32 write_idx; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - -struct hv_device_info { - u32 chn_id; - u32 chn_state; - uuid_le chn_type; - uuid_le chn_instance; - - u32 monitor_id; - u32 server_monitor_pending; - u32 server_monitor_latency; - u32 server_monitor_conn_id; - u32 client_monitor_pending; - u32 client_monitor_latency; - u32 client_monitor_conn_id; - - struct hv_dev_port_info inbound; - struct hv_dev_port_info outbound; -}; - -/* Base driver object */ -struct hv_driver { - const char *name; - - /* the device type supported by this driver */ - uuid_le dev_type; - const struct hv_vmbus_device_id *id_table; - - struct device_driver driver; - - int (*probe)(struct hv_device *, const struct hv_vmbus_device_id *); - int (*remove)(struct hv_device *); - void (*shutdown)(struct hv_device *); - -}; - -/* Base device object */ -struct hv_device { - /* the device type id of this device */ - uuid_le dev_type; - - /* the device instance id of this device */ - uuid_le dev_instance; - - struct device device; - - struct vmbus_channel *channel; -}; - - -static inline struct hv_device *device_to_hv_device(struct device *d) -{ - return container_of(d, struct hv_device, device); -} - -static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) -{ - return container_of(d, struct hv_driver, driver); -} - -static inline void hv_set_drvdata(struct hv_device *dev, void *data) -{ - dev_set_drvdata(&dev->device, data); -} - -static inline void *hv_get_drvdata(struct hv_device *dev) -{ - return dev_get_drvdata(&dev->device); -} - -/* Vmbus interface */ -#define vmbus_driver_register(driver) \ - __vmbus_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) -int __must_check __vmbus_driver_register(struct hv_driver *hv_driver, - struct module *owner, - const char *mod_name); -void vmbus_driver_unregister(struct hv_driver *hv_driver); - -/** - * VMBUS_DEVICE - macro used to describe a specific hyperv vmbus device - * - * This macro is used to create a struct hv_vmbus_device_id that matches a - * specific device. - */ -#define VMBUS_DEVICE(g0, g1, g2, g3, g4, g5, g6, g7, \ - g8, g9, ga, gb, gc, gd, ge, gf) \ - .guid = { g0, g1, g2, g3, g4, g5, g6, g7, \ - g8, g9, ga, gb, gc, gd, ge, gf }, - -/* - * Common header for Hyper-V ICs - */ - -#define ICMSGTYPE_NEGOTIATE 0 -#define ICMSGTYPE_HEARTBEAT 1 -#define ICMSGTYPE_KVPEXCHANGE 2 -#define ICMSGTYPE_SHUTDOWN 3 -#define ICMSGTYPE_TIMESYNC 4 -#define ICMSGTYPE_VSS 5 - -#define ICMSGHDRFLAG_TRANSACTION 1 -#define ICMSGHDRFLAG_REQUEST 2 -#define ICMSGHDRFLAG_RESPONSE 4 - -#define HV_S_OK 0x00000000 -#define HV_E_FAIL 0x80004005 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 - -/* - * While we want to handle util services as regular devices, - * there is only one instance of each of these services; so - * we statically allocate the service specific state. - */ - -struct hv_util_service { - u8 *recv_buffer; - void (*util_cb)(void *); - int (*util_init)(struct hv_util_service *); - void (*util_deinit)(void); -}; - -struct vmbuspipe_hdr { - u32 flags; - u32 msgsize; -} __packed; - -struct ic_version { - u16 major; - u16 minor; -} __packed; - -struct icmsg_hdr { - struct ic_version icverframe; - u16 icmsgtype; - struct ic_version icvermsg; - u16 icmsgsize; - u32 status; - u8 ictransaction_id; - u8 icflags; - u8 reserved[2]; -} __packed; - -struct icmsg_negotiate { - u16 icframe_vercnt; - u16 icmsg_vercnt; - u32 reserved; - struct ic_version icversion_data[1]; /* any size array */ -} __packed; - -struct shutdown_msg_data { - u32 reason_code; - u32 timeout_seconds; - u32 flags; - u8 display_message[2048]; -} __packed; - -struct heartbeat_msg_data { - u64 seq_num; - u32 reserved[8]; -} __packed; - -/* Time Sync IC defs */ -#define ICTIMESYNCFLAG_PROBE 0 -#define ICTIMESYNCFLAG_SYNC 1 -#define ICTIMESYNCFLAG_SAMPLE 2 - -#ifdef __x86_64__ -#define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ -#else -#define WLTIMEDELTA 116444736000000000LL -#endif - -struct ictimesync_data { - u64 parenttime; - u64 childtime; - u64 roundtriptime; - u8 flags; -} __packed; - -struct hyperv_service_callback { - u8 msg_type; - char *log_msg; - uuid_le data; - struct vmbus_channel *channel; - void (*callback) (void *context); -}; - -extern void prep_negotiate_resp(struct icmsg_hdr *, - struct icmsg_negotiate *, u8 *); - -#endif /* _HYPERV_H */ diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h index 366dd2b32b13..ac1ec8405124 100644 --- a/drivers/staging/hv/hyperv_net.h +++ b/drivers/staging/hv/hyperv_net.h @@ -26,7 +26,7 @@ #define _HYPERV_NET_H #include -#include "hyperv.h" +#include /* Fwd declaration */ struct hv_netvsc_packet; diff --git a/drivers/staging/hv/hyperv_vmbus.h b/drivers/staging/hv/hyperv_vmbus.h deleted file mode 100644 index 3d2d836c3cc8..000000000000 --- a/drivers/staging/hv/hyperv_vmbus.h +++ /dev/null @@ -1,629 +0,0 @@ -/* - * - * Copyright (c) 2011, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * K. Y. Srinivasan - * - */ - -#ifndef _HYPERV_VMBUS_H -#define _HYPERV_VMBUS_H - -#include -#include -#include - -#include "hyperv.h" - -/* - * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent - * is set by CPUID(HVCPUID_VERSION_FEATURES). - */ -enum hv_cpuid_function { - HVCPUID_VERSION_FEATURES = 0x00000001, - HVCPUID_VENDOR_MAXFUNCTION = 0x40000000, - HVCPUID_INTERFACE = 0x40000001, - - /* - * The remaining functions depend on the value of - * HVCPUID_INTERFACE - */ - HVCPUID_VERSION = 0x40000002, - HVCPUID_FEATURES = 0x40000003, - HVCPUID_ENLIGHTENMENT_INFO = 0x40000004, - HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005, -}; - -/* Define version of the synthetic interrupt controller. */ -#define HV_SYNIC_VERSION (1) - -/* Define the expected SynIC version. */ -#define HV_SYNIC_VERSION_1 (0x1) - -/* Define synthetic interrupt controller message constants. */ -#define HV_MESSAGE_SIZE (256) -#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240) -#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) -#define HV_ANY_VP (0xFFFFFFFF) - -/* Define synthetic interrupt controller flag constants. */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) -#define HV_EVENT_FLAGS_BYTE_COUNT (256) -#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) - -/* Define hypervisor message types. */ -enum hv_message_type { - HVMSG_NONE = 0x00000000, - - /* Memory access messages. */ - HVMSG_UNMAPPED_GPA = 0x80000000, - HVMSG_GPA_INTERCEPT = 0x80000001, - - /* Timer notification messages. */ - HVMSG_TIMER_EXPIRED = 0x80000010, - - /* Error messages. */ - HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020, - HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021, - HVMSG_UNSUPPORTED_FEATURE = 0x80000022, - - /* Trace buffer complete messages. */ - HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040, - - /* Platform-specific processor intercept messages. */ - HVMSG_X64_IOPORT_INTERCEPT = 0x80010000, - HVMSG_X64_MSR_INTERCEPT = 0x80010001, - HVMSG_X64_CPUID_INTERCEPT = 0x80010002, - HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003, - HVMSG_X64_APIC_EOI = 0x80010004, - HVMSG_X64_LEGACY_FP_ERROR = 0x80010005 -}; - -/* Define the number of synthetic interrupt sources. */ -#define HV_SYNIC_SINT_COUNT (16) -#define HV_SYNIC_STIMER_COUNT (4) - -/* Define invalid partition identifier. */ -#define HV_PARTITION_ID_INVALID ((u64)0x0) - -/* Define connection identifier type. */ -union hv_connection_id { - u32 asu32; - struct { - u32 id:24; - u32 reserved:8; - } u; -}; - -/* Define port identifier type. */ -union hv_port_id { - u32 asu32; - struct { - u32 id:24; - u32 reserved:8; - } u ; -}; - -/* Define port type. */ -enum hv_port_type { - HVPORT_MSG = 1, - HVPORT_EVENT = 2, - HVPORT_MONITOR = 3 -}; - -/* Define port information structure. */ -struct hv_port_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u32 target_sint; - u32 target_vp; - u64 rsvdz; - } message_port_info; - struct { - u32 target_sint; - u32 target_vp; - u16 base_flag_bumber; - u16 flag_count; - u32 rsvdz; - } event_port_info; - struct { - u64 monitor_address; - u64 rsvdz; - } monitor_port_info; - }; -}; - -struct hv_connection_info { - enum hv_port_type port_type; - u32 padding; - union { - struct { - u64 rsvdz; - } message_connection_info; - struct { - u64 rsvdz; - } event_connection_info; - struct { - u64 monitor_address; - } monitor_connection_info; - }; -}; - -/* Define synthetic interrupt controller message flags. */ -union hv_message_flags { - u8 asu8; - struct { - u8 msg_pending:1; - u8 reserved:7; - }; -}; - -/* Define synthetic interrupt controller message header. */ -struct hv_message_header { - enum hv_message_type message_type; - u8 payload_size; - union hv_message_flags message_flags; - u8 reserved[2]; - union { - u64 sender; - union hv_port_id port; - }; -}; - -/* Define timer message payload structure. */ -struct hv_timer_message_payload { - u32 timer_index; - u32 reserved; - u64 expiration_time; /* When the timer expired */ - u64 delivery_time; /* When the message was delivered */ -}; - -/* Define synthetic interrupt controller message format. */ -struct hv_message { - struct hv_message_header header; - union { - u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; - } u ; -}; - -/* Define the number of message buffers associated with each port. */ -#define HV_PORT_MESSAGE_BUFFER_COUNT (16) - -/* Define the synthetic interrupt message page layout. */ -struct hv_message_page { - struct hv_message sint_message[HV_SYNIC_SINT_COUNT]; -}; - -/* Define the synthetic interrupt controller event flags format. */ -union hv_synic_event_flags { - u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; - u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; -}; - -/* Define the synthetic interrupt flags page layout. */ -struct hv_synic_event_flags_page { - union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; -}; - -/* Define SynIC control register. */ -union hv_synic_scontrol { - u64 as_uint64; - struct { - u64 enable:1; - u64 reserved:63; - }; -}; - -/* Define synthetic interrupt source. */ -union hv_synic_sint { - u64 as_uint64; - struct { - u64 vector:8; - u64 reserved1:8; - u64 masked:1; - u64 auto_eoi:1; - u64 reserved2:46; - }; -}; - -/* Define the format of the SIMP register */ -union hv_synic_simp { - u64 as_uint64; - struct { - u64 simp_enabled:1; - u64 preserved:11; - u64 base_simp_gpa:52; - }; -}; - -/* Define the format of the SIEFP register */ -union hv_synic_siefp { - u64 as_uint64; - struct { - u64 siefp_enabled:1; - u64 preserved:11; - u64 base_siefp_gpa:52; - }; -}; - -/* Definitions for the monitored notification facility */ -union hv_monitor_trigger_group { - u64 as_uint64; - struct { - u32 pending; - u32 armed; - }; -}; - -struct hv_monitor_parameter { - union hv_connection_id connectionid; - u16 flagnumber; - u16 rsvdz; -}; - -union hv_monitor_trigger_state { - u32 asu32; - - struct { - u32 group_enable:4; - u32 rsvdz:28; - }; -}; - -/* struct hv_monitor_page Layout */ -/* ------------------------------------------------------ */ -/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */ -/* | 8 | TriggerGroup[0] | */ -/* | 10 | TriggerGroup[1] | */ -/* | 18 | TriggerGroup[2] | */ -/* | 20 | TriggerGroup[3] | */ -/* | 28 | Rsvd2[0] | */ -/* | 30 | Rsvd2[1] | */ -/* | 38 | Rsvd2[2] | */ -/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */ -/* | ... | */ -/* | 240 | Latency[0][0..3] | */ -/* | 340 | Rsvz3[0] | */ -/* | 440 | Parameter[0][0] | */ -/* | 448 | Parameter[0][1] | */ -/* | ... | */ -/* | 840 | Rsvd4[0] | */ -/* ------------------------------------------------------ */ -struct hv_monitor_page { - union hv_monitor_trigger_state trigger_state; - u32 rsvdz1; - - union hv_monitor_trigger_group trigger_group[4]; - u64 rsvdz2[3]; - - s32 next_checktime[4][32]; - - u16 latency[4][32]; - u64 rsvdz3[32]; - - struct hv_monitor_parameter parameter[4][32]; - - u8 rsvdz4[1984]; -}; - -/* Declare the various hypercall operations. */ -enum hv_call_code { - HVCALL_POST_MESSAGE = 0x005c, - HVCALL_SIGNAL_EVENT = 0x005d, -}; - -/* Definition of the hv_post_message hypercall input structure. */ -struct hv_input_post_message { - union hv_connection_id connectionid; - u32 reserved; - enum hv_message_type message_type; - u32 payload_size; - u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; -}; - -/* Definition of the hv_signal_event hypercall input structure. */ -struct hv_input_signal_event { - union hv_connection_id connectionid; - u16 flag_number; - u16 rsvdz; -}; - -/* - * Versioning definitions used for guests reporting themselves to the - * hypervisor, and visa versa. - */ - -/* Version info reported by guest OS's */ -enum hv_guest_os_vendor { - HVGUESTOS_VENDOR_MICROSOFT = 0x0001 -}; - -enum hv_guest_os_microsoft_ids { - HVGUESTOS_MICROSOFT_UNDEFINED = 0x00, - HVGUESTOS_MICROSOFT_MSDOS = 0x01, - HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02, - HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03, - HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04, - HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05 -}; - -/* - * Declare the MSR used to identify the guest OS. - */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -union hv_x64_msr_guest_os_id_contents { - u64 as_uint64; - struct { - u64 build_number:16; - u64 service_version:8; /* Service Pack, etc. */ - u64 minor_version:8; - u64 major_version:8; - u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */ - u64 vendor_id:16; /* enum hv_guest_os_vendor */ - }; -}; - -/* - * Declare the MSR used to setup pages used to communicate with the hypervisor. - */ -#define HV_X64_MSR_HYPERCALL 0x40000001 - -union hv_x64_msr_hypercall_contents { - u64 as_uint64; - struct { - u64 enable:1; - u64 reserved:11; - u64 guest_physical_address:52; - }; -}; - - -enum { - VMBUS_MESSAGE_CONNECTION_ID = 1, - VMBUS_MESSAGE_PORT_ID = 1, - VMBUS_EVENT_CONNECTION_ID = 2, - VMBUS_EVENT_PORT_ID = 2, - VMBUS_MONITOR_CONNECTION_ID = 3, - VMBUS_MONITOR_PORT_ID = 3, - VMBUS_MESSAGE_SINT = 2, -}; - -/* #defines */ - -#define HV_PRESENT_BIT 0x80000000 - -#define HV_LINUX_GUEST_ID_LO 0x00000000 -#define HV_LINUX_GUEST_ID_HI 0xB16B00B5 -#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \ - HV_LINUX_GUEST_ID_LO) - -#define HV_CPU_POWER_MANAGEMENT (1 << 0) -#define HV_RECOMMENDATIONS_MAX 4 - -#define HV_X64_MAX 5 -#define HV_CAPS_MAX 8 - - -#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64) - - -/* Service definitions */ - -#define HV_SERVICE_PARENT_PORT (0) -#define HV_SERVICE_PARENT_CONNECTION (0) - -#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0) -#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1) -#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2) -#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3) - -#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1) -#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2) -#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3) -#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4) -#define HV_SERVICE_MAX_MESSAGE_ID (4) - -#define HV_SERVICE_PROTOCOL_VERSION (0x0010) -#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64 - -/* #define VMBUS_REVISION_NUMBER 6 */ - -/* Our local vmbus's port and connection id. Anything >0 is fine */ -/* #define VMBUS_PORT_ID 11 */ - -/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */ -static const uuid_le VMBUS_SERVICE_ID = { - .b = { - 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, - 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4 - }, -}; - -#define MAX_NUM_CPUS 32 - - -struct hv_input_signal_event_buffer { - u64 align8; - struct hv_input_signal_event event; -}; - -struct hv_context { - /* We only support running on top of Hyper-V - * So at this point this really can only contain the Hyper-V ID - */ - u64 guestid; - - void *hypercall_page; - - bool synic_initialized; - - /* - * This is used as an input param to HvCallSignalEvent hypercall. The - * input param is immutable in our usage and must be dynamic mem (vs - * stack or global). */ - struct hv_input_signal_event_buffer *signal_event_buffer; - /* 8-bytes aligned of the buffer above */ - struct hv_input_signal_event *signal_event_param; - - void *synic_message_page[MAX_NUM_CPUS]; - void *synic_event_page[MAX_NUM_CPUS]; -}; - -extern struct hv_context hv_context; - - -/* Hv Interface */ - -extern int hv_init(void); - -extern void hv_cleanup(void); - -extern u16 hv_post_message(union hv_connection_id connection_id, - enum hv_message_type message_type, - void *payload, size_t payload_size); - -extern u16 hv_signal_event(void); - -extern void hv_synic_init(void *irqarg); - -extern void hv_synic_cleanup(void *arg); - - -/* Interface */ - - -int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, - u32 buflen); - -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); - -int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, - u32 buflen); - -int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, - void *buffer, - u32 buflen, - u32 offset); - -u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); - -void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); - -void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, - struct hv_ring_buffer_debug_info *debug_info); - -/* - * Maximum channels is determined by the size of the interrupt page - * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt - * and the other is receive endpoint interrupt - */ -#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */ - -/* The value here must be in multiple of 32 */ -/* TODO: Need to make this configurable */ -#define MAX_NUM_CHANNELS_SUPPORTED 256 - - -enum vmbus_connect_state { - DISCONNECTED, - CONNECTING, - CONNECTED, - DISCONNECTING -}; - -#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT - -struct vmbus_connection { - enum vmbus_connect_state conn_state; - - atomic_t next_gpadl_handle; - - /* - * Represents channel interrupts. Each bit position represents a - * channel. When a channel sends an interrupt via VMBUS, it finds its - * bit in the sendInterruptPage, set it and calls Hv to generate a port - * event. The other end receives the port event and parse the - * recvInterruptPage to see which bit is set - */ - void *int_page; - void *send_int_page; - void *recv_int_page; - - /* - * 2 pages - 1st page for parent->child notification and 2nd - * is child->parent notification - */ - void *monitor_pages; - struct list_head chn_msg_list; - spinlock_t channelmsg_lock; - - /* List of channels */ - struct list_head chn_list; - spinlock_t channel_lock; - - struct workqueue_struct *work_queue; -}; - - -struct vmbus_msginfo { - /* Bookkeeping stuff */ - struct list_head msglist_entry; - - /* The message itself */ - unsigned char msg[0]; -}; - - -extern struct vmbus_connection vmbus_connection; - -/* General vmbus interface */ - -struct hv_device *vmbus_device_create(uuid_le *type, - uuid_le *instance, - struct vmbus_channel *channel); - -int vmbus_device_register(struct hv_device *child_device_obj); -void vmbus_device_unregister(struct hv_device *device_obj); - -/* static void */ -/* VmbusChildDeviceDestroy( */ -/* struct hv_device *); */ - -struct vmbus_channel *relid2channel(u32 relid); - - -/* Connection interface */ - -int vmbus_connect(void); - -int vmbus_post_msg(void *buffer, size_t buflen); - -int vmbus_set_event(u32 child_relid); - -void vmbus_on_event(unsigned long data); - - -#endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/staging/hv/ring_buffer.c b/drivers/staging/hv/ring_buffer.c deleted file mode 100644 index 70e2e66fec71..000000000000 --- a/drivers/staging/hv/ring_buffer.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * K. Y. Srinivasan - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - - -/* #defines */ - - -/* Amount of space to write to */ -#define BYTES_AVAIL_TO_WRITE(r, w, z) \ - ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) - - -/* - * - * hv_get_ringbuffer_availbytes() - * - * Get number of bytes available to read and to write to - * for the specified ring buffer - */ -static inline void -hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, - u32 *read, u32 *write) -{ - u32 read_loc, write_loc; - - smp_read_barrier_depends(); - - /* Capture the read/write indices before they changed */ - read_loc = rbi->ring_buffer->read_index; - write_loc = rbi->ring_buffer->write_index; - - *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize); - *read = rbi->ring_datasize - *write; -} - -/* - * hv_get_next_write_location() - * - * Get the next write location for the specified ring buffer - * - */ -static inline u32 -hv_get_next_write_location(struct hv_ring_buffer_info *ring_info) -{ - u32 next = ring_info->ring_buffer->write_index; - - return next; -} - -/* - * hv_set_next_write_location() - * - * Set the next write location for the specified ring buffer - * - */ -static inline void -hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, - u32 next_write_location) -{ - ring_info->ring_buffer->write_index = next_write_location; -} - -/* - * hv_get_next_read_location() - * - * Get the next read location for the specified ring buffer - */ -static inline u32 -hv_get_next_read_location(struct hv_ring_buffer_info *ring_info) -{ - u32 next = ring_info->ring_buffer->read_index; - - return next; -} - -/* - * hv_get_next_readlocation_withoffset() - * - * Get the next read location + offset for the specified ring buffer. - * This allows the caller to skip - */ -static inline u32 -hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info, - u32 offset) -{ - u32 next = ring_info->ring_buffer->read_index; - - next += offset; - next %= ring_info->ring_datasize; - - return next; -} - -/* - * - * hv_set_next_read_location() - * - * Set the next read location for the specified ring buffer - * - */ -static inline void -hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, - u32 next_read_location) -{ - ring_info->ring_buffer->read_index = next_read_location; -} - - -/* - * - * hv_get_ring_buffer() - * - * Get the start of the ring buffer - */ -static inline void * -hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info) -{ - return (void *)ring_info->ring_buffer->buffer; -} - - -/* - * - * hv_get_ring_buffersize() - * - * Get the size of the ring buffer - */ -static inline u32 -hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info) -{ - return ring_info->ring_datasize; -} - -/* - * - * hv_get_ring_bufferindices() - * - * Get the read and write indices as u64 of the specified ring buffer - * - */ -static inline u64 -hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) -{ - return (u64)ring_info->ring_buffer->write_index << 32; -} - - -/* - * - * hv_dump_ring_info() - * - * Dump out to console the ring buffer info - * - */ -void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) -{ - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - - hv_get_ringbuffer_availbytes(ring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - DPRINT(VMBUS, - DEBUG_RING_LVL, - "%s <>", - prefix, - ring_info, - ring_info->ring_buffer->buffer, - bytes_avail_towrite, - bytes_avail_toread, - ring_info->ring_buffer->read_index, - ring_info->ring_buffer->write_index); -} - - -/* - * - * hv_copyfrom_ringbuffer() - * - * Helper routine to copy to source from ring buffer. - * Assume there is enough room. Handles wrap-around in src case only!! - * - */ -static u32 hv_copyfrom_ringbuffer( - struct hv_ring_buffer_info *ring_info, - void *dest, - u32 destlen, - u32 start_read_offset) -{ - void *ring_buffer = hv_get_ring_buffer(ring_info); - u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); - - u32 frag_len; - - /* wrap-around detected at the src */ - if (destlen > ring_buffer_size - start_read_offset) { - frag_len = ring_buffer_size - start_read_offset; - - memcpy(dest, ring_buffer + start_read_offset, frag_len); - memcpy(dest + frag_len, ring_buffer, destlen - frag_len); - } else - - memcpy(dest, ring_buffer + start_read_offset, destlen); - - - start_read_offset += destlen; - start_read_offset %= ring_buffer_size; - - return start_read_offset; -} - - -/* - * - * hv_copyto_ringbuffer() - * - * Helper routine to copy from source to ring buffer. - * Assume there is enough room. Handles wrap-around in dest case only!! - * - */ -static u32 hv_copyto_ringbuffer( - struct hv_ring_buffer_info *ring_info, - u32 start_write_offset, - void *src, - u32 srclen) -{ - void *ring_buffer = hv_get_ring_buffer(ring_info); - u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); - u32 frag_len; - - /* wrap-around detected! */ - if (srclen > ring_buffer_size - start_write_offset) { - frag_len = ring_buffer_size - start_write_offset; - memcpy(ring_buffer + start_write_offset, src, frag_len); - memcpy(ring_buffer, src + frag_len, srclen - frag_len); - } else - memcpy(ring_buffer + start_write_offset, src, srclen); - - start_write_offset += srclen; - start_write_offset %= ring_buffer_size; - - return start_write_offset; -} - -/* - * - * hv_ringbuffer_get_debuginfo() - * - * Get various debug metrics for the specified ring buffer - * - */ -void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, - struct hv_ring_buffer_debug_info *debug_info) -{ - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - - if (ring_info->ring_buffer) { - hv_get_ringbuffer_availbytes(ring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - debug_info->bytes_avail_toread = bytes_avail_toread; - debug_info->bytes_avail_towrite = bytes_avail_towrite; - debug_info->current_read_index = - ring_info->ring_buffer->read_index; - debug_info->current_write_index = - ring_info->ring_buffer->write_index; - debug_info->current_interrupt_mask = - ring_info->ring_buffer->interrupt_mask; - } -} - - -/* - * - * hv_get_ringbuffer_interrupt_mask() - * - * Get the interrupt mask for the specified ring buffer - * - */ -u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) -{ - return rbi->ring_buffer->interrupt_mask; -} - -/* - * - * hv_ringbuffer_init() - * - *Initialize the ring buffer - * - */ -int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, - void *buffer, u32 buflen) -{ - if (sizeof(struct hv_ring_buffer) != PAGE_SIZE) - return -EINVAL; - - memset(ring_info, 0, sizeof(struct hv_ring_buffer_info)); - - ring_info->ring_buffer = (struct hv_ring_buffer *)buffer; - ring_info->ring_buffer->read_index = - ring_info->ring_buffer->write_index = 0; - - ring_info->ring_size = buflen; - ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer); - - spin_lock_init(&ring_info->ring_lock); - - return 0; -} - -/* - * - * hv_ringbuffer_cleanup() - * - * Cleanup the ring buffer - * - */ -void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) -{ -} - -/* - * - * hv_ringbuffer_write() - * - * Write to the ring buffer - * - */ -int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, - struct scatterlist *sglist, u32 sgcount) -{ - int i = 0; - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - u32 totalbytes_towrite = 0; - - struct scatterlist *sg; - u32 next_write_location; - u64 prev_indices = 0; - unsigned long flags; - - for_each_sg(sglist, sg, sgcount, i) - { - totalbytes_towrite += sg->length; - } - - totalbytes_towrite += sizeof(u64); - - spin_lock_irqsave(&outring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(outring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - - /* If there is only room for the packet, assume it is full. */ - /* Otherwise, the next time around, we think the ring buffer */ - /* is empty since the read index == write index */ - if (bytes_avail_towrite <= totalbytes_towrite) { - spin_unlock_irqrestore(&outring_info->ring_lock, flags); - return -EAGAIN; - } - - /* Write to the ring buffer */ - next_write_location = hv_get_next_write_location(outring_info); - - for_each_sg(sglist, sg, sgcount, i) - { - next_write_location = hv_copyto_ringbuffer(outring_info, - next_write_location, - sg_virt(sg), - sg->length); - } - - /* Set previous packet start */ - prev_indices = hv_get_ring_bufferindices(outring_info); - - next_write_location = hv_copyto_ringbuffer(outring_info, - next_write_location, - &prev_indices, - sizeof(u64)); - - /* Make sure we flush all writes before updating the writeIndex */ - smp_wmb(); - - /* Now, update the write location */ - hv_set_next_write_location(outring_info, next_write_location); - - - spin_unlock_irqrestore(&outring_info->ring_lock, flags); - return 0; -} - - -/* - * - * hv_ringbuffer_peek() - * - * Read without advancing the read index - * - */ -int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, - void *Buffer, u32 buflen) -{ - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - u32 next_read_location = 0; - unsigned long flags; - - spin_lock_irqsave(&Inring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(Inring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - /* Make sure there is something to read */ - if (bytes_avail_toread < buflen) { - - spin_unlock_irqrestore(&Inring_info->ring_lock, flags); - - return -EAGAIN; - } - - /* Convert to byte offset */ - next_read_location = hv_get_next_read_location(Inring_info); - - next_read_location = hv_copyfrom_ringbuffer(Inring_info, - Buffer, - buflen, - next_read_location); - - spin_unlock_irqrestore(&Inring_info->ring_lock, flags); - - return 0; -} - - -/* - * - * hv_ringbuffer_read() - * - * Read and advance the read index - * - */ -int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, - u32 buflen, u32 offset) -{ - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - u32 next_read_location = 0; - u64 prev_indices = 0; - unsigned long flags; - - if (buflen <= 0) - return -EINVAL; - - spin_lock_irqsave(&inring_info->ring_lock, flags); - - hv_get_ringbuffer_availbytes(inring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - /* Make sure there is something to read */ - if (bytes_avail_toread < buflen) { - spin_unlock_irqrestore(&inring_info->ring_lock, flags); - - return -EAGAIN; - } - - next_read_location = - hv_get_next_readlocation_withoffset(inring_info, offset); - - next_read_location = hv_copyfrom_ringbuffer(inring_info, - buffer, - buflen, - next_read_location); - - next_read_location = hv_copyfrom_ringbuffer(inring_info, - &prev_indices, - sizeof(u64), - next_read_location); - - /* Make sure all reads are done before we update the read index since */ - /* the writer may start writing to the read area once the read index */ - /*is updated */ - smp_mb(); - - /* Update the read index */ - hv_set_next_read_location(inring_info, next_read_location); - - spin_unlock_irqrestore(&inring_info->ring_lock, flags); - - return 0; -} diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index e41271632609..af185abbaa73 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include #include -#include "hyperv.h" #define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; diff --git a/drivers/staging/hv/tools/hv_kvp_daemon.c b/drivers/staging/hv/tools/hv_kvp_daemon.c deleted file mode 100644 index 11224eddcdc2..000000000000 --- a/drivers/staging/hv/tools/hv_kvp_daemon.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * An implementation of key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * KYS: TODO. Need to register these in the kernel. - * - * The following definitions are shared with the in-kernel component; do not - * change any of this without making the corresponding changes in - * the KVP kernel component. - */ -#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - -/* - * KVP protocol: The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * We use this infrastructure for also supporting queries from user mode - * application for state that may be maintained in the KVP kernel component. - * - * XXXKYS: Have a shared header file between the user and kernel (TODO) - */ - -enum kvp_op { - KVP_REGISTER = 0, /* Register the user mode component*/ - KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ - KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ - KVP_USER_GET, /*User is requesting the value for the specified key*/ - KVP_USER_SET /*User is providing the value for the specified key*/ -}; - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 - -struct hv_ku_msg { - __u32 kvp_index; - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - -enum key_index { - FullyQualifiedDomainName = 0, - IntegrationServicesVersion, /*This key is serviced in the kernel*/ - NetworkAddressIPv4, - NetworkAddressIPv6, - OSBuildNumber, - OSName, - OSMajorVersion, - OSMinorVersion, - OSVersion, - ProcessorArchitecture -}; - -/* - * End of shared definitions. - */ - -static char kvp_send_buffer[4096]; -static char kvp_recv_buffer[4096]; -static struct sockaddr_nl addr; - -static char *os_name = ""; -static char *os_major = ""; -static char *os_minor = ""; -static char *processor_arch; -static char *os_build; -static char *lic_version; -static struct utsname uts_buf; - -void kvp_get_os_info(void) -{ - FILE *file; - char *p, buf[512]; - - uname(&uts_buf); - os_build = uts_buf.release; - processor_arch = uts_buf.machine; - - /* - * The current windows host (win7) expects the build - * string to be of the form: x.y.z - * Strip additional information we may have. - */ - p = strchr(os_build, '-'); - if (p) - *p = '\0'; - - file = fopen("/etc/SuSE-release", "r"); - if (file != NULL) - goto kvp_osinfo_found; - file = fopen("/etc/redhat-release", "r"); - if (file != NULL) - goto kvp_osinfo_found; - /* - * Add code for other supported platforms. - */ - - /* - * We don't have information about the os. - */ - os_name = uts_buf.sysname; - return; - -kvp_osinfo_found: - /* up to three lines */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (!p) - goto done; - os_name = p; - - /* second line */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (!p) - goto done; - os_major = p; - - /* third line */ - p = fgets(buf, sizeof(buf), file); - if (p) { - p = strchr(buf, '\n'); - if (p) - *p = '\0'; - p = strdup(buf); - if (p) - os_minor = p; - } - } - } - -done: - fclose(file); - return; -} - -static int -kvp_get_ip_address(int family, char *buffer, int length) -{ - struct ifaddrs *ifap; - struct ifaddrs *curp; - int ipv4_len = strlen("255.255.255.255") + 1; - int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1; - int offset = 0; - const char *str; - char tmp[50]; - int error = 0; - - /* - * On entry into this function, the buffer is capable of holding the - * maximum key value (2048 bytes). - */ - - if (getifaddrs(&ifap)) { - strcpy(buffer, "getifaddrs failed\n"); - return 1; - } - - curp = ifap; - while (curp != NULL) { - if ((curp->ifa_addr != NULL) && - (curp->ifa_addr->sa_family == family)) { - if (family == AF_INET) { - struct sockaddr_in *addr = - (struct sockaddr_in *) curp->ifa_addr; - - str = inet_ntop(family, &addr->sin_addr, - tmp, 50); - if (str == NULL) { - strcpy(buffer, "inet_ntop failed\n"); - error = 1; - goto getaddr_done; - } - if (offset == 0) - strcpy(buffer, tmp); - else - strcat(buffer, tmp); - strcat(buffer, ";"); - - offset += strlen(str) + 1; - if ((length - offset) < (ipv4_len + 1)) - goto getaddr_done; - - } else { - - /* - * We only support AF_INET and AF_INET6 - * and the list of addresses is separated by a ";". - */ - struct sockaddr_in6 *addr = - (struct sockaddr_in6 *) curp->ifa_addr; - - str = inet_ntop(family, - &addr->sin6_addr.s6_addr, - tmp, 50); - if (str == NULL) { - strcpy(buffer, "inet_ntop failed\n"); - error = 1; - goto getaddr_done; - } - if (offset == 0) - strcpy(buffer, tmp); - else - strcat(buffer, tmp); - strcat(buffer, ";"); - offset += strlen(str) + 1; - if ((length - offset) < (ipv6_len + 1)) - goto getaddr_done; - - } - - } - curp = curp->ifa_next; - } - -getaddr_done: - freeifaddrs(ifap); - return error; -} - - -static int -kvp_get_domain_name(char *buffer, int length) -{ - struct addrinfo hints, *info ; - int error = 0; - - gethostname(buffer, length); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - error = getaddrinfo(buffer, NULL, &hints, &info); - if (error != 0) { - strcpy(buffer, "getaddrinfo failed\n"); - return error; - } - strcpy(buffer, info->ai_canonname); - freeaddrinfo(info); - return error; -} - -static int -netlink_send(int fd, struct cn_msg *msg) -{ - struct nlmsghdr *nlh; - unsigned int size; - struct msghdr message; - char buffer[64]; - struct iovec iov[2]; - - size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); - - nlh = (struct nlmsghdr *)buffer; - nlh->nlmsg_seq = 0; - nlh->nlmsg_pid = getpid(); - nlh->nlmsg_type = NLMSG_DONE; - nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); - nlh->nlmsg_flags = 0; - - iov[0].iov_base = nlh; - iov[0].iov_len = sizeof(*nlh); - - iov[1].iov_base = msg; - iov[1].iov_len = size; - - memset(&message, 0, sizeof(message)); - message.msg_name = &addr; - message.msg_namelen = sizeof(addr); - message.msg_iov = iov; - message.msg_iovlen = 2; - - return sendmsg(fd, &message, 0); -} - -int main(void) -{ - int fd, len, sock_opt; - int error; - struct cn_msg *message; - struct pollfd pfd; - struct nlmsghdr *incoming_msg; - struct cn_msg *incoming_cn_msg; - struct hv_ku_msg *hv_msg; - char *p; - char *key_value; - char *key_name; - - daemon(1, 0); - openlog("KVP", 0, LOG_USER); - syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); - /* - * Retrieve OS release information. - */ - kvp_get_os_info(); - - fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); - if (fd < 0) { - syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); - exit(-1); - } - addr.nl_family = AF_NETLINK; - addr.nl_pad = 0; - addr.nl_pid = 0; - addr.nl_groups = CN_KVP_IDX; - - - error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); - if (error < 0) { - syslog(LOG_ERR, "bind failed; error:%d", error); - close(fd); - exit(-1); - } - sock_opt = addr.nl_groups; - setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); - /* - * Register ourselves with the kernel. - */ - message = (struct cn_msg *)kvp_send_buffer; - message->id.idx = CN_KVP_IDX; - message->id.val = CN_KVP_VAL; - message->seq = KVP_REGISTER; - message->ack = 0; - message->len = 0; - - len = netlink_send(fd, message); - if (len < 0) { - syslog(LOG_ERR, "netlink_send failed; error:%d", len); - close(fd); - exit(-1); - } - - pfd.fd = fd; - - while (1) { - pfd.events = POLLIN; - pfd.revents = 0; - poll(&pfd, 1, -1); - - len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0); - - if (len < 0) { - syslog(LOG_ERR, "recv failed; error:%d", len); - close(fd); - return -1; - } - - incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; - incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); - - switch (incoming_cn_msg->seq) { - case KVP_REGISTER: - /* - * Driver is registering with us; stash away the version - * information. - */ - p = (char *)incoming_cn_msg->data; - lic_version = malloc(strlen(p) + 1); - if (lic_version) { - strcpy(lic_version, p); - syslog(LOG_INFO, "KVP LIC Version: %s", - lic_version); - } else { - syslog(LOG_ERR, "malloc failed"); - } - continue; - - case KVP_KERNEL_GET: - break; - default: - continue; - } - - hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; - key_name = (char *)hv_msg->kvp_key; - key_value = (char *)hv_msg->kvp_value; - - switch (hv_msg->kvp_index) { - case FullyQualifiedDomainName: - kvp_get_domain_name(key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "FullyQualifiedDomainName"); - break; - case IntegrationServicesVersion: - strcpy(key_name, "IntegrationServicesVersion"); - strcpy(key_value, lic_version); - break; - case NetworkAddressIPv4: - kvp_get_ip_address(AF_INET, key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "NetworkAddressIPv4"); - break; - case NetworkAddressIPv6: - kvp_get_ip_address(AF_INET6, key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); - strcpy(key_name, "NetworkAddressIPv6"); - break; - case OSBuildNumber: - strcpy(key_value, os_build); - strcpy(key_name, "OSBuildNumber"); - break; - case OSName: - strcpy(key_value, os_name); - strcpy(key_name, "OSName"); - break; - case OSMajorVersion: - strcpy(key_value, os_major); - strcpy(key_name, "OSMajorVersion"); - break; - case OSMinorVersion: - strcpy(key_value, os_minor); - strcpy(key_name, "OSMinorVersion"); - break; - case OSVersion: - strcpy(key_value, os_build); - strcpy(key_name, "OSVersion"); - break; - case ProcessorArchitecture: - strcpy(key_value, processor_arch); - strcpy(key_name, "ProcessorArchitecture"); - break; - default: - strcpy(key_value, "Unknown Key"); - /* - * We use a null key name to terminate enumeration. - */ - strcpy(key_name, ""); - break; - } - /* - * Send the value back to the kernel. The response is - * already in the receive buffer. Update the cn_msg header to - * reflect the key value that has been added to the message - */ - - incoming_cn_msg->id.idx = CN_KVP_IDX; - incoming_cn_msg->id.val = CN_KVP_VAL; - incoming_cn_msg->seq = KVP_USER_SET; - incoming_cn_msg->ack = 0; - incoming_cn_msg->len = sizeof(struct hv_ku_msg); - - len = netlink_send(fd, incoming_cn_msg); - if (len < 0) { - syslog(LOG_ERR, "net_link send failed; error:%d", len); - exit(-1); - } - } - -} diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c deleted file mode 100644 index d2562afcce4c..000000000000 --- a/drivers/staging/hv/vmbus_drv.c +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang - * Hank Janssen - * K. Y. Srinivasan - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hyperv.h" -#include "hyperv_vmbus.h" - - -static struct acpi_device *hv_acpi_dev; - -static struct tasklet_struct msg_dpc; -static struct tasklet_struct event_dpc; - -unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); -EXPORT_SYMBOL(vmbus_loglevel); - -static struct completion probe_event; -static int irq; - -static void get_channel_info(struct hv_device *device, - struct hv_device_info *info) -{ - struct vmbus_channel_debug_info debug_info; - - if (!device->channel) - return; - - vmbus_get_debug_info(device->channel, &debug_info); - - info->chn_id = debug_info.relid; - info->chn_state = debug_info.state; - memcpy(&info->chn_type, &debug_info.interfacetype, - sizeof(uuid_le)); - memcpy(&info->chn_instance, &debug_info.interface_instance, - sizeof(uuid_le)); - - info->monitor_id = debug_info.monitorid; - - info->server_monitor_pending = debug_info.servermonitor_pending; - info->server_monitor_latency = debug_info.servermonitor_latency; - info->server_monitor_conn_id = debug_info.servermonitor_connectionid; - - info->client_monitor_pending = debug_info.clientmonitor_pending; - info->client_monitor_latency = debug_info.clientmonitor_latency; - info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; - - info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; - info->inbound.read_idx = debug_info.inbound.current_read_index; - info->inbound.write_idx = debug_info.inbound.current_write_index; - info->inbound.bytes_avail_toread = - debug_info.inbound.bytes_avail_toread; - info->inbound.bytes_avail_towrite = - debug_info.inbound.bytes_avail_towrite; - - info->outbound.int_mask = - debug_info.outbound.current_interrupt_mask; - info->outbound.read_idx = debug_info.outbound.current_read_index; - info->outbound.write_idx = debug_info.outbound.current_write_index; - info->outbound.bytes_avail_toread = - debug_info.outbound.bytes_avail_toread; - info->outbound.bytes_avail_towrite = - debug_info.outbound.bytes_avail_towrite; -} - -#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) -static void print_alias_name(struct hv_device *hv_dev, char *alias_name) -{ - int i; - for (i = 0; i < VMBUS_ALIAS_LEN; i += 2) - sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); -} - -/* - * vmbus_show_device_attr - Show the device attribute in sysfs. - * - * This is invoked when user does a - * "cat /sys/bus/vmbus/devices//" - */ -static ssize_t vmbus_show_device_attr(struct device *dev, - struct device_attribute *dev_attr, - char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - struct hv_device_info *device_info; - char alias_name[VMBUS_ALIAS_LEN + 1]; - int ret = 0; - - device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL); - if (!device_info) - return ret; - - get_channel_info(hv_dev, device_info); - - if (!strcmp(dev_attr->attr.name, "class_id")) { - ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}\n", - device_info->chn_type.b[3], - device_info->chn_type.b[2], - device_info->chn_type.b[1], - device_info->chn_type.b[0], - device_info->chn_type.b[5], - device_info->chn_type.b[4], - device_info->chn_type.b[7], - device_info->chn_type.b[6], - device_info->chn_type.b[8], - device_info->chn_type.b[9], - device_info->chn_type.b[10], - device_info->chn_type.b[11], - device_info->chn_type.b[12], - device_info->chn_type.b[13], - device_info->chn_type.b[14], - device_info->chn_type.b[15]); - } else if (!strcmp(dev_attr->attr.name, "device_id")) { - ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x%02x%02x}\n", - device_info->chn_instance.b[3], - device_info->chn_instance.b[2], - device_info->chn_instance.b[1], - device_info->chn_instance.b[0], - device_info->chn_instance.b[5], - device_info->chn_instance.b[4], - device_info->chn_instance.b[7], - device_info->chn_instance.b[6], - device_info->chn_instance.b[8], - device_info->chn_instance.b[9], - device_info->chn_instance.b[10], - device_info->chn_instance.b[11], - device_info->chn_instance.b[12], - device_info->chn_instance.b[13], - device_info->chn_instance.b[14], - device_info->chn_instance.b[15]); - } else if (!strcmp(dev_attr->attr.name, "modalias")) { - print_alias_name(hv_dev, alias_name); - ret = sprintf(buf, "vmbus:%s\n", alias_name); - } else if (!strcmp(dev_attr->attr.name, "state")) { - ret = sprintf(buf, "%d\n", device_info->chn_state); - } else if (!strcmp(dev_attr->attr.name, "id")) { - ret = sprintf(buf, "%d\n", device_info->chn_id); - } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); - } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.read_idx); - } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.write_idx); - } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->outbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->outbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->inbound.int_mask); - } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.read_idx); - } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.write_idx); - } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->inbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { - ret = sprintf(buf, "%d\n", device_info->monitor_id); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { - ret = sprintf(buf, "%d\n", device_info->server_monitor_pending); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { - ret = sprintf(buf, "%d\n", device_info->server_monitor_latency); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { - ret = sprintf(buf, "%d\n", - device_info->server_monitor_conn_id); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { - ret = sprintf(buf, "%d\n", device_info->client_monitor_pending); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { - ret = sprintf(buf, "%d\n", device_info->client_monitor_latency); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { - ret = sprintf(buf, "%d\n", - device_info->client_monitor_conn_id); - } - - kfree(device_info); - return ret; -} - -/* Set up per device attributes in /sys/bus/vmbus/devices/ */ -static struct device_attribute vmbus_device_attrs[] = { - __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR_NULL -}; - - -/* - * vmbus_uevent - add uevent for our device - * - * This routine is invoked when a device is added or removed on the vmbus to - * generate a uevent to udev in the userspace. The udev will then look at its - * rule and the uevent generated here to load the appropriate driver - * - * The alias string will be of the form vmbus:guid where guid is the string - * representation of the device guid (each byte of the guid will be - * represented with two hex characters. - */ -static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) -{ - struct hv_device *dev = device_to_hv_device(device); - int ret; - char alias_name[VMBUS_ALIAS_LEN + 1]; - - print_alias_name(dev, alias_name); - ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name); - return ret; -} - -static uuid_le null_guid; - -static inline bool is_null_guid(const __u8 *guid) -{ - if (memcmp(guid, &null_guid, sizeof(uuid_le))) - return false; - return true; -} - -/* - * Return a matching hv_vmbus_device_id pointer. - * If there is no match, return NULL. - */ -static const struct hv_vmbus_device_id *hv_vmbus_get_id( - const struct hv_vmbus_device_id *id, - __u8 *guid) -{ - for (; !is_null_guid(id->guid); id++) - if (!memcmp(&id->guid, guid, sizeof(uuid_le))) - return id; - - return NULL; -} - - - -/* - * vmbus_match - Attempt to match the specified device to the specified driver - */ -static int vmbus_match(struct device *device, struct device_driver *driver) -{ - struct hv_driver *drv = drv_to_hv_drv(driver); - struct hv_device *hv_dev = device_to_hv_device(device); - - if (hv_vmbus_get_id(drv->id_table, hv_dev->dev_type.b)) - return 1; - - return 0; -} - -/* - * vmbus_probe - Add the new vmbus's child device - */ -static int vmbus_probe(struct device *child_device) -{ - int ret = 0; - struct hv_driver *drv = - drv_to_hv_drv(child_device->driver); - struct hv_device *dev = device_to_hv_device(child_device); - const struct hv_vmbus_device_id *dev_id; - - dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b); - if (drv->probe) { - ret = drv->probe(dev, dev_id); - if (ret != 0) - pr_err("probe failed for device %s (%d)\n", - dev_name(child_device), ret); - - } else { - pr_err("probe not set for driver %s\n", - dev_name(child_device)); - ret = -ENODEV; - } - return ret; -} - -/* - * vmbus_remove - Remove a vmbus device - */ -static int vmbus_remove(struct device *child_device) -{ - struct hv_driver *drv = drv_to_hv_drv(child_device->driver); - struct hv_device *dev = device_to_hv_device(child_device); - - if (drv->remove) - drv->remove(dev); - else - pr_err("remove not set for driver %s\n", - dev_name(child_device)); - - return 0; -} - - -/* - * vmbus_shutdown - Shutdown a vmbus device - */ -static void vmbus_shutdown(struct device *child_device) -{ - struct hv_driver *drv; - struct hv_device *dev = device_to_hv_device(child_device); - - - /* The device may not be attached yet */ - if (!child_device->driver) - return; - - drv = drv_to_hv_drv(child_device->driver); - - if (drv->shutdown) - drv->shutdown(dev); - - return; -} - - -/* - * vmbus_device_release - Final callback release of the vmbus child device - */ -static void vmbus_device_release(struct device *device) -{ - struct hv_device *hv_dev = device_to_hv_device(device); - - kfree(hv_dev); - -} - -/* The one and only one */ -static struct bus_type hv_bus = { - .name = "vmbus", - .match = vmbus_match, - .shutdown = vmbus_shutdown, - .remove = vmbus_remove, - .probe = vmbus_probe, - .uevent = vmbus_uevent, - .dev_attrs = vmbus_device_attrs, -}; - -static const char *driver_name = "hyperv"; - - -struct onmessage_work_context { - struct work_struct work; - struct hv_message msg; -}; - -static void vmbus_onmessage_work(struct work_struct *work) -{ - struct onmessage_work_context *ctx; - - ctx = container_of(work, struct onmessage_work_context, - work); - vmbus_onmessage(&ctx->msg); - kfree(ctx); -} - -static void vmbus_on_msg_dpc(unsigned long data) -{ - int cpu = smp_processor_id(); - void *page_addr = hv_context.synic_message_page[cpu]; - struct hv_message *msg = (struct hv_message *)page_addr + - VMBUS_MESSAGE_SINT; - struct onmessage_work_context *ctx; - - while (1) { - if (msg->header.message_type == HVMSG_NONE) { - /* no msg */ - break; - } else { - ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); - if (ctx == NULL) - continue; - INIT_WORK(&ctx->work, vmbus_onmessage_work); - memcpy(&ctx->msg, msg, sizeof(*msg)); - queue_work(vmbus_connection.work_queue, &ctx->work); - } - - msg->header.message_type = HVMSG_NONE; - - /* - * Make sure the write to MessageType (ie set to - * HVMSG_NONE) happens before we read the - * MessagePending and EOMing. Otherwise, the EOMing - * will not deliver any more messages since there is - * no empty slot - */ - smp_mb(); - - if (msg->header.message_flags.msg_pending) { - /* - * This will cause message queue rescan to - * possibly deliver another msg from the - * hypervisor - */ - wrmsrl(HV_X64_MSR_EOM, 0); - } - } -} - -static irqreturn_t vmbus_isr(int irq, void *dev_id) -{ - int cpu = smp_processor_id(); - void *page_addr; - struct hv_message *msg; - union hv_synic_event_flags *event; - bool handled = false; - - /* - * Check for events before checking for messages. This is the order - * in which events and messages are checked in Windows guests on - * Hyper-V, and the Windows team suggested we do the same. - */ - - page_addr = hv_context.synic_event_page[cpu]; - event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; - - /* Since we are a child, we only need to check bit 0 */ - if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { - handled = true; - tasklet_schedule(&event_dpc); - } - - page_addr = hv_context.synic_message_page[cpu]; - msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; - - /* Check if there are actual msgs to be processed */ - if (msg->header.message_type != HVMSG_NONE) { - handled = true; - tasklet_schedule(&msg_dpc); - } - - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; -} - -/* - * vmbus_bus_init -Main vmbus driver initialization routine. - * - * Here, we - * - initialize the vmbus driver context - * - invoke the vmbus hv main init routine - * - get the irq resource - * - retrieve the channel offers - */ -static int vmbus_bus_init(int irq) -{ - int ret; - unsigned int vector; - - /* Hypervisor initialization...setup hypercall page..etc */ - ret = hv_init(); - if (ret != 0) { - pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); - return ret; - } - - tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); - tasklet_init(&event_dpc, vmbus_on_event, 0); - - ret = bus_register(&hv_bus); - if (ret) - goto err_cleanup; - - ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM, - driver_name, hv_acpi_dev); - - if (ret != 0) { - pr_err("Unable to request IRQ %d\n", - irq); - goto err_unregister; - } - - vector = IRQ0_VECTOR + irq; - - /* - * Notify the hypervisor of our irq and - * connect to the host. - */ - on_each_cpu(hv_synic_init, (void *)&vector, 1); - ret = vmbus_connect(); - if (ret) - goto err_irq; - - vmbus_request_offers(); - - return 0; - -err_irq: - free_irq(irq, hv_acpi_dev); - -err_unregister: - bus_unregister(&hv_bus); - -err_cleanup: - hv_cleanup(); - - return ret; -} - -/** - * __vmbus_child_driver_register - Register a vmbus's driver - * @drv: Pointer to driver structure you want to register - * @owner: owner module of the drv - * @mod_name: module name string - * - * Registers the given driver with Linux through the 'driver_register()' call - * and sets up the hyper-v vmbus handling for this driver. - * It will return the state of the 'driver_register()' call. - * - */ -int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, const char *mod_name) -{ - int ret; - - pr_info("registering driver %s\n", hv_driver->name); - - hv_driver->driver.name = hv_driver->name; - hv_driver->driver.owner = owner; - hv_driver->driver.mod_name = mod_name; - hv_driver->driver.bus = &hv_bus; - - ret = driver_register(&hv_driver->driver); - - vmbus_request_offers(); - - return ret; -} -EXPORT_SYMBOL_GPL(__vmbus_driver_register); - -/** - * vmbus_driver_unregister() - Unregister a vmbus's driver - * @drv: Pointer to driver structure you want to un-register - * - * Un-register the given driver that was previous registered with a call to - * vmbus_driver_register() - */ -void vmbus_driver_unregister(struct hv_driver *hv_driver) -{ - pr_info("unregistering driver %s\n", hv_driver->name); - - driver_unregister(&hv_driver->driver); - -} -EXPORT_SYMBOL_GPL(vmbus_driver_unregister); - -/* - * vmbus_device_create - Creates and registers a new child device - * on the vmbus. - */ -struct hv_device *vmbus_device_create(uuid_le *type, - uuid_le *instance, - struct vmbus_channel *channel) -{ - struct hv_device *child_device_obj; - - child_device_obj = kzalloc(sizeof(struct hv_device), GFP_KERNEL); - if (!child_device_obj) { - pr_err("Unable to allocate device object for child device\n"); - return NULL; - } - - child_device_obj->channel = channel; - memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le)); - memcpy(&child_device_obj->dev_instance, instance, - sizeof(uuid_le)); - - - return child_device_obj; -} - -/* - * vmbus_device_register - Register the child device - */ -int vmbus_device_register(struct hv_device *child_device_obj) -{ - int ret = 0; - - static atomic_t device_num = ATOMIC_INIT(0); - - dev_set_name(&child_device_obj->device, "vmbus_0_%d", - atomic_inc_return(&device_num)); - - child_device_obj->device.bus = &hv_bus; - child_device_obj->device.parent = &hv_acpi_dev->dev; - child_device_obj->device.release = vmbus_device_release; - - /* - * Register with the LDM. This will kick off the driver/device - * binding...which will eventually call vmbus_match() and vmbus_probe() - */ - ret = device_register(&child_device_obj->device); - - if (ret) - pr_err("Unable to register child device\n"); - else - pr_info("child device %s registered\n", - dev_name(&child_device_obj->device)); - - return ret; -} - -/* - * vmbus_device_unregister - Remove the specified child device - * from the vmbus. - */ -void vmbus_device_unregister(struct hv_device *device_obj) -{ - /* - * Kick off the process of unregistering the device. - * This will call vmbus_remove() and eventually vmbus_device_release() - */ - device_unregister(&device_obj->device); - - pr_info("child device %s unregistered\n", - dev_name(&device_obj->device)); -} - - -/* - * VMBUS is an acpi enumerated device. Get the the IRQ information - * from DSDT. - */ - -static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) -{ - - if (res->type == ACPI_RESOURCE_TYPE_IRQ) { - struct acpi_resource_irq *irqp; - irqp = &res->data.irq; - - *((unsigned int *)irq) = irqp->interrupts[0]; - } - - return AE_OK; -} - -static int vmbus_acpi_add(struct acpi_device *device) -{ - acpi_status result; - - hv_acpi_dev = device; - - result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - vmbus_walk_resources, &irq); - - if (ACPI_FAILURE(result)) { - complete(&probe_event); - return -ENODEV; - } - complete(&probe_event); - return 0; -} - -static const struct acpi_device_id vmbus_acpi_device_ids[] = { - {"VMBUS", 0}, - {"VMBus", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids); - -static struct acpi_driver vmbus_acpi_driver = { - .name = "vmbus", - .ids = vmbus_acpi_device_ids, - .ops = { - .add = vmbus_acpi_add, - }, -}; - -static int __init hv_acpi_init(void) -{ - int ret, t; - - init_completion(&probe_event); - - /* - * Get irq resources first. - */ - - ret = acpi_bus_register_driver(&vmbus_acpi_driver); - - if (ret) - return ret; - - t = wait_for_completion_timeout(&probe_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (irq <= 0) { - ret = -ENODEV; - goto cleanup; - } - - ret = vmbus_bus_init(irq); - if (ret) - goto cleanup; - - return 0; - -cleanup: - acpi_bus_unregister_driver(&vmbus_acpi_driver); - return ret; -} - - -MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); -module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); - -module_init(hv_acpi_init); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h new file mode 100644 index 000000000000..edaa9e2f58ec --- /dev/null +++ b/include/linux/hyperv.h @@ -0,0 +1,969 @@ +/* + * + * Copyright (c) 2011, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang + * Hank Janssen + * K. Y. Srinivasan + * + */ + +#ifndef _HYPERV_H +#define _HYPERV_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + + +#define MAX_PAGE_BUFFER_COUNT 16 +#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ + +#pragma pack(push, 1) + +/* Single-page buffer */ +struct hv_page_buffer { + u32 len; + u32 offset; + u64 pfn; +}; + +/* Multiple-page buffer */ +struct hv_multipage_buffer { + /* Length and Offset determines the # of pfns in the array */ + u32 len; + u32 offset; + u64 pfn_array[MAX_MULTIPAGE_BUFFER_COUNT]; +}; + +/* 0x18 includes the proprietary packet header */ +#define MAX_PAGE_BUFFER_PACKET (0x18 + \ + (sizeof(struct hv_page_buffer) * \ + MAX_PAGE_BUFFER_COUNT)) +#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \ + sizeof(struct hv_multipage_buffer)) + + +#pragma pack(pop) + +struct hv_ring_buffer { + /* Offset in bytes from the start of ring data below */ + u32 write_index; + + /* Offset in bytes from the start of ring data below */ + u32 read_index; + + u32 interrupt_mask; + + /* Pad it to PAGE_SIZE so that data starts on page boundary */ + u8 reserved[4084]; + + /* NOTE: + * The interrupt_mask field is used only for channels but since our + * vmbus connection also uses this data structure and its data starts + * here, we commented out this field. + */ + + /* + * Ring data starts here + RingDataStartOffset + * !!! DO NOT place any fields below this !!! + */ + u8 buffer[0]; +} __packed; + +struct hv_ring_buffer_info { + struct hv_ring_buffer *ring_buffer; + u32 ring_size; /* Include the shared header */ + spinlock_t ring_lock; + + u32 ring_datasize; /* < ring_size */ + u32 ring_data_startoffset; +}; + +struct hv_ring_buffer_debug_info { + u32 current_interrupt_mask; + u32 current_read_index; + u32 current_write_index; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +/* + * We use the same version numbering for all Hyper-V modules. + * + * Definition of versioning is as follows; + * + * Major Number Changes for these scenarios; + * 1. When a new version of Windows Hyper-V + * is released. + * 2. A Major change has occurred in the + * Linux IC's. + * (For example the merge for the first time + * into the kernel) Every time the Major Number + * changes, the Revision number is reset to 0. + * Minor Number Changes when new functionality is added + * to the Linux IC's that is not a bug fix. + * + * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync + */ +#define HV_DRV_VERSION "3.1" + + +/* + * A revision number of vmbus that is used for ensuring both ends on a + * partition are using compatible versions. + */ +#define VMBUS_REVISION_NUMBER 13 + +/* Make maximum size of pipe payload of 16K */ +#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) + +/* Define PipeMode values. */ +#define VMBUS_PIPE_TYPE_BYTE 0x00000000 +#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004 + +/* The size of the user defined data buffer for non-pipe offers. */ +#define MAX_USER_DEFINED_BYTES 120 + +/* The size of the user defined data buffer for pipe offers. */ +#define MAX_PIPE_USER_DEFINED_BYTES 116 + +/* + * At the center of the Channel Management library is the Channel Offer. This + * struct contains the fundamental information about an offer. + */ +struct vmbus_channel_offer { + uuid_le if_type; + uuid_le if_instance; + u64 int_latency; /* in 100ns units */ + u32 if_revision; + u32 server_ctx_size; /* in bytes */ + u16 chn_flags; + u16 mmio_megabytes; /* in bytes * 1024 * 1024 */ + + union { + /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */ + struct { + unsigned char user_def[MAX_USER_DEFINED_BYTES]; + } std; + + /* + * Pipes: + * The following sructure is an integrated pipe protocol, which + * is implemented on top of standard user-defined data. Pipe + * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own + * use. + */ + struct { + u32 pipe_mode; + unsigned char user_def[MAX_PIPE_USER_DEFINED_BYTES]; + } pipe; + } u; + u32 padding; +} __packed; + +/* Server Flags */ +#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4 +#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 +#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 +#define VMBUS_CHANNEL_PARENT_OFFER 0x200 +#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 + +struct vmpacket_descriptor { + u16 type; + u16 offset8; + u16 len8; + u16 flags; + u64 trans_id; +} __packed; + +struct vmpacket_header { + u32 prev_pkt_start_offset; + struct vmpacket_descriptor descriptor; +} __packed; + +struct vmtransfer_page_range { + u32 byte_count; + u32 byte_offset; +} __packed; + +struct vmtransfer_page_packet_header { + struct vmpacket_descriptor d; + u16 xfer_pageset_id; + bool sender_owns_set; + u8 reserved; + u32 range_cnt; + struct vmtransfer_page_range ranges[1]; +} __packed; + +struct vmgpadl_packet_header { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; +} __packed; + +struct vmadd_remove_transfer_page_set { + struct vmpacket_descriptor d; + u32 gpadl; + u16 xfer_pageset_id; + u16 reserved; +} __packed; + +/* + * This structure defines a range in guest physical space that can be made to + * look virtually contiguous. + */ +struct gpa_range { + u32 byte_count; + u32 byte_offset; + u64 pfn_array[0]; +}; + +/* + * This is the format for an Establish Gpadl packet, which contains a handle by + * which this GPADL will be known and a set of GPA ranges associated with it. + * This can be converted to a MDL by the guest OS. If there are multiple GPA + * ranges, then the resulting MDL will be "chained," representing multiple VA + * ranges. + */ +struct vmestablish_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* + * This is the format for a Teardown Gpadl packet, which indicates that the + * GPADL handle in the Establish Gpadl packet will never be referenced again. + */ +struct vmteardown_gpadl { + struct vmpacket_descriptor d; + u32 gpadl; + u32 reserved; /* for alignment to a 8-byte boundary */ +} __packed; + +/* + * This is the format for a GPA-Direct packet, which contains a set of GPA + * ranges, in addition to commands and/or data. + */ +struct vmdata_gpa_direct { + struct vmpacket_descriptor d; + u32 reserved; + u32 range_cnt; + struct gpa_range range[1]; +} __packed; + +/* This is the format for a Additional Data Packet. */ +struct vmadditional_data { + struct vmpacket_descriptor d; + u64 total_bytes; + u32 offset; + u32 byte_cnt; + unsigned char data[1]; +} __packed; + +union vmpacket_largest_possible_header { + struct vmpacket_descriptor simple_hdr; + struct vmtransfer_page_packet_header xfer_page_hdr; + struct vmgpadl_packet_header gpadl_hdr; + struct vmadd_remove_transfer_page_set add_rm_xfer_page_hdr; + struct vmestablish_gpadl establish_gpadl_hdr; + struct vmteardown_gpadl teardown_gpadl_hdr; + struct vmdata_gpa_direct data_gpa_direct_hdr; +}; + +#define VMPACKET_DATA_START_ADDRESS(__packet) \ + (void *)(((unsigned char *)__packet) + \ + ((struct vmpacket_descriptor)__packet)->offset8 * 8) + +#define VMPACKET_DATA_LENGTH(__packet) \ + ((((struct vmpacket_descriptor)__packet)->len8 - \ + ((struct vmpacket_descriptor)__packet)->offset8) * 8) + +#define VMPACKET_TRANSFER_MODE(__packet) \ + (((struct IMPACT)__packet)->type) + +enum vmbus_packet_type { + VM_PKT_INVALID = 0x0, + VM_PKT_SYNCH = 0x1, + VM_PKT_ADD_XFER_PAGESET = 0x2, + VM_PKT_RM_XFER_PAGESET = 0x3, + VM_PKT_ESTABLISH_GPADL = 0x4, + VM_PKT_TEARDOWN_GPADL = 0x5, + VM_PKT_DATA_INBAND = 0x6, + VM_PKT_DATA_USING_XFER_PAGES = 0x7, + VM_PKT_DATA_USING_GPADL = 0x8, + VM_PKT_DATA_USING_GPA_DIRECT = 0x9, + VM_PKT_CANCEL_REQUEST = 0xa, + VM_PKT_COMP = 0xb, + VM_PKT_DATA_USING_ADDITIONAL_PKT = 0xc, + VM_PKT_ADDITIONAL_DATA = 0xd +}; + +#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1 + + +/* Version 1 messages */ +enum vmbus_channel_message_type { + CHANNELMSG_INVALID = 0, + CHANNELMSG_OFFERCHANNEL = 1, + CHANNELMSG_RESCIND_CHANNELOFFER = 2, + CHANNELMSG_REQUESTOFFERS = 3, + CHANNELMSG_ALLOFFERS_DELIVERED = 4, + CHANNELMSG_OPENCHANNEL = 5, + CHANNELMSG_OPENCHANNEL_RESULT = 6, + CHANNELMSG_CLOSECHANNEL = 7, + CHANNELMSG_GPADL_HEADER = 8, + CHANNELMSG_GPADL_BODY = 9, + CHANNELMSG_GPADL_CREATED = 10, + CHANNELMSG_GPADL_TEARDOWN = 11, + CHANNELMSG_GPADL_TORNDOWN = 12, + CHANNELMSG_RELID_RELEASED = 13, + CHANNELMSG_INITIATE_CONTACT = 14, + CHANNELMSG_VERSION_RESPONSE = 15, + CHANNELMSG_UNLOAD = 16, +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD + CHANNELMSG_VIEWRANGE_ADD = 17, + CHANNELMSG_VIEWRANGE_REMOVE = 18, +#endif + CHANNELMSG_COUNT +}; + +struct vmbus_channel_message_header { + enum vmbus_channel_message_type msgtype; + u32 padding; +} __packed; + +/* Query VMBus Version parameters */ +struct vmbus_channel_query_vmbus_version { + struct vmbus_channel_message_header header; + u32 version; +} __packed; + +/* VMBus Version Supported parameters */ +struct vmbus_channel_version_supported { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +/* Offer Channel parameters */ +struct vmbus_channel_offer_channel { + struct vmbus_channel_message_header header; + struct vmbus_channel_offer offer; + u32 child_relid; + u8 monitorid; + bool monitor_allocated; +} __packed; + +/* Rescind Offer parameters */ +struct vmbus_channel_rescind_offer { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* + * Request Offer -- no parameters, SynIC message contains the partition ID + * Set Snoop -- no parameters, SynIC message contains the partition ID + * Clear Snoop -- no parameters, SynIC message contains the partition ID + * All Offers Delivered -- no parameters, SynIC message contains the partition + * ID + * Flush Client -- no parameters, SynIC message contains the partition ID + */ + +/* Open Channel parameters */ +struct vmbus_channel_open_channel { + struct vmbus_channel_message_header header; + + /* Identifies the specific VMBus channel that is being opened. */ + u32 child_relid; + + /* ID making a particular open request at a channel offer unique. */ + u32 openid; + + /* GPADL for the channel's ring buffer. */ + u32 ringbuffer_gpadlhandle; + + /* GPADL for the channel's server context save area. */ + u32 server_contextarea_gpadlhandle; + + /* + * The upstream ring buffer begins at offset zero in the memory + * described by RingBufferGpadlHandle. The downstream ring buffer + * follows it at this offset (in pages). + */ + u32 downstream_ringbuffer_pageoffset; + + /* User-specific data to be passed along to the server endpoint. */ + unsigned char userdata[MAX_USER_DEFINED_BYTES]; +} __packed; + +/* Open Channel Result parameters */ +struct vmbus_channel_open_result { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 openid; + u32 status; +} __packed; + +/* Close channel parameters; */ +struct vmbus_channel_close_channel { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +/* Channel Message GPADL */ +#define GPADL_TYPE_RING_BUFFER 1 +#define GPADL_TYPE_SERVER_SAVE_AREA 2 +#define GPADL_TYPE_TRANSACTION 8 + +/* + * The number of PFNs in a GPADL message is defined by the number of + * pages that would be spanned by ByteCount and ByteOffset. If the + * implied number of PFNs won't fit in this packet, there will be a + * follow-up packet that contains more. + */ +struct vmbus_channel_gpadl_header { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u16 range_buflen; + u16 rangecount; + struct gpa_range range[0]; +} __packed; + +/* This is the followup packet that contains more PFNs. */ +struct vmbus_channel_gpadl_body { + struct vmbus_channel_message_header header; + u32 msgnumber; + u32 gpadl; + u64 pfn[0]; +} __packed; + +struct vmbus_channel_gpadl_created { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; + u32 creation_status; +} __packed; + +struct vmbus_channel_gpadl_teardown { + struct vmbus_channel_message_header header; + u32 child_relid; + u32 gpadl; +} __packed; + +struct vmbus_channel_gpadl_torndown { + struct vmbus_channel_message_header header; + u32 gpadl; +} __packed; + +#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD +struct vmbus_channel_view_range_add { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u64 viewrange_length; + u32 child_relid; +} __packed; + +struct vmbus_channel_view_range_remove { + struct vmbus_channel_message_header header; + PHYSICAL_ADDRESS viewrange_base; + u32 child_relid; +} __packed; +#endif + +struct vmbus_channel_relid_released { + struct vmbus_channel_message_header header; + u32 child_relid; +} __packed; + +struct vmbus_channel_initiate_contact { + struct vmbus_channel_message_header header; + u32 vmbus_version_requested; + u32 padding2; + u64 interrupt_page; + u64 monitor_page1; + u64 monitor_page2; +} __packed; + +struct vmbus_channel_version_response { + struct vmbus_channel_message_header header; + bool version_supported; +} __packed; + +enum vmbus_channel_state { + CHANNEL_OFFER_STATE, + CHANNEL_OPENING_STATE, + CHANNEL_OPEN_STATE, +}; + +struct vmbus_channel_debug_info { + u32 relid; + enum vmbus_channel_state state; + uuid_le interfacetype; + uuid_le interface_instance; + u32 monitorid; + u32 servermonitor_pending; + u32 servermonitor_latency; + u32 servermonitor_connectionid; + u32 clientmonitor_pending; + u32 clientmonitor_latency; + u32 clientmonitor_connectionid; + + struct hv_ring_buffer_debug_info inbound; + struct hv_ring_buffer_debug_info outbound; +}; + +/* + * Represents each channel msg on the vmbus connection This is a + * variable-size data structure depending on the msg type itself + */ +struct vmbus_channel_msginfo { + /* Bookkeeping stuff */ + struct list_head msglistentry; + + /* So far, this is only used to handle gpadl body message */ + struct list_head submsglist; + + /* Synchronize the request/response if needed */ + struct completion waitevent; + union { + struct vmbus_channel_version_supported version_supported; + struct vmbus_channel_open_result open_result; + struct vmbus_channel_gpadl_torndown gpadl_torndown; + struct vmbus_channel_gpadl_created gpadl_created; + struct vmbus_channel_version_response version_response; + } response; + + u32 msgsize; + /* + * The channel message that goes out on the "wire". + * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header + */ + unsigned char msg[0]; +}; + +struct vmbus_close_msg { + struct vmbus_channel_msginfo info; + struct vmbus_channel_close_channel msg; +}; + +struct vmbus_channel { + struct list_head listentry; + + struct hv_device *device_obj; + + struct work_struct work; + + enum vmbus_channel_state state; + + struct vmbus_channel_offer_channel offermsg; + /* + * These are based on the OfferMsg.MonitorId. + * Save it here for easy access. + */ + u8 monitor_grp; + u8 monitor_bit; + + u32 ringbuffer_gpadlhandle; + + /* Allocated memory for ring buffer */ + void *ringbuffer_pages; + u32 ringbuffer_pagecount; + struct hv_ring_buffer_info outbound; /* send to parent */ + struct hv_ring_buffer_info inbound; /* receive from parent */ + spinlock_t inbound_lock; + struct workqueue_struct *controlwq; + + struct vmbus_close_msg close_msg; + + /* Channel callback are invoked in this workqueue context */ + /* HANDLE dataWorkQueue; */ + + void (*onchannel_callback)(void *context); + void *channel_callback_context; +}; + +void free_channel(struct vmbus_channel *channel); + +void vmbus_onmessage(void *context); + +int vmbus_request_offers(void); + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_page_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; + struct hv_page_buffer range[MAX_PAGE_BUFFER_COUNT]; +} __packed; + +/* The format must be the same as struct vmdata_gpa_direct */ +struct vmbus_channel_packet_multipage_buffer { + u16 type; + u16 dataoffset8; + u16 length8; + u16 flags; + u64 transactionid; + u32 reserved; + u32 rangecount; /* Always 1 in this case */ + struct hv_multipage_buffer range; +} __packed; + + +extern int vmbus_open(struct vmbus_channel *channel, + u32 send_ringbuffersize, + u32 recv_ringbuffersize, + void *userdata, + u32 userdatalen, + void(*onchannel_callback)(void *context), + void *context); + +extern void vmbus_close(struct vmbus_channel *channel); + +extern int vmbus_sendpacket(struct vmbus_channel *channel, + const void *buffer, + u32 bufferLen, + u64 requestid, + enum vmbus_packet_type type, + u32 flags); + +extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, + struct hv_page_buffer pagebuffers[], + u32 pagecount, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, + struct hv_multipage_buffer *mpb, + void *buffer, + u32 bufferlen, + u64 requestid); + +extern int vmbus_establish_gpadl(struct vmbus_channel *channel, + void *kbuffer, + u32 size, + u32 *gpadl_handle); + +extern int vmbus_teardown_gpadl(struct vmbus_channel *channel, + u32 gpadl_handle); + +extern int vmbus_recvpacket(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + +extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, + void *buffer, + u32 bufferlen, + u32 *buffer_actual_len, + u64 *requestid); + + +extern void vmbus_get_debug_info(struct vmbus_channel *channel, + struct vmbus_channel_debug_info *debug); + +extern void vmbus_ontimer(unsigned long data); + + +#define LOWORD(dw) ((unsigned short)(dw)) +#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) + + +#define VMBUS 0x0001 +#define STORVSC 0x0002 +#define NETVSC 0x0004 +#define INPUTVSC 0x0008 +#define BLKVSC 0x0010 +#define VMBUS_DRV 0x0100 +#define STORVSC_DRV 0x0200 +#define NETVSC_DRV 0x0400 +#define INPUTVSC_DRV 0x0800 +#define BLKVSC_DRV 0x1000 + +#define ALL_MODULES (VMBUS |\ + STORVSC |\ + NETVSC |\ + INPUTVSC |\ + BLKVSC |\ + VMBUS_DRV |\ + STORVSC_DRV |\ + NETVSC_DRV |\ + INPUTVSC_DRV|\ + BLKVSC_DRV) + +/* Logging Level */ +#define ERROR_LVL 3 +#define WARNING_LVL 4 +#define INFO_LVL 6 +#define DEBUG_LVL 7 +#define DEBUG_LVL_ENTEREXIT 8 +#define DEBUG_RING_LVL 9 + +extern unsigned int vmbus_loglevel; + +#define DPRINT(mod, lvl, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (lvl <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_DBG(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ + } while (0) + +#define DPRINT_INFO(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (INFO_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_INFO #mod": " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_WARN(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ + } while (0) + +#define DPRINT_ERR(mod, fmt, args...) do {\ + if ((mod & (HIWORD(vmbus_loglevel))) && \ + (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ + printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ + __func__, ## args);\ + } while (0) + + + +struct hv_driver; +struct hv_device; + +struct hv_dev_port_info { + u32 int_mask; + u32 read_idx; + u32 write_idx; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; + +struct hv_device_info { + u32 chn_id; + u32 chn_state; + uuid_le chn_type; + uuid_le chn_instance; + + u32 monitor_id; + u32 server_monitor_pending; + u32 server_monitor_latency; + u32 server_monitor_conn_id; + u32 client_monitor_pending; + u32 client_monitor_latency; + u32 client_monitor_conn_id; + + struct hv_dev_port_info inbound; + struct hv_dev_port_info outbound; +}; + +/* Base driver object */ +struct hv_driver { + const char *name; + + /* the device type supported by this driver */ + uuid_le dev_type; + const struct hv_vmbus_device_id *id_table; + + struct device_driver driver; + + int (*probe)(struct hv_device *, const struct hv_vmbus_device_id *); + int (*remove)(struct hv_device *); + void (*shutdown)(struct hv_device *); + +}; + +/* Base device object */ +struct hv_device { + /* the device type id of this device */ + uuid_le dev_type; + + /* the device instance id of this device */ + uuid_le dev_instance; + + struct device device; + + struct vmbus_channel *channel; +}; + + +static inline struct hv_device *device_to_hv_device(struct device *d) +{ + return container_of(d, struct hv_device, device); +} + +static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d) +{ + return container_of(d, struct hv_driver, driver); +} + +static inline void hv_set_drvdata(struct hv_device *dev, void *data) +{ + dev_set_drvdata(&dev->device, data); +} + +static inline void *hv_get_drvdata(struct hv_device *dev) +{ + return dev_get_drvdata(&dev->device); +} + +/* Vmbus interface */ +#define vmbus_driver_register(driver) \ + __vmbus_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) +int __must_check __vmbus_driver_register(struct hv_driver *hv_driver, + struct module *owner, + const char *mod_name); +void vmbus_driver_unregister(struct hv_driver *hv_driver); + +/** + * VMBUS_DEVICE - macro used to describe a specific hyperv vmbus device + * + * This macro is used to create a struct hv_vmbus_device_id that matches a + * specific device. + */ +#define VMBUS_DEVICE(g0, g1, g2, g3, g4, g5, g6, g7, \ + g8, g9, ga, gb, gc, gd, ge, gf) \ + .guid = { g0, g1, g2, g3, g4, g5, g6, g7, \ + g8, g9, ga, gb, gc, gd, ge, gf }, + +/* + * Common header for Hyper-V ICs + */ + +#define ICMSGTYPE_NEGOTIATE 0 +#define ICMSGTYPE_HEARTBEAT 1 +#define ICMSGTYPE_KVPEXCHANGE 2 +#define ICMSGTYPE_SHUTDOWN 3 +#define ICMSGTYPE_TIMESYNC 4 +#define ICMSGTYPE_VSS 5 + +#define ICMSGHDRFLAG_TRANSACTION 1 +#define ICMSGHDRFLAG_REQUEST 2 +#define ICMSGHDRFLAG_RESPONSE 4 + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 + +/* + * While we want to handle util services as regular devices, + * there is only one instance of each of these services; so + * we statically allocate the service specific state. + */ + +struct hv_util_service { + u8 *recv_buffer; + void (*util_cb)(void *); + int (*util_init)(struct hv_util_service *); + void (*util_deinit)(void); +}; + +struct vmbuspipe_hdr { + u32 flags; + u32 msgsize; +} __packed; + +struct ic_version { + u16 major; + u16 minor; +} __packed; + +struct icmsg_hdr { + struct ic_version icverframe; + u16 icmsgtype; + struct ic_version icvermsg; + u16 icmsgsize; + u32 status; + u8 ictransaction_id; + u8 icflags; + u8 reserved[2]; +} __packed; + +struct icmsg_negotiate { + u16 icframe_vercnt; + u16 icmsg_vercnt; + u32 reserved; + struct ic_version icversion_data[1]; /* any size array */ +} __packed; + +struct shutdown_msg_data { + u32 reason_code; + u32 timeout_seconds; + u32 flags; + u8 display_message[2048]; +} __packed; + +struct heartbeat_msg_data { + u64 seq_num; + u32 reserved[8]; +} __packed; + +/* Time Sync IC defs */ +#define ICTIMESYNCFLAG_PROBE 0 +#define ICTIMESYNCFLAG_SYNC 1 +#define ICTIMESYNCFLAG_SAMPLE 2 + +#ifdef __x86_64__ +#define WLTIMEDELTA 116444736000000000L /* in 100ns unit */ +#else +#define WLTIMEDELTA 116444736000000000LL +#endif + +struct ictimesync_data { + u64 parenttime; + u64 childtime; + u64 roundtriptime; + u8 flags; +} __packed; + +struct hyperv_service_callback { + u8 msg_type; + char *log_msg; + uuid_le data; + struct vmbus_channel *channel; + void (*callback) (void *context); +}; + +extern void prep_negotiate_resp(struct icmsg_hdr *, + struct icmsg_negotiate *, u8 *); + +#endif /* _HYPERV_H */ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c new file mode 100644 index 000000000000..11224eddcdc2 --- /dev/null +++ b/tools/hv/hv_kvp_daemon.c @@ -0,0 +1,500 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * KYS: TODO. Need to register these in the kernel. + * + * The following definitions are shared with the in-kernel component; do not + * change any of this without making the corresponding changes in + * the KVP kernel component. + */ +#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ +#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ +#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ + +/* + * KVP protocol: The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * We use this infrastructure for also supporting queries from user mode + * application for state that may be maintained in the KVP kernel component. + * + * XXXKYS: Have a shared header file between the user and kernel (TODO) + */ + +enum kvp_op { + KVP_REGISTER = 0, /* Register the user mode component*/ + KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ + KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ + KVP_USER_GET, /*User is requesting the value for the specified key*/ + KVP_USER_SET /*User is providing the value for the specified key*/ +}; + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 + +struct hv_ku_msg { + __u32 kvp_index; + __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ + __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ +}; + +enum key_index { + FullyQualifiedDomainName = 0, + IntegrationServicesVersion, /*This key is serviced in the kernel*/ + NetworkAddressIPv4, + NetworkAddressIPv6, + OSBuildNumber, + OSName, + OSMajorVersion, + OSMinorVersion, + OSVersion, + ProcessorArchitecture +}; + +/* + * End of shared definitions. + */ + +static char kvp_send_buffer[4096]; +static char kvp_recv_buffer[4096]; +static struct sockaddr_nl addr; + +static char *os_name = ""; +static char *os_major = ""; +static char *os_minor = ""; +static char *processor_arch; +static char *os_build; +static char *lic_version; +static struct utsname uts_buf; + +void kvp_get_os_info(void) +{ + FILE *file; + char *p, buf[512]; + + uname(&uts_buf); + os_build = uts_buf.release; + processor_arch = uts_buf.machine; + + /* + * The current windows host (win7) expects the build + * string to be of the form: x.y.z + * Strip additional information we may have. + */ + p = strchr(os_build, '-'); + if (p) + *p = '\0'; + + file = fopen("/etc/SuSE-release", "r"); + if (file != NULL) + goto kvp_osinfo_found; + file = fopen("/etc/redhat-release", "r"); + if (file != NULL) + goto kvp_osinfo_found; + /* + * Add code for other supported platforms. + */ + + /* + * We don't have information about the os. + */ + os_name = uts_buf.sysname; + return; + +kvp_osinfo_found: + /* up to three lines */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (!p) + goto done; + os_name = p; + + /* second line */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (!p) + goto done; + os_major = p; + + /* third line */ + p = fgets(buf, sizeof(buf), file); + if (p) { + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + p = strdup(buf); + if (p) + os_minor = p; + } + } + } + +done: + fclose(file); + return; +} + +static int +kvp_get_ip_address(int family, char *buffer, int length) +{ + struct ifaddrs *ifap; + struct ifaddrs *curp; + int ipv4_len = strlen("255.255.255.255") + 1; + int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1; + int offset = 0; + const char *str; + char tmp[50]; + int error = 0; + + /* + * On entry into this function, the buffer is capable of holding the + * maximum key value (2048 bytes). + */ + + if (getifaddrs(&ifap)) { + strcpy(buffer, "getifaddrs failed\n"); + return 1; + } + + curp = ifap; + while (curp != NULL) { + if ((curp->ifa_addr != NULL) && + (curp->ifa_addr->sa_family == family)) { + if (family == AF_INET) { + struct sockaddr_in *addr = + (struct sockaddr_in *) curp->ifa_addr; + + str = inet_ntop(family, &addr->sin_addr, + tmp, 50); + if (str == NULL) { + strcpy(buffer, "inet_ntop failed\n"); + error = 1; + goto getaddr_done; + } + if (offset == 0) + strcpy(buffer, tmp); + else + strcat(buffer, tmp); + strcat(buffer, ";"); + + offset += strlen(str) + 1; + if ((length - offset) < (ipv4_len + 1)) + goto getaddr_done; + + } else { + + /* + * We only support AF_INET and AF_INET6 + * and the list of addresses is separated by a ";". + */ + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *) curp->ifa_addr; + + str = inet_ntop(family, + &addr->sin6_addr.s6_addr, + tmp, 50); + if (str == NULL) { + strcpy(buffer, "inet_ntop failed\n"); + error = 1; + goto getaddr_done; + } + if (offset == 0) + strcpy(buffer, tmp); + else + strcat(buffer, tmp); + strcat(buffer, ";"); + offset += strlen(str) + 1; + if ((length - offset) < (ipv6_len + 1)) + goto getaddr_done; + + } + + } + curp = curp->ifa_next; + } + +getaddr_done: + freeifaddrs(ifap); + return error; +} + + +static int +kvp_get_domain_name(char *buffer, int length) +{ + struct addrinfo hints, *info ; + int error = 0; + + gethostname(buffer, length); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + + error = getaddrinfo(buffer, NULL, &hints, &info); + if (error != 0) { + strcpy(buffer, "getaddrinfo failed\n"); + return error; + } + strcpy(buffer, info->ai_canonname); + freeaddrinfo(info); + return error; +} + +static int +netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr *nlh; + unsigned int size; + struct msghdr message; + char buffer[64]; + struct iovec iov[2]; + + size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); + + nlh = (struct nlmsghdr *)buffer; + nlh->nlmsg_seq = 0; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_type = NLMSG_DONE; + nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); + nlh->nlmsg_flags = 0; + + iov[0].iov_base = nlh; + iov[0].iov_len = sizeof(*nlh); + + iov[1].iov_base = msg; + iov[1].iov_len = size; + + memset(&message, 0, sizeof(message)); + message.msg_name = &addr; + message.msg_namelen = sizeof(addr); + message.msg_iov = iov; + message.msg_iovlen = 2; + + return sendmsg(fd, &message, 0); +} + +int main(void) +{ + int fd, len, sock_opt; + int error; + struct cn_msg *message; + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; + struct hv_ku_msg *hv_msg; + char *p; + char *key_value; + char *key_name; + + daemon(1, 0); + openlog("KVP", 0, LOG_USER); + syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); + /* + * Retrieve OS release information. + */ + kvp_get_os_info(); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); + exit(-1); + } + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 0; + addr.nl_groups = CN_KVP_IDX; + + + error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (error < 0) { + syslog(LOG_ERR, "bind failed; error:%d", error); + close(fd); + exit(-1); + } + sock_opt = addr.nl_groups; + setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)kvp_send_buffer; + message->id.idx = CN_KVP_IDX; + message->id.val = CN_KVP_VAL; + message->seq = KVP_REGISTER; + message->ack = 0; + message->len = 0; + + len = netlink_send(fd, message); + if (len < 0) { + syslog(LOG_ERR, "netlink_send failed; error:%d", len); + close(fd); + exit(-1); + } + + pfd.fd = fd; + + while (1) { + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, -1); + + len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0); + + if (len < 0) { + syslog(LOG_ERR, "recv failed; error:%d", len); + close(fd); + return -1; + } + + incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + + switch (incoming_cn_msg->seq) { + case KVP_REGISTER: + /* + * Driver is registering with us; stash away the version + * information. + */ + p = (char *)incoming_cn_msg->data; + lic_version = malloc(strlen(p) + 1); + if (lic_version) { + strcpy(lic_version, p); + syslog(LOG_INFO, "KVP LIC Version: %s", + lic_version); + } else { + syslog(LOG_ERR, "malloc failed"); + } + continue; + + case KVP_KERNEL_GET: + break; + default: + continue; + } + + hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->kvp_key; + key_value = (char *)hv_msg->kvp_value; + + switch (hv_msg->kvp_index) { + case FullyQualifiedDomainName: + kvp_get_domain_name(key_value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_name, "FullyQualifiedDomainName"); + break; + case IntegrationServicesVersion: + strcpy(key_name, "IntegrationServicesVersion"); + strcpy(key_value, lic_version); + break; + case NetworkAddressIPv4: + kvp_get_ip_address(AF_INET, key_value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_name, "NetworkAddressIPv4"); + break; + case NetworkAddressIPv6: + kvp_get_ip_address(AF_INET6, key_value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_name, "NetworkAddressIPv6"); + break; + case OSBuildNumber: + strcpy(key_value, os_build); + strcpy(key_name, "OSBuildNumber"); + break; + case OSName: + strcpy(key_value, os_name); + strcpy(key_name, "OSName"); + break; + case OSMajorVersion: + strcpy(key_value, os_major); + strcpy(key_name, "OSMajorVersion"); + break; + case OSMinorVersion: + strcpy(key_value, os_minor); + strcpy(key_name, "OSMinorVersion"); + break; + case OSVersion: + strcpy(key_value, os_build); + strcpy(key_name, "OSVersion"); + break; + case ProcessorArchitecture: + strcpy(key_value, processor_arch); + strcpy(key_name, "ProcessorArchitecture"); + break; + default: + strcpy(key_value, "Unknown Key"); + /* + * We use a null key name to terminate enumeration. + */ + strcpy(key_name, ""); + break; + } + /* + * Send the value back to the kernel. The response is + * already in the receive buffer. Update the cn_msg header to + * reflect the key value that has been added to the message + */ + + incoming_cn_msg->id.idx = CN_KVP_IDX; + incoming_cn_msg->id.val = CN_KVP_VAL; + incoming_cn_msg->seq = KVP_USER_SET; + incoming_cn_msg->ack = 0; + incoming_cn_msg->len = sizeof(struct hv_ku_msg); + + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { + syslog(LOG_ERR, "net_link send failed; error:%d", len); + exit(-1); + } + } + +} -- cgit v1.2.3 From 407dd1644302ea78fa5d740e67a1c09677aa18a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 08:36:44 -0600 Subject: Staging: hv: remove unneeded asm include file in hyperv.h No one outside of the hyperv core needs to include the asm/hyperv.h file, so don't put it in the "global" include/linux/hyperv.h file. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 2 +- drivers/hv/hv.c | 2 +- drivers/hv/vmbus_drv.c | 2 +- include/linux/hyperv.h | 3 --- 4 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 5f438b650068..650c9f0b6642 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -30,7 +30,7 @@ #include #include #include - +#include #include "hyperv_vmbus.h" diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 931b7b030784..0fb100ed91a3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -26,7 +26,7 @@ #include #include #include - +#include #include "hyperv_vmbus.h" /* The one and only */ diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index b0d08f980de1..bb9f20507d77 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -33,7 +33,7 @@ #include #include #include - +#include #include "hyperv_vmbus.h" diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index edaa9e2f58ec..4c8414a8b07f 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -35,9 +35,6 @@ #include -#include - - #define MAX_PAGE_BUFFER_COUNT 16 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ -- cgit v1.2.3 From da0e96315ca703ab6540cc7665549622f71c155f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 08:42:28 -0600 Subject: hv: rename prep_negotiate_resp() to vmbus_prep_negotiate_resp() It's a global symbol, so properly prefix it and use the proper EXPORT value as well. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 9 ++++----- drivers/hv/hv_kvp.c | 2 +- drivers/hv/hv_util.c | 6 +++--- include/linux/hyperv.h | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 41bf287baa1c..a2fc487d1422 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -114,7 +114,7 @@ static const uuid_le /** - * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message + * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message * @icmsghdrp: Pointer to msg header structure * @icmsg_negotiate: Pointer to negotiate message structure * @buf: Raw buffer channel data @@ -128,9 +128,8 @@ static const uuid_le * * Mainly used by Hyper-V drivers. */ -void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - struct icmsg_negotiate *negop, - u8 *buf) +void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, + struct icmsg_negotiate *negop, u8 *buf) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { icmsghdrp->icmsgsize = 0x10; @@ -156,7 +155,7 @@ void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, negop->icmsg_vercnt = 1; } } -EXPORT_SYMBOL(prep_negotiate_resp); +EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); /* * alloc_channel - Allocate and initialize a vmbus channel object diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 69c4c985daeb..89f52440fcf4 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -260,7 +260,7 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, recv_buffer); + vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer); } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index e0e3a6d0244d..55d58f21e6d4 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -73,7 +73,7 @@ static void shutdown_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); + vmbus_prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf); } else { shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ @@ -198,7 +198,7 @@ static void timesync_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); + vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf); } else { timedatap = (struct ictimesync_data *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr) + @@ -237,7 +237,7 @@ static void heartbeat_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); + vmbus_prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf); } else { heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 4c8414a8b07f..98a57a51acfc 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -960,7 +960,7 @@ struct hyperv_service_callback { void (*callback) (void *context); }; -extern void prep_negotiate_resp(struct icmsg_hdr *, - struct icmsg_negotiate *, u8 *); +extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, + struct icmsg_negotiate *, u8 *); #endif /* _HYPERV_H */ -- cgit v1.2.3 From a832a1eba94096513bc42c96f33957cc46c5f2bf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 08:47:32 -0600 Subject: hv: remove a bunch of unused debug macros from hyperv.h These aren't used by anyone anymore, so remove them before someone tries to use them again. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 98a57a51acfc..2229073874b0 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -721,11 +721,8 @@ extern void vmbus_ontimer(unsigned long data); BLKVSC_DRV) /* Logging Level */ -#define ERROR_LVL 3 #define WARNING_LVL 4 #define INFO_LVL 6 -#define DEBUG_LVL 7 -#define DEBUG_LVL_ENTEREXIT 8 #define DEBUG_RING_LVL 9 extern unsigned int vmbus_loglevel; @@ -736,33 +733,12 @@ extern unsigned int vmbus_loglevel; printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ } while (0) -#define DPRINT_DBG(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - -#define DPRINT_INFO(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (INFO_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_INFO #mod": " fmt "\n", ## args);\ - } while (0) - #define DPRINT_WARN(mod, fmt, args...) do {\ if ((mod & (HIWORD(vmbus_loglevel))) && \ (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ } while (0) -#define DPRINT_ERR(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (ERROR_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \ - __func__, ## args);\ - } while (0) - - - struct hv_driver; struct hv_device; -- cgit v1.2.3 From d181daa06dd72fa88652b1d8bf723570a9fc55ea Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:20:31 -0600 Subject: Staging: hv: storvsc: remove last usage of DPRINT_WARN Used the correct dev_warn() call instead. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hv/storvsc_drv.c | 16 ++++++++-------- include/linux/hyperv.h | 6 ------ 2 files changed, 8 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index af185abbaa73..1f9d23e48b2a 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -510,20 +510,20 @@ static void storvsc_on_io_completion(struct hv_device *device, if (vstor_packet->vm_srb.scsi_status != 0 || vstor_packet->vm_srb.srb_status != 1){ - DPRINT_WARN(STORVSC, - "cmd 0x%x scsi status 0x%x srb status 0x%x\n", - stor_pkt->vm_srb.cdb[0], - vstor_packet->vm_srb.scsi_status, - vstor_packet->vm_srb.srb_status); + dev_warn(&device->device, + "cmd 0x%x scsi status 0x%x srb status 0x%x\n", + stor_pkt->vm_srb.cdb[0], + vstor_packet->vm_srb.scsi_status, + vstor_packet->vm_srb.srb_status); } if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { /* CHECK_CONDITION */ if (vstor_packet->vm_srb.srb_status & 0x80) { /* autosense data available */ - DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " - "valid - len %d\n", request, - vstor_packet->vm_srb.sense_info_length); + dev_warn(&device->device, + "storvsc pkt %p autosense data valid - len %d\n", + request, vstor_packet->vm_srb.sense_info_length); memcpy(request->sense_buffer, vstor_packet->vm_srb.sense_data, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 2229073874b0..097d6dbe685a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -721,7 +721,6 @@ extern void vmbus_ontimer(unsigned long data); BLKVSC_DRV) /* Logging Level */ -#define WARNING_LVL 4 #define INFO_LVL 6 #define DEBUG_RING_LVL 9 @@ -733,11 +732,6 @@ extern unsigned int vmbus_loglevel; printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ } while (0) -#define DPRINT_WARN(mod, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (WARNING_LVL <= LOWORD(vmbus_loglevel))) \ - printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\ - } while (0) struct hv_driver; struct hv_device; -- cgit v1.2.3 From 1a2643012fa2262e823b7f11d9732b7fea4c25ce Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:25:14 -0600 Subject: Staging: hv: remove last user of DPRINT() macro This also removed the unused function hv_dump_ring_info(). Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 2 -- drivers/hv/ring_buffer.c | 31 ------------------------------- include/linux/hyperv.h | 8 -------- 3 files changed, 41 deletions(-) (limited to 'include') diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 8261cb64931b..0aee1122734c 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -529,8 +529,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); -void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix); - void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, struct hv_ring_buffer_debug_info *debug_info); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index f594ed09d7e0..8af25a097d75 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -172,37 +172,6 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) return (u64)ring_info->ring_buffer->write_index << 32; } - -/* - * - * hv_dump_ring_info() - * - * Dump out to console the ring buffer info - * - */ -void hv_dump_ring_info(struct hv_ring_buffer_info *ring_info, char *prefix) -{ - u32 bytes_avail_towrite; - u32 bytes_avail_toread; - - hv_get_ringbuffer_availbytes(ring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - - DPRINT(VMBUS, - DEBUG_RING_LVL, - "%s <>", - prefix, - ring_info, - ring_info->ring_buffer->buffer, - bytes_avail_towrite, - bytes_avail_toread, - ring_info->ring_buffer->read_index, - ring_info->ring_buffer->write_index); -} - - /* * * hv_copyfrom_ringbuffer() diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 097d6dbe685a..4fa32ec43aa4 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -722,17 +722,9 @@ extern void vmbus_ontimer(unsigned long data); /* Logging Level */ #define INFO_LVL 6 -#define DEBUG_RING_LVL 9 extern unsigned int vmbus_loglevel; -#define DPRINT(mod, lvl, fmt, args...) do {\ - if ((mod & (HIWORD(vmbus_loglevel))) && \ - (lvl <= LOWORD(vmbus_loglevel))) \ - printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\ - } while (0) - - struct hv_driver; struct hv_device; -- cgit v1.2.3 From 815166b95df1acb4890e9dbdb26660e9c00a7505 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:27:48 -0600 Subject: Staging: hv: remove vmbus_loglevel as it is not used at all anymore As there is no user of this variable, it's time to delete it. For dynamic debugging of the hyperv code, use the standard dynamic debug kernel interface. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 5 ----- include/linux/hyperv.h | 5 ----- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index bb9f20507d77..e648571a5b05 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -41,10 +41,6 @@ static struct acpi_device *hv_acpi_dev; static struct tasklet_struct msg_dpc; static struct tasklet_struct event_dpc; - -unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL); -EXPORT_SYMBOL(vmbus_loglevel); - static struct completion probe_event; static int irq; @@ -767,6 +763,5 @@ cleanup: MODULE_LICENSE("GPL"); MODULE_VERSION(HV_DRV_VERSION); -module_param(vmbus_loglevel, int, S_IRUGO|S_IWUSR); module_init(hv_acpi_init); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 4fa32ec43aa4..7211e2ce24f5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -720,11 +720,6 @@ extern void vmbus_ontimer(unsigned long data); INPUTVSC_DRV|\ BLKVSC_DRV) -/* Logging Level */ -#define INFO_LVL 6 - -extern unsigned int vmbus_loglevel; - struct hv_driver; struct hv_device; -- cgit v1.2.3 From 5557e8a60570d0c2f3a06d6e9e6a0f5074c313f2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:30:22 -0600 Subject: hv: remove unused LOWORD and HIWORD macros from hyperv.h They aren't used anywhere anymore now that the debugging macros are gone, so remove it from hyperv.h as well. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7211e2ce24f5..6d9a53fc8a37 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -694,10 +694,6 @@ extern void vmbus_get_debug_info(struct vmbus_channel *channel, extern void vmbus_ontimer(unsigned long data); -#define LOWORD(dw) ((unsigned short)(dw)) -#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF)) - - #define VMBUS 0x0001 #define STORVSC 0x0002 #define NETVSC 0x0004 -- cgit v1.2.3 From 7a4ba88cc11eb4ba72778f491d8241385cb0475d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:33:29 -0600 Subject: hv: hyperv.h: remove unused module macros I have no idea what these were ever for, but they aren't used, so delete them. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 6d9a53fc8a37..f2bf0a51914b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -693,29 +693,6 @@ extern void vmbus_get_debug_info(struct vmbus_channel *channel, extern void vmbus_ontimer(unsigned long data); - -#define VMBUS 0x0001 -#define STORVSC 0x0002 -#define NETVSC 0x0004 -#define INPUTVSC 0x0008 -#define BLKVSC 0x0010 -#define VMBUS_DRV 0x0100 -#define STORVSC_DRV 0x0200 -#define NETVSC_DRV 0x0400 -#define INPUTVSC_DRV 0x0800 -#define BLKVSC_DRV 0x1000 - -#define ALL_MODULES (VMBUS |\ - STORVSC |\ - NETVSC |\ - INPUTVSC |\ - BLKVSC |\ - VMBUS_DRV |\ - STORVSC_DRV |\ - NETVSC_DRV |\ - INPUTVSC_DRV|\ - BLKVSC_DRV) - struct hv_driver; struct hv_device; -- cgit v1.2.3 From 2726f95e0b6c850b6162f287f2f83d9db37decd7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:37:27 -0600 Subject: hv: hyperv.h: remove unneeded forward declarations of structures This file was created by mushing different .h files together and it shows. This change removes some unneeded forward declarations. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index f2bf0a51914b..ae865a11ad46 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -693,9 +693,6 @@ extern void vmbus_get_debug_info(struct vmbus_channel *channel, extern void vmbus_ontimer(unsigned long data); -struct hv_driver; -struct hv_device; - struct hv_dev_port_info { u32 int_mask; u32 read_idx; -- cgit v1.2.3 From 9f3e28e375a8d509a27efe89f3c8ea2a15aeb524 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:40:01 -0600 Subject: hv: remove free_channel() from hyperv.h This function is only used in the file it is declared in (channel_mgmt.c) so make it static and remove it from the hyperv.h file. Cc: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 +- include/linux/hyperv.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index a2fc487d1422..12b85ff957fd 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -196,7 +196,7 @@ static void release_channel(struct work_struct *work) /* * free_channel - Release the resources used by the vmbus channel object */ -void free_channel(struct vmbus_channel *channel) +static void free_channel(struct vmbus_channel *channel) { /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index ae865a11ad46..240e1141cbbd 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -606,8 +606,6 @@ struct vmbus_channel { void *channel_callback_context; }; -void free_channel(struct vmbus_channel *channel); - void vmbus_onmessage(void *context); int vmbus_request_offers(void); -- cgit v1.2.3 From 15b80d641793968605254c2bbb2aa9a10accb415 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Oct 2011 09:43:14 -0600 Subject: hv: remove struct hv_device_info from hyperv.h This is only used/needed by the vmbus core code, so move it out of the hyperv.h file and into the .c file that uses it. Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 19 +++++++++++++++++++ include/linux/hyperv.h | 18 ------------------ 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e648571a5b05..0c048dd8013f 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -44,6 +44,25 @@ static struct tasklet_struct event_dpc; static struct completion probe_event; static int irq; +struct hv_device_info { + u32 chn_id; + u32 chn_state; + uuid_le chn_type; + uuid_le chn_instance; + + u32 monitor_id; + u32 server_monitor_pending; + u32 server_monitor_latency; + u32 server_monitor_conn_id; + u32 client_monitor_pending; + u32 client_monitor_latency; + u32 client_monitor_conn_id; + + struct hv_dev_port_info inbound; + struct hv_dev_port_info outbound; +}; + + static void get_channel_info(struct hv_device *device, struct hv_device_info *info) { diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 240e1141cbbd..12ec328481de 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -699,24 +699,6 @@ struct hv_dev_port_info { u32 bytes_avail_towrite; }; -struct hv_device_info { - u32 chn_id; - u32 chn_state; - uuid_le chn_type; - uuid_le chn_instance; - - u32 monitor_id; - u32 server_monitor_pending; - u32 server_monitor_latency; - u32 server_monitor_conn_id; - u32 client_monitor_pending; - u32 client_monitor_latency; - u32 client_monitor_conn_id; - - struct hv_dev_port_info inbound; - struct hv_dev_port_info outbound; -}; - /* Base driver object */ struct hv_driver { const char *name; -- cgit v1.2.3