diff --git a/debian/changelog b/debian/changelog index f0672588a..5a8c97c2b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,7 @@ linux-2.6 (3.2.14-2) UNRELEASED; urgency=low (Closes: #668036) * TOMOYO: Fix mount flags checking order. * drm/radeon/kms: fix fans after resume (Closes: #596741) + * [x86] hv: Update all Hyper-V drivers to 3.4-rc1 (Closes: #661318) [ Jonathan Nieder ] * [x86] ioat: fix size of 'completion' for Xen (Closes: #660554) diff --git a/debian/config/defines b/debian/config/defines index 120dbcea5..6459309a2 100644 --- a/debian/config/defines +++ b/debian/config/defines @@ -1,6 +1,7 @@ [abi] abiname: 2 ignore-changes: module:drivers/net/wireless/ath/ath9k/* + module:drivers/hv/* [base] arches: diff --git a/debian/patches/debian/nls-Avoid-ABI-change-from-improvement-to-utf8s_to_ut.patch b/debian/patches/debian/nls-Avoid-ABI-change-from-improvement-to-utf8s_to_ut.patch new file mode 100644 index 000000000..9389aa233 --- /dev/null +++ b/debian/patches/debian/nls-Avoid-ABI-change-from-improvement-to-utf8s_to_ut.patch @@ -0,0 +1,60 @@ +From: Ben Hutchings +Date: Mon, 9 Apr 2012 00:51:37 +0100 +Subject: [PATCH] nls: Avoid ABI change from improvement to utf8s_to_utf16s + +It's easy to add a temporary wrapper for binary-compatibility. +--- + fs/nls/nls_base.c | 13 ++++++++++++- + include/linux/nls.h | 4 +++- + 2 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c +index 0eb059e..91f145a 100644 +--- a/fs/nls/nls_base.c ++++ b/fs/nls/nls_base.c +@@ -129,7 +129,7 @@ static inline void put_utf16(wchar_t *s, unsigned c, enum utf16_endian endian) + } + } + +-int utf8s_to_utf16s(const u8 *s, int len, enum utf16_endian endian, ++int utf8s_to_utf16s_new(const u8 *s, int len, enum utf16_endian endian, + wchar_t *pwcs, int maxlen) + { + u16 *op; +@@ -171,6 +171,17 @@ int utf8s_to_utf16s(const u8 *s, int len, enum utf16_endian endian, + } + EXPORT_SYMBOL(utf8s_to_utf16s); + ++/* Binary-compatibility wrapper */ ++#undef utf8s_to_utf16s ++int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs) ++{ ++ /* Output length in code units is never longer than input ++ * length in bytes, so we can trivially set maxlen = len. ++ */ ++ return utf8s_to_utf16s_new(s, len, UTF16_HOST_ENDIAN, pwcs, len); ++} ++EXPORT_SYMBOL(utf8s_to_utf16s); ++ + static inline unsigned long get_utf16(unsigned c, enum utf16_endian endian) + { + switch (endian) { +diff --git a/include/linux/nls.h b/include/linux/nls.h +index 5dc635f..6a2a00c 100644 +--- a/include/linux/nls.h ++++ b/include/linux/nls.h +@@ -52,8 +52,10 @@ extern struct nls_table *load_nls_default(void); + + extern int utf8_to_utf32(const u8 *s, int len, unicode_t *pu); + extern int utf32_to_utf8(unicode_t u, u8 *s, int maxlen); +-extern int utf8s_to_utf16s(const u8 *s, int len, ++extern int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs); ++extern int utf8s_to_utf16s_new(const u8 *s, int len, + enum utf16_endian endian, wchar_t *pwcs, int maxlen); ++#define utf8s_to_utf16s utf8s_to_utf16s_new + extern int utf16s_to_utf8s(const wchar_t *pwcs, int len, + enum utf16_endian endian, u8 *s, int maxlen); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch b/debian/patches/features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch new file mode 100644 index 000000000..85879b917 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch @@ -0,0 +1,164 @@ +From: Alan Stern +Date: Thu, 17 Nov 2011 16:42:19 -0500 +Subject: [PATCH 01/77] NLS: improve UTF8 -> UTF16 string conversion routine + +commit 0720a06a7518c9d0c0125bd5d1f3b6264c55c3dd upstream. + +The utf8s_to_utf16s conversion routine needs to be improved. Unlike +its utf16s_to_utf8s sibling, it doesn't accept arguments specifying +the maximum length of the output buffer or the endianness of its +16-bit output. + +This patch (as1501) adds the two missing arguments, and adjusts the +only two places in the kernel where the function is called. A +follow-on patch will add a third caller that does utilize the new +capabilities. + +The two conversion routines are still annoyingly inconsistent in the +way they handle invalid byte combinations. But that's a subject for a +different patch. + +Signed-off-by: Alan Stern +CC: Clemens Ladisch +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 10 ++++++---- + fs/fat/namei_vfat.c | 3 ++- + fs/nls/nls_base.c | 43 +++++++++++++++++++++++++++++++++---------- + include/linux/nls.h | 5 +++-- + 4 files changed, 44 insertions(+), 17 deletions(-) + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index 89f5244..0e8343f 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -212,11 +212,13 @@ kvp_respond_to_host(char *key, char *value, int error) + * 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); ++ keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, ++ (wchar_t *) kvp_data->data.key, ++ HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); + kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ +- valuelen = utf8s_to_utf16s(value, strlen(value), +- (wchar_t *)kvp_data->data.value); ++ valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, ++ (wchar_t *) kvp_data->data.value, ++ HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); + kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + + kvp_data->data.value_type = REG_SZ; /* all our values are strings */ +diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c +index a87a656..c25cf15 100644 +--- a/fs/fat/namei_vfat.c ++++ b/fs/fat/namei_vfat.c +@@ -512,7 +512,8 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, + int charlen; + + if (utf8) { +- *outlen = utf8s_to_utf16s(name, len, (wchar_t *)outname); ++ *outlen = utf8s_to_utf16s(name, len, UTF16_HOST_ENDIAN, ++ (wchar_t *) outname, FAT_LFN_LEN + 2); + if (*outlen < 0) + return *outlen; + else if (*outlen > FAT_LFN_LEN) +diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c +index 44a88a9..0eb059e 100644 +--- a/fs/nls/nls_base.c ++++ b/fs/nls/nls_base.c +@@ -114,34 +114,57 @@ int utf32_to_utf8(unicode_t u, u8 *s, int maxlen) + } + EXPORT_SYMBOL(utf32_to_utf8); + +-int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs) ++static inline void put_utf16(wchar_t *s, unsigned c, enum utf16_endian endian) ++{ ++ switch (endian) { ++ default: ++ *s = (wchar_t) c; ++ break; ++ case UTF16_LITTLE_ENDIAN: ++ *s = __cpu_to_le16(c); ++ break; ++ case UTF16_BIG_ENDIAN: ++ *s = __cpu_to_be16(c); ++ break; ++ } ++} ++ ++int utf8s_to_utf16s(const u8 *s, int len, enum utf16_endian endian, ++ wchar_t *pwcs, int maxlen) + { + u16 *op; + int size; + unicode_t u; + + op = pwcs; +- while (*s && len > 0) { ++ while (len > 0 && maxlen > 0 && *s) { + if (*s & 0x80) { + size = utf8_to_utf32(s, len, &u); + if (size < 0) + return -EINVAL; ++ s += size; ++ len -= size; + + if (u >= PLANE_SIZE) { ++ if (maxlen < 2) ++ break; + u -= PLANE_SIZE; +- *op++ = (wchar_t) (SURROGATE_PAIR | +- ((u >> 10) & SURROGATE_BITS)); +- *op++ = (wchar_t) (SURROGATE_PAIR | ++ put_utf16(op++, SURROGATE_PAIR | ++ ((u >> 10) & SURROGATE_BITS), ++ endian); ++ put_utf16(op++, SURROGATE_PAIR | + SURROGATE_LOW | +- (u & SURROGATE_BITS)); ++ (u & SURROGATE_BITS), ++ endian); ++ maxlen -= 2; + } else { +- *op++ = (wchar_t) u; ++ put_utf16(op++, u, endian); ++ maxlen--; + } +- s += size; +- len -= size; + } else { +- *op++ = *s++; ++ put_utf16(op++, *s++, endian); + len--; ++ maxlen--; + } + } + return op - pwcs; +diff --git a/include/linux/nls.h b/include/linux/nls.h +index d47beef..5dc635f 100644 +--- a/include/linux/nls.h ++++ b/include/linux/nls.h +@@ -43,7 +43,7 @@ enum utf16_endian { + UTF16_BIG_ENDIAN + }; + +-/* nls.c */ ++/* nls_base.c */ + extern int register_nls(struct nls_table *); + extern int unregister_nls(struct nls_table *); + extern struct nls_table *load_nls(char *); +@@ -52,7 +52,8 @@ extern struct nls_table *load_nls_default(void); + + extern int utf8_to_utf32(const u8 *s, int len, unicode_t *pu); + extern int utf32_to_utf8(unicode_t u, u8 *s, int maxlen); +-extern int utf8s_to_utf16s(const u8 *s, int len, wchar_t *pwcs); ++extern int utf8s_to_utf16s(const u8 *s, int len, ++ enum utf16_endian endian, wchar_t *pwcs, int maxlen); + extern int utf16s_to_utf8s(const wchar_t *pwcs, int len, + enum utf16_endian endian, u8 *s, int maxlen); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0002-HID-Move-the-hid-hyperv-driver-out-of-staging.patch b/debian/patches/features/x86/hyperv/0002-HID-Move-the-hid-hyperv-driver-out-of-staging.patch new file mode 100644 index 000000000..c5c5263a9 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0002-HID-Move-the-hid-hyperv-driver-out-of-staging.patch @@ -0,0 +1,646 @@ +From: "K. Y. Srinivasan" +Date: Thu, 17 Nov 2011 08:47:29 -0800 +Subject: [PATCH 02/77] HID: Move the hid-hyperv driver out of staging + +commit b95f5bcb811e3905b5376f87789da8d097fee682 upstream. + +The file hid-hyperv.c implements a hid compliant mouse driver for use on a +Hyper-V based system. This driver is currently in the staging area and as part +of the effort to move this driver out of staging, I had posted the driver code +for community review a few weeks ago. This current patch addresses all the +review comments I have gotten to date. All the relevant patches have already +been submitted to the staging tree as well. + +As per Greg's suggestion, this patch does not get rid of the code from +the staging area. Once the mouse driver lands under the hid directory, +we will cleanup the staging directory. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Jiri Kosina +--- + drivers/hid/Kconfig | 6 + + drivers/hid/Makefile | 1 + + drivers/hid/hid-hyperv.c | 582 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 589 insertions(+) + create mode 100644 drivers/hid/hid-hyperv.c + +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index d21f6d0..4c09210 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -646,6 +646,12 @@ config HID_ZYDACRON + ---help--- + Support for Zydacron remote control. + ++config HYPERV_MOUSE ++ tristate "Microsoft Hyper-V mouse driver" ++ depends on HYPERV ++ ---help--- ++ Select this option to enable the Hyper-V mouse driver. ++ + endmenu + + endif # HID_SUPPORT +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index 1e0d2a6..fbb1ee6 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -78,6 +78,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o + obj-$(CONFIG_HID_WACOM) += hid-wacom.o + obj-$(CONFIG_HID_WALTOP) += hid-waltop.o + obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o ++obj-$(CONFIG_HYPERV_MOUSE) += hid-hyperv.o + + obj-$(CONFIG_USB_HID) += usbhid/ + obj-$(CONFIG_USB_MOUSE) += usbhid/ +diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c +new file mode 100644 +index 0000000..d503cbb +--- /dev/null ++++ b/drivers/hid/hid-hyperv.c +@@ -0,0 +1,582 @@ ++/* ++ * Copyright (c) 2009, Citrix Systems, Inc. ++ * Copyright (c) 2010, Microsoft Corporation. ++ * Copyright (c) 2011, Novell Inc. ++ * ++ * 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. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct hv_input_dev_info { ++ unsigned int size; ++ unsigned short vendor; ++ unsigned short product; ++ unsigned short version; ++ unsigned short reserved[11]; ++}; ++ ++/* The maximum size of a synthetic input message. */ ++#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 ++ ++/* ++ * Current version ++ * ++ * History: ++ * Beta, RC < 2008/1/22 1,0 ++ * RC > 2008/1/22 2,0 ++ */ ++#define SYNTHHID_INPUT_VERSION_MAJOR 2 ++#define SYNTHHID_INPUT_VERSION_MINOR 0 ++#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ ++ (SYNTHHID_INPUT_VERSION_MAJOR << 16)) ++ ++ ++#pragma pack(push, 1) ++/* ++ * Message types in the synthetic input protocol ++ */ ++enum synthhid_msg_type { ++ SYNTH_HID_PROTOCOL_REQUEST, ++ SYNTH_HID_PROTOCOL_RESPONSE, ++ SYNTH_HID_INITIAL_DEVICE_INFO, ++ SYNTH_HID_INITIAL_DEVICE_INFO_ACK, ++ SYNTH_HID_INPUT_REPORT, ++ SYNTH_HID_MAX ++}; ++ ++/* ++ * Basic message structures. ++ */ ++struct synthhid_msg_hdr { ++ enum synthhid_msg_type type; ++ u32 size; ++}; ++ ++struct synthhid_msg { ++ struct synthhid_msg_hdr header; ++ char data[1]; /* Enclosed message */ ++}; ++ ++union synthhid_version { ++ struct { ++ u16 minor_version; ++ u16 major_version; ++ }; ++ u32 version; ++}; ++ ++/* ++ * Protocol messages ++ */ ++struct synthhid_protocol_request { ++ struct synthhid_msg_hdr header; ++ union synthhid_version version_requested; ++}; ++ ++struct synthhid_protocol_response { ++ struct synthhid_msg_hdr header; ++ union synthhid_version version_requested; ++ unsigned char approved; ++}; ++ ++struct synthhid_device_info { ++ struct synthhid_msg_hdr header; ++ struct hv_input_dev_info hid_dev_info; ++ struct hid_descriptor hid_descriptor; ++}; ++ ++struct synthhid_device_info_ack { ++ struct synthhid_msg_hdr header; ++ unsigned char reserved; ++}; ++ ++struct synthhid_input_report { ++ struct synthhid_msg_hdr header; ++ char buffer[1]; ++}; ++ ++#pragma pack(pop) ++ ++#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) ++#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) ++ ++ ++enum pipe_prot_msg_type { ++ PIPE_MESSAGE_INVALID, ++ PIPE_MESSAGE_DATA, ++ PIPE_MESSAGE_MAXIMUM ++}; ++ ++ ++struct pipe_prt_msg { ++ enum pipe_prot_msg_type type; ++ u32 size; ++ char data[1]; ++}; ++ ++struct mousevsc_prt_msg { ++ enum pipe_prot_msg_type type; ++ u32 size; ++ union { ++ struct synthhid_protocol_request request; ++ struct synthhid_protocol_response response; ++ struct synthhid_device_info_ack ack; ++ }; ++}; ++ ++/* ++ * Represents an mousevsc device ++ */ ++struct mousevsc_dev { ++ struct hv_device *device; ++ bool init_complete; ++ bool connected; ++ struct mousevsc_prt_msg protocol_req; ++ struct mousevsc_prt_msg protocol_resp; ++ /* Synchronize the request/response if needed */ ++ struct completion wait_event; ++ int dev_info_status; ++ ++ struct hid_descriptor *hid_desc; ++ unsigned char *report_desc; ++ u32 report_desc_size; ++ struct hv_input_dev_info hid_dev_info; ++ struct hid_device *hid_device; ++}; ++ ++ ++static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device) ++{ ++ struct mousevsc_dev *input_dev; ++ ++ input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); ++ ++ if (!input_dev) ++ return NULL; ++ ++ input_dev->device = device; ++ hv_set_drvdata(device, input_dev); ++ init_completion(&input_dev->wait_event); ++ input_dev->init_complete = false; ++ ++ return input_dev; ++} ++ ++static void mousevsc_free_device(struct mousevsc_dev *device) ++{ ++ kfree(device->hid_desc); ++ kfree(device->report_desc); ++ hv_set_drvdata(device->device, NULL); ++ kfree(device); ++} ++ ++static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, ++ struct synthhid_device_info *device_info) ++{ ++ int ret = 0; ++ struct hid_descriptor *desc; ++ struct mousevsc_prt_msg ack; ++ ++ input_device->dev_info_status = -ENOMEM; ++ ++ input_device->hid_dev_info = device_info->hid_dev_info; ++ desc = &device_info->hid_descriptor; ++ if (desc->bLength == 0) ++ goto cleanup; ++ ++ input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); ++ ++ if (!input_device->hid_desc) ++ goto cleanup; ++ ++ memcpy(input_device->hid_desc, desc, desc->bLength); ++ ++ input_device->report_desc_size = desc->desc[0].wDescriptorLength; ++ if (input_device->report_desc_size == 0) { ++ input_device->dev_info_status = -EINVAL; ++ goto cleanup; ++ } ++ ++ input_device->report_desc = kzalloc(input_device->report_desc_size, ++ GFP_ATOMIC); ++ ++ if (!input_device->report_desc) { ++ input_device->dev_info_status = -ENOMEM; ++ goto cleanup; ++ } ++ ++ memcpy(input_device->report_desc, ++ ((unsigned char *)desc) + desc->bLength, ++ desc->desc[0].wDescriptorLength); ++ ++ /* Send the ack */ ++ memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); ++ ++ ack.type = PIPE_MESSAGE_DATA; ++ ack.size = sizeof(struct synthhid_device_info_ack); ++ ++ ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; ++ ack.ack.header.size = 1; ++ ack.ack.reserved = 0; ++ ++ ret = vmbus_sendpacket(input_device->device->channel, ++ &ack, ++ sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + ++ sizeof(struct synthhid_device_info_ack), ++ (unsigned long)&ack, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ ++ if (!ret) ++ input_device->dev_info_status = 0; ++ ++cleanup: ++ complete(&input_device->wait_event); ++ ++ return; ++} ++ ++static void mousevsc_on_receive(struct hv_device *device, ++ struct vmpacket_descriptor *packet) ++{ ++ struct pipe_prt_msg *pipe_msg; ++ struct synthhid_msg *hid_msg; ++ struct mousevsc_dev *input_dev = hv_get_drvdata(device); ++ struct synthhid_input_report *input_report; ++ ++ pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + ++ (packet->offset8 << 3)); ++ ++ if (pipe_msg->type != PIPE_MESSAGE_DATA) ++ return; ++ ++ hid_msg = (struct synthhid_msg *)pipe_msg->data; ++ ++ switch (hid_msg->header.type) { ++ case SYNTH_HID_PROTOCOL_RESPONSE: ++ /* ++ * While it will be impossible for us to protect against ++ * malicious/buggy hypervisor/host, add a check here to ++ * ensure we don't corrupt memory. ++ */ ++ if ((pipe_msg->size + sizeof(struct pipe_prt_msg) ++ - sizeof(unsigned char)) ++ > sizeof(struct mousevsc_prt_msg)) { ++ WARN_ON(1); ++ break; ++ } ++ ++ memcpy(&input_dev->protocol_resp, pipe_msg, ++ pipe_msg->size + sizeof(struct pipe_prt_msg) - ++ sizeof(unsigned char)); ++ complete(&input_dev->wait_event); ++ break; ++ ++ case SYNTH_HID_INITIAL_DEVICE_INFO: ++ WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); ++ ++ /* ++ * Parse out the device info into device attr, ++ * hid desc and report desc ++ */ ++ mousevsc_on_receive_device_info(input_dev, ++ (struct synthhid_device_info *)pipe_msg->data); ++ break; ++ case SYNTH_HID_INPUT_REPORT: ++ input_report = ++ (struct synthhid_input_report *)pipe_msg->data; ++ if (!input_dev->init_complete) ++ break; ++ hid_input_report(input_dev->hid_device, ++ HID_INPUT_REPORT, input_report->buffer, ++ input_report->header.size, 1); ++ break; ++ default: ++ pr_err("unsupported hid msg type - type %d len %d", ++ hid_msg->header.type, hid_msg->header.size); ++ break; ++ } ++ ++} ++ ++static void mousevsc_on_channel_callback(void *context) ++{ ++ const int packet_size = 0x100; ++ int ret; ++ struct hv_device *device = context; ++ u32 bytes_recvd; ++ u64 req_id; ++ struct vmpacket_descriptor *desc; ++ unsigned char *buffer; ++ int bufferlen = packet_size; ++ ++ buffer = kmalloc(bufferlen, GFP_ATOMIC); ++ if (!buffer) ++ return; ++ ++ do { ++ ret = vmbus_recvpacket_raw(device->channel, buffer, ++ bufferlen, &bytes_recvd, &req_id); ++ ++ switch (ret) { ++ case 0: ++ if (bytes_recvd <= 0) { ++ kfree(buffer); ++ return; ++ } ++ desc = (struct vmpacket_descriptor *)buffer; ++ ++ switch (desc->type) { ++ case VM_PKT_COMP: ++ break; ++ ++ case VM_PKT_DATA_INBAND: ++ mousevsc_on_receive(device, desc); ++ break; ++ ++ default: ++ pr_err("unhandled packet type %d, tid %llx len %d\n", ++ desc->type, req_id, bytes_recvd); ++ break; ++ } ++ ++ break; ++ ++ case -ENOBUFS: ++ kfree(buffer); ++ /* Handle large packet */ ++ bufferlen = bytes_recvd; ++ buffer = kmalloc(bytes_recvd, GFP_ATOMIC); ++ ++ if (!buffer) ++ return; ++ ++ break; ++ } ++ } while (1); ++ ++} ++ ++static int mousevsc_connect_to_vsp(struct hv_device *device) ++{ ++ int ret = 0; ++ int t; ++ struct mousevsc_dev *input_dev = hv_get_drvdata(device); ++ struct mousevsc_prt_msg *request; ++ struct mousevsc_prt_msg *response; ++ ++ request = &input_dev->protocol_req; ++ memset(request, 0, sizeof(struct mousevsc_prt_msg)); ++ ++ request->type = PIPE_MESSAGE_DATA; ++ request->size = sizeof(struct synthhid_protocol_request); ++ request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; ++ request->request.header.size = sizeof(unsigned int); ++ request->request.version_requested.version = SYNTHHID_INPUT_VERSION; ++ ++ ret = vmbus_sendpacket(device->channel, request, ++ sizeof(struct pipe_prt_msg) - ++ sizeof(unsigned char) + ++ sizeof(struct synthhid_protocol_request), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ if (ret) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); ++ if (!t) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ response = &input_dev->protocol_resp; ++ ++ if (!response->response.approved) { ++ pr_err("synthhid protocol request failed (version %d)\n", ++ SYNTHHID_INPUT_VERSION); ++ ret = -ENODEV; ++ goto cleanup; ++ } ++ ++ t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); ++ if (!t) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ /* ++ * We should have gotten the device attr, hid desc and report ++ * desc at this point ++ */ ++ ret = input_dev->dev_info_status; ++ ++cleanup: ++ return ret; ++} ++ ++static int mousevsc_hid_open(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static int mousevsc_hid_start(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void mousevsc_hid_close(struct hid_device *hid) ++{ ++} ++ ++static void mousevsc_hid_stop(struct hid_device *hid) ++{ ++} ++ ++static struct hid_ll_driver mousevsc_ll_driver = { ++ .open = mousevsc_hid_open, ++ .close = mousevsc_hid_close, ++ .start = mousevsc_hid_start, ++ .stop = mousevsc_hid_stop, ++}; ++ ++static struct hid_driver mousevsc_hid_driver; ++ ++static int mousevsc_probe(struct hv_device *device, ++ const struct hv_vmbus_device_id *dev_id) ++{ ++ int ret; ++ struct mousevsc_dev *input_dev; ++ struct hid_device *hid_dev; ++ ++ input_dev = mousevsc_alloc_device(device); ++ ++ if (!input_dev) ++ return -ENOMEM; ++ ++ ret = vmbus_open(device->channel, ++ INPUTVSC_SEND_RING_BUFFER_SIZE, ++ INPUTVSC_RECV_RING_BUFFER_SIZE, ++ NULL, ++ 0, ++ mousevsc_on_channel_callback, ++ device ++ ); ++ ++ if (ret) ++ goto probe_err0; ++ ++ ret = mousevsc_connect_to_vsp(device); ++ ++ if (ret) ++ goto probe_err1; ++ ++ /* workaround SA-167 */ ++ if (input_dev->report_desc[14] == 0x25) ++ input_dev->report_desc[14] = 0x29; ++ ++ hid_dev = hid_allocate_device(); ++ if (IS_ERR(hid_dev)) { ++ ret = PTR_ERR(hid_dev); ++ goto probe_err1; ++ } ++ ++ hid_dev->ll_driver = &mousevsc_ll_driver; ++ hid_dev->driver = &mousevsc_hid_driver; ++ hid_dev->bus = BUS_VIRTUAL; ++ hid_dev->vendor = input_dev->hid_dev_info.vendor; ++ hid_dev->product = input_dev->hid_dev_info.product; ++ hid_dev->version = input_dev->hid_dev_info.version; ++ input_dev->hid_device = hid_dev; ++ ++ sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); ++ ++ ret = hid_parse_report(hid_dev, input_dev->report_desc, ++ input_dev->report_desc_size); ++ ++ if (ret) { ++ hid_err(hid_dev, "parse failed\n"); ++ goto probe_err2; ++ } ++ ++ ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); ++ ++ if (ret) { ++ hid_err(hid_dev, "hw start failed\n"); ++ goto probe_err2; ++ } ++ ++ input_dev->connected = true; ++ input_dev->init_complete = true; ++ ++ return ret; ++ ++probe_err2: ++ hid_destroy_device(hid_dev); ++ ++probe_err1: ++ vmbus_close(device->channel); ++ ++probe_err0: ++ mousevsc_free_device(input_dev); ++ ++ return ret; ++} ++ ++ ++static int mousevsc_remove(struct hv_device *dev) ++{ ++ struct mousevsc_dev *input_dev = hv_get_drvdata(dev); ++ ++ vmbus_close(dev->channel); ++ hid_destroy_device(input_dev->hid_device); ++ mousevsc_free_device(input_dev); ++ ++ return 0; ++} ++ ++static const struct hv_vmbus_device_id id_table[] = { ++ /* Mouse guid */ ++ { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, ++ 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(vmbus, id_table); ++ ++static struct hv_driver mousevsc_drv = { ++ .name = KBUILD_MODNAME, ++ .id_table = id_table, ++ .probe = mousevsc_probe, ++ .remove = mousevsc_remove, ++}; ++ ++static int __init mousevsc_init(void) ++{ ++ return vmbus_driver_register(&mousevsc_drv); ++} ++ ++static void __exit mousevsc_exit(void) ++{ ++ vmbus_driver_unregister(&mousevsc_drv); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(HV_DRV_VERSION); ++module_init(mousevsc_init); ++module_exit(mousevsc_exit); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0003-Staging-hv-storvsc-Use-mempools-to-allocate-struct-s.patch b/debian/patches/features/x86/hyperv/0003-Staging-hv-storvsc-Use-mempools-to-allocate-struct-s.patch new file mode 100644 index 000000000..a79eaa8d1 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0003-Staging-hv-storvsc-Use-mempools-to-allocate-struct-s.patch @@ -0,0 +1,147 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:40 -0800 +Subject: [PATCH 03/77] Staging: hv: storvsc: Use mempools to allocate struct + storvsc_cmd_request + +commit 4e03e697c5446146318cdb6344af0060541cbc1c upstream. + +We intend to use the storage driver to manage the root device. +To avoid deadlocks, use mempools to allocate struct storvsc_cmd_request. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index ae8c33e..6a255e9 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -42,6 +43,7 @@ + #include + + ++#define STORVSC_MIN_BUF_NR 64 + #define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) + static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; + +@@ -287,6 +289,7 @@ struct storvsc_device { + struct hv_host_device { + struct hv_device *dev; + struct kmem_cache *request_pool; ++ mempool_t *request_mempool; + unsigned int port; + unsigned char path; + unsigned char target; +@@ -974,8 +977,10 @@ static int storvsc_remove(struct hv_device *dev) + + storvsc_dev_remove(dev); + if (host_dev->request_pool) { ++ mempool_destroy(host_dev->request_mempool); + kmem_cache_destroy(host_dev->request_pool); + host_dev->request_pool = NULL; ++ host_dev->request_mempool = NULL; + } + return 0; + } +@@ -1120,7 +1125,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + + scsi_done_fn(scmnd); + +- kmem_cache_free(host_dev->request_pool, cmd_request); ++ mempool_free(cmd_request, host_dev->request_mempool); + } + + static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) +@@ -1176,12 +1181,13 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + + request_size = sizeof(struct storvsc_cmd_request); + +- cmd_request = kmem_cache_zalloc(host_dev->request_pool, ++ cmd_request = mempool_alloc(host_dev->request_mempool, + GFP_ATOMIC); + if (!cmd_request) { + scmnd->scsi_done = NULL; + return SCSI_MLQUEUE_DEVICE_BUSY; + } ++ memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); + + /* Setup the cmd request */ + cmd_request->bounce_sgl_count = 0; +@@ -1235,8 +1241,8 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + if (!cmd_request->bounce_sgl) { + scmnd->scsi_done = NULL; + scmnd->host_scribble = NULL; +- kmem_cache_free(host_dev->request_pool, +- cmd_request); ++ mempool_free(cmd_request, ++ host_dev->request_mempool); + + return SCSI_MLQUEUE_HOST_BUSY; + } +@@ -1278,7 +1284,7 @@ retry_request: + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + +- kmem_cache_free(host_dev->request_pool, cmd_request); ++ mempool_free(cmd_request, host_dev->request_mempool); + + scmnd->scsi_done = NULL; + scmnd->host_scribble = NULL; +@@ -1348,6 +1354,7 @@ static int storvsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) + { + int ret; ++ int number = STORVSC_MIN_BUF_NR; + struct Scsi_Host *host; + struct hv_host_device *host_dev; + bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); +@@ -1376,8 +1383,19 @@ static int storvsc_probe(struct hv_device *device, + return -ENOMEM; + } + ++ host_dev->request_mempool = mempool_create(number, mempool_alloc_slab, ++ mempool_free_slab, ++ host_dev->request_pool); ++ ++ if (!host_dev->request_mempool) { ++ kmem_cache_destroy(host_dev->request_pool); ++ scsi_host_put(host); ++ return -ENOMEM; ++ } ++ + stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); + if (!stor_device) { ++ mempool_destroy(host_dev->request_mempool); + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -ENOMEM; +@@ -1392,6 +1410,7 @@ static int storvsc_probe(struct hv_device *device, + stor_device->port_number = host->host_no; + ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); + if (ret) { ++ mempool_destroy(host_dev->request_mempool); + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + kfree(stor_device); +@@ -1431,6 +1450,7 @@ static int storvsc_probe(struct hv_device *device, + + err_out: + storvsc_dev_remove(device); ++ mempool_destroy(host_dev->request_mempool); + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return -ENODEV; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0004-Staging-hv-storvsc-Cleanup-error-handling-in-the-pro.patch b/debian/patches/features/x86/hyperv/0004-Staging-hv-storvsc-Cleanup-error-handling-in-the-pro.patch new file mode 100644 index 000000000..f8ab5b9c7 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0004-Staging-hv-storvsc-Cleanup-error-handling-in-the-pro.patch @@ -0,0 +1,104 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:41 -0800 +Subject: [PATCH 04/77] Staging: hv: storvsc: Cleanup error handling in the + probe function + +commit 225ce6eab741d51d565f67ce9065c596c6535f25 upstream. + +Cleanup error handling in the probe function. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 42 ++++++++++++++++++++++---------------- + 1 file changed, 24 insertions(+), 18 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 6a255e9..a72cc22 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1388,17 +1388,14 @@ static int storvsc_probe(struct hv_device *device, + host_dev->request_pool); + + if (!host_dev->request_mempool) { +- kmem_cache_destroy(host_dev->request_pool); +- scsi_host_put(host); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_out0; + } + + stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); + if (!stor_device) { +- mempool_destroy(host_dev->request_mempool); +- kmem_cache_destroy(host_dev->request_pool); +- scsi_host_put(host); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_out1; + } + + stor_device->destroy = false; +@@ -1409,13 +1406,8 @@ static int storvsc_probe(struct hv_device *device, + + stor_device->port_number = host->host_no; + ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); +- if (ret) { +- mempool_destroy(host_dev->request_mempool); +- kmem_cache_destroy(host_dev->request_pool); +- scsi_host_put(host); +- kfree(stor_device); +- return ret; +- } ++ if (ret) ++ goto err_out2; + + if (dev_is_ide) + storvsc_get_ide_info(device, &target, &path); +@@ -1435,7 +1427,7 @@ static int storvsc_probe(struct hv_device *device, + /* Register the HBA and start the scsi bus scan */ + ret = scsi_add_host(host, &device->device); + if (ret != 0) +- goto err_out; ++ goto err_out3; + + if (!dev_is_ide) { + scsi_scan_host(host); +@@ -1444,16 +1436,30 @@ static int storvsc_probe(struct hv_device *device, + ret = scsi_add_device(host, 0, target, 0); + if (ret) { + scsi_remove_host(host); +- goto err_out; ++ goto err_out3; + } + return 0; + +-err_out: ++err_out3: ++ /* ++ * Once we have connected with the host, we would need to ++ * to invoke storvsc_dev_remove() to rollback this state and ++ * this call also frees up the stor_device; hence the jump around ++ * err_out2 label. ++ */ + storvsc_dev_remove(device); ++ goto err_out1; ++ ++err_out2: ++ kfree(stor_device); ++ ++err_out1: + mempool_destroy(host_dev->request_mempool); ++ ++err_out0: + kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); +- return -ENODEV; ++ return ret; + } + + /* The one and only one */ +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0005-Staging-hv-storvsc-Fixup-the-error-when-processing-S.patch b/debian/patches/features/x86/hyperv/0005-Staging-hv-storvsc-Fixup-the-error-when-processing-S.patch new file mode 100644 index 000000000..0347ce080 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0005-Staging-hv-storvsc-Fixup-the-error-when-processing-S.patch @@ -0,0 +1,32 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:42 -0800 +Subject: [PATCH 05/77] Staging: hv: storvsc: Fixup the error when processing + SET_WINDOW command + +commit 59e00e744d2413430ebaa09a5fdb61e9ad02a492 upstream. + +Fixup the error when processing SET_WINDOW command. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index a72cc22..e16c7a3 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1136,7 +1136,7 @@ static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) + switch (scsi_op) { + /* smartd sends this command, which will offline the device */ + case SET_WINDOW: +- scmnd->result = DID_ERROR << 16; ++ scmnd->result = ILLEGAL_REQUEST << 16; + allowed = false; + break; + default: +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0006-Staging-hv-storvsc-Fix-error-handling-storvsc_host_r.patch b/debian/patches/features/x86/hyperv/0006-Staging-hv-storvsc-Fix-error-handling-storvsc_host_r.patch new file mode 100644 index 000000000..160b124af --- /dev/null +++ b/debian/patches/features/x86/hyperv/0006-Staging-hv-storvsc-Fix-error-handling-storvsc_host_r.patch @@ -0,0 +1,79 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:43 -0800 +Subject: [PATCH 06/77] Staging: hv: storvsc: Fix error handling + storvsc_host_reset() + +commit a00e8224c19fa5ba3007da00d850865cbefcaabd upstream. + +Fix error handling storvsc_host_reset(). I would like to thank +Long Li for reporting this. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Reported-by: Long Li +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 20 ++++++-------------- + 1 file changed, 6 insertions(+), 14 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index e16c7a3..a145245 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1019,7 +1019,7 @@ static int storvsc_host_reset(struct hv_device *device) + + stor_device = get_out_stor_device(device); + if (!stor_device) +- return -ENODEV; ++ return FAILED; + + request = &stor_device->reset_request; + vstor_packet = &request->vstor_packet; +@@ -1036,13 +1036,11 @@ static int storvsc_host_reset(struct hv_device *device) + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) +- goto cleanup; ++ return FAILED; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } ++ if (t == 0) ++ return TIMEOUT_ERROR; + + + /* +@@ -1050,8 +1048,7 @@ static int storvsc_host_reset(struct hv_device *device) + * should have been flushed out and return to us + */ + +-cleanup: +- return ret; ++ return SUCCESS; + } + + +@@ -1060,16 +1057,11 @@ cleanup: + */ + static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + { +- int ret; + struct hv_host_device *host_dev = + (struct hv_host_device *)scmnd->device->host->hostdata; + struct hv_device *dev = host_dev->dev; + +- ret = storvsc_host_reset(dev); +- if (ret != 0) +- return ret; +- +- return ret; ++ return storvsc_host_reset(dev); + } + + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0007-Staging-hv-storvsc-Use-the-accessor-function-shost_p.patch b/debian/patches/features/x86/hyperv/0007-Staging-hv-storvsc-Use-the-accessor-function-shost_p.patch new file mode 100644 index 000000000..668ee1f02 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0007-Staging-hv-storvsc-Use-the-accessor-function-shost_p.patch @@ -0,0 +1,72 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:44 -0800 +Subject: [PATCH 07/77] Staging: hv: storvsc: Use the accessor function + shost_priv() + +commit 7f33f30a67cebbdaa938e34c5144424d2f0ce9cc upstream. + +Use the accessor function shost_priv(). + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index a145245..b21e85f 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -968,8 +968,7 @@ static int storvsc_remove(struct hv_device *dev) + { + struct storvsc_device *stor_device = hv_get_drvdata(dev); + struct Scsi_Host *host = stor_device->host; +- struct hv_host_device *host_dev = +- (struct hv_host_device *)host->hostdata; ++ struct hv_host_device *host_dev = shost_priv(host); + + scsi_remove_host(host); + +@@ -1057,8 +1056,7 @@ static int storvsc_host_reset(struct hv_device *device) + */ + static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + { +- struct hv_host_device *host_dev = +- (struct hv_host_device *)scmnd->device->host->hostdata; ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); + struct hv_device *dev = host_dev->dev; + + return storvsc_host_reset(dev); +@@ -1073,8 +1071,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + struct storvsc_cmd_request *cmd_request = + (struct storvsc_cmd_request *)request->context; + struct scsi_cmnd *scmnd = cmd_request->cmd; +- struct hv_host_device *host_dev = +- (struct hv_host_device *)scmnd->device->host->hostdata; ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); + void (*scsi_done_fn)(struct scsi_cmnd *); + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; +@@ -1144,8 +1141,7 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + void (*done)(struct scsi_cmnd *)) + { + int ret; +- struct hv_host_device *host_dev = +- (struct hv_host_device *)scmnd->device->host->hostdata; ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); + struct hv_device *dev = host_dev->dev; + struct hv_storvsc_request *request; + struct storvsc_cmd_request *cmd_request; +@@ -1359,7 +1355,7 @@ static int storvsc_probe(struct hv_device *device, + if (!host) + return -ENOMEM; + +- host_dev = (struct hv_host_device *)host->hostdata; ++ host_dev = shost_priv(host); + memset(host_dev, 0, sizeof(struct hv_host_device)); + + host_dev->port = host->host_no; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0008-Staging-hv-storvsc-Use-the-unlocked-version-queuecom.patch b/debian/patches/features/x86/hyperv/0008-Staging-hv-storvsc-Use-the-unlocked-version-queuecom.patch new file mode 100644 index 000000000..1080b6450 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0008-Staging-hv-storvsc-Use-the-unlocked-version-queuecom.patch @@ -0,0 +1,91 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:45 -0800 +Subject: [PATCH 08/77] Staging: hv: storvsc: Use the unlocked version + queuecommand + +commit bab445e12b45153282fb7d39143516e11987deaa upstream. + +Use the unlocked version queuecommand. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 19 +++++-------------- + 1 file changed, 5 insertions(+), 14 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index b21e85f..827bc68 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1137,11 +1137,10 @@ static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) + /* + * storvsc_queuecommand - Initiate command processing + */ +-static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, +- void (*done)(struct scsi_cmnd *)) ++static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + { + int ret; +- struct hv_host_device *host_dev = shost_priv(scmnd->device->host); ++ struct hv_host_device *host_dev = shost_priv(host); + struct hv_device *dev = host_dev->dev; + struct hv_storvsc_request *request; + struct storvsc_cmd_request *cmd_request; +@@ -1152,7 +1151,7 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + struct vmscsi_request *vm_srb; + + if (storvsc_check_scsi_cmd(scmnd) == false) { +- done(scmnd); ++ scmnd->scsi_done(scmnd); + return 0; + } + +@@ -1165,16 +1164,13 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + goto retry_request; + } + +- scmnd->scsi_done = done; +- + request_size = sizeof(struct storvsc_cmd_request); + + cmd_request = mempool_alloc(host_dev->request_mempool, + GFP_ATOMIC); +- if (!cmd_request) { +- scmnd->scsi_done = NULL; ++ if (!cmd_request) + return SCSI_MLQUEUE_DEVICE_BUSY; +- } ++ + memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); + + /* Setup the cmd request */ +@@ -1227,7 +1223,6 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, + create_bounce_buffer(sgl, scsi_sg_count(scmnd), + scsi_bufflen(scmnd)); + if (!cmd_request->bounce_sgl) { +- scmnd->scsi_done = NULL; + scmnd->host_scribble = NULL; + mempool_free(cmd_request, + host_dev->request_mempool); +@@ -1274,7 +1269,6 @@ retry_request: + + mempool_free(cmd_request, host_dev->request_mempool); + +- scmnd->scsi_done = NULL; + scmnd->host_scribble = NULL; + + ret = SCSI_MLQUEUE_DEVICE_BUSY; +@@ -1283,9 +1277,6 @@ retry_request: + return ret; + } + +-static DEF_SCSI_QCMD(storvsc_queuecommand) +- +- + /* Scsi driver */ + static struct scsi_host_template scsi_driver = { + .module = THIS_MODULE, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0009-Staging-hv-storvsc-use-the-macro-KBUILD_MODNAME.patch b/debian/patches/features/x86/hyperv/0009-Staging-hv-storvsc-use-the-macro-KBUILD_MODNAME.patch new file mode 100644 index 000000000..3916f6516 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0009-Staging-hv-storvsc-use-the-macro-KBUILD_MODNAME.patch @@ -0,0 +1,31 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:46 -0800 +Subject: [PATCH 09/77] Staging: hv: storvsc: use the macro KBUILD_MODNAME + +commit fafb0efc303a916f9902cbadddef3ea125720c1b upstream. + +Use the macro KBUILD_MODNAME. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 827bc68..916f5d0 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1444,7 +1444,7 @@ err_out0: + /* The one and only one */ + + static struct hv_driver storvsc_drv = { +- .name = "storvsc", ++ .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = storvsc_probe, + .remove = storvsc_remove, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0010-Staging-hv-storvsc-Get-rid-of-an-unnecessary-forward.patch b/debian/patches/features/x86/hyperv/0010-Staging-hv-storvsc-Get-rid-of-an-unnecessary-forward.patch new file mode 100644 index 000000000..fdda6f082 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0010-Staging-hv-storvsc-Get-rid-of-an-unnecessary-forward.patch @@ -0,0 +1,32 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:47 -0800 +Subject: [PATCH 10/77] Staging: hv: storvsc: Get rid of an unnecessary + forward declaration + +commit a404c1ff5556fe29460c097a34d2abe10ddd6e0a upstream. + +Get rid of an unnecessary forward declaration. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 916f5d0..4cdb417 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -236,8 +236,6 @@ struct vstor_packet { + #define STORVSC_MAX_CHANNELS 1 + #define STORVSC_MAX_CMD_LEN 16 + +-struct hv_storvsc_request; +- + /* Matches Windows-end */ + enum storvsc_request_type { + WRITE_TYPE, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0011-Staging-hv-storvsc-Upgrade-the-vmstor-protocol-versi.patch b/debian/patches/features/x86/hyperv/0011-Staging-hv-storvsc-Upgrade-the-vmstor-protocol-versi.patch new file mode 100644 index 000000000..abf5d4cd8 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0011-Staging-hv-storvsc-Upgrade-the-vmstor-protocol-versi.patch @@ -0,0 +1,43 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:48 -0800 +Subject: [PATCH 11/77] Staging: hv: storvsc: Upgrade the vmstor protocol + version + +commit 2b9525f511791758b394643b61754a9688e0707d upstream. + +In preparation for supporting hot add/remove of scsi devices, +upgrade the vmstor protocol version. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 4cdb417..9153641 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -80,7 +80,7 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + /* V1 Beta 0.1 */ + /* V1 RC < 2008/1/31 1.0 */ + /* V1 RC > 2008/1/31 2.0 */ +-#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) ++#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(4, 2) + + + +@@ -106,7 +106,8 @@ enum vstor_packet_operation { + VSTOR_OPERATION_END_INITIALIZATION = 8, + VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, + VSTOR_OPERATION_QUERY_PROPERTIES = 10, +- VSTOR_OPERATION_MAXIMUM = 10 ++ VSTOR_OPERATION_ENUMERATE_BUS = 11, ++ VSTOR_OPERATION_MAXIMUM = 11 + }; + + /* +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0012-Staging-hv-storvsc-Support-hot-add-of-scsi-disks.patch b/debian/patches/features/x86/hyperv/0012-Staging-hv-storvsc-Support-hot-add-of-scsi-disks.patch new file mode 100644 index 000000000..37768e1ec --- /dev/null +++ b/debian/patches/features/x86/hyperv/0012-Staging-hv-storvsc-Support-hot-add-of-scsi-disks.patch @@ -0,0 +1,79 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:49 -0800 +Subject: [PATCH 12/77] Staging: hv: storvsc: Support hot add of scsi disks + +commit 126757998a50659f79a3f1ff23fccfc40ff1bf5c upstream. + +Support hot add of scsi disks. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 9153641..7c82d14 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -304,6 +304,30 @@ struct storvsc_cmd_request { + struct hv_storvsc_request request; + }; + ++struct storvsc_scan_work { ++ struct work_struct work; ++ struct Scsi_Host *host; ++ uint lun; ++}; ++ ++static void storvsc_bus_scan(struct work_struct *work) ++{ ++ struct storvsc_scan_work *wrk; ++ int id, order_id; ++ ++ wrk = container_of(work, struct storvsc_scan_work, work); ++ for (id = 0; id < wrk->host->max_id; ++id) { ++ if (wrk->host->reverse_ordering) ++ order_id = wrk->host->max_id - id - 1; ++ else ++ order_id = id; ++ ++ scsi_scan_target(&wrk->host->shost_gendev, 0, ++ order_id, SCAN_WILD_CARD, 1); ++ } ++ kfree(wrk); ++} ++ + static inline struct storvsc_device *get_out_stor_device( + struct hv_device *device) + { +@@ -551,11 +575,25 @@ static void storvsc_on_receive(struct hv_device *device, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) + { ++ struct storvsc_scan_work *work; ++ struct storvsc_device *stor_device; ++ + switch (vstor_packet->operation) { + case VSTOR_OPERATION_COMPLETE_IO: + storvsc_on_io_completion(device, vstor_packet, request); + break; ++ + case VSTOR_OPERATION_REMOVE_DEVICE: ++ case VSTOR_OPERATION_ENUMERATE_BUS: ++ stor_device = get_in_stor_device(device); ++ work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); ++ if (!work) ++ return; ++ ++ INIT_WORK(&work->work, storvsc_bus_scan); ++ work->host = stor_device->host; ++ schedule_work(&work->work); ++ break; + + default: + break; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0013-Staging-hv-storvsc-Support-hot-removing-of-scsi-devi.patch b/debian/patches/features/x86/hyperv/0013-Staging-hv-storvsc-Support-hot-removing-of-scsi-devi.patch new file mode 100644 index 000000000..1bd1d5ecd --- /dev/null +++ b/debian/patches/features/x86/hyperv/0013-Staging-hv-storvsc-Support-hot-removing-of-scsi-devi.patch @@ -0,0 +1,89 @@ +From: "K. Y. Srinivasan" +Date: Tue, 8 Nov 2011 09:01:50 -0800 +Subject: [PATCH 13/77] Staging: hv: storvsc: Support hot-removing of scsi + devices + +commit b401731985bfa7c02e4e5673a151b29ae7180fce upstream. + +Support hot-removing of scsi devices. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 45 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 45 insertions(+) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 7c82d14..0245143 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -328,6 +328,27 @@ static void storvsc_bus_scan(struct work_struct *work) + kfree(wrk); + } + ++static void storvsc_remove_lun(struct work_struct *work) ++{ ++ struct storvsc_scan_work *wrk; ++ struct scsi_device *sdev; ++ ++ wrk = container_of(work, struct storvsc_scan_work, work); ++ if (!scsi_host_get(wrk->host)) ++ goto done; ++ ++ sdev = scsi_device_lookup(wrk->host, 0, 0, wrk->lun); ++ ++ if (sdev) { ++ scsi_remove_device(sdev); ++ scsi_device_put(sdev); ++ } ++ scsi_host_put(wrk->host); ++ ++done: ++ kfree(wrk); ++} ++ + static inline struct storvsc_device *get_out_stor_device( + struct hv_device *device) + { +@@ -1112,6 +1133,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + void (*scsi_done_fn)(struct scsi_cmnd *); + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; ++ struct storvsc_scan_work *wrk; + + vm_srb = &request->vstor_packet.vm_srb; + if (cmd_request->bounce_sgl_count) { +@@ -1134,6 +1156,29 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + else + scmnd->result = vm_srb->scsi_status; + ++ /* ++ * If the LUN is invalid; remove the device. ++ */ ++ if (vm_srb->srb_status == 0x20) { ++ struct storvsc_device *stor_dev; ++ struct hv_device *dev = host_dev->dev; ++ struct Scsi_Host *host; ++ ++ stor_dev = get_in_stor_device(dev); ++ host = stor_dev->host; ++ ++ wrk = kmalloc(sizeof(struct storvsc_scan_work), ++ GFP_ATOMIC); ++ if (!wrk) { ++ scmnd->result = DID_TARGET_FAILURE << 16; ++ } else { ++ wrk->host = host; ++ wrk->lun = vm_srb->lun; ++ INIT_WORK(&wrk->work, storvsc_remove_lun); ++ schedule_work(&wrk->work); ++ } ++ } ++ + if (scmnd->result) { + if (scsi_normalize_sense(scmnd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr)) +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0014-staging-hv-Use-kmemdup-rather-than-duplicating-its-i.patch b/debian/patches/features/x86/hyperv/0014-staging-hv-Use-kmemdup-rather-than-duplicating-its-i.patch new file mode 100644 index 000000000..4511f7e7b --- /dev/null +++ b/debian/patches/features/x86/hyperv/0014-staging-hv-Use-kmemdup-rather-than-duplicating-its-i.patch @@ -0,0 +1,68 @@ +From: Thomas Meyer +Date: Sat, 12 Nov 2011 13:21:49 +0100 +Subject: [PATCH 14/77] staging: hv: Use kmemdup rather than duplicating its + implementation + +commit 27b7923035f737a7e6384d0084e34706ea068040 upstream. + +Use kmemdup rather than duplicating its implementation + +The semantic patch that makes this change is available +in scripts/coccinelle/api/memdup.cocci. + +Signed-off-by: Thomas Meyer +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/hv_mouse.c | 4 +--- + drivers/staging/hv/netvsc.c | 11 +++-------- + 2 files changed, 4 insertions(+), 11 deletions(-) + +diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c +index ccd39c7..3689217 100644 +--- a/drivers/staging/hv/hv_mouse.c ++++ b/drivers/staging/hv/hv_mouse.c +@@ -205,13 +205,11 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, + desc = &device_info->hid_descriptor; + WARN_ON(desc->bLength == 0); + +- input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); ++ input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); + + if (!input_device->hid_desc) + goto cleanup; + +- memcpy(input_device->hid_desc, desc, desc->bLength); +- + input_device->report_desc_size = desc->desc[0].wDescriptorLength; + if (input_device->report_desc_size == 0) + goto cleanup; +diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c +index b902579..28e69a6 100644 +--- a/drivers/staging/hv/netvsc.c ++++ b/drivers/staging/hv/netvsc.c +@@ -230,19 +230,14 @@ static int netvsc_init_recv_buf(struct hv_device *device) + net_device->recv_section_cnt = init_packet->msg. + v1_msg.send_recv_buf_complete.num_sections; + +- net_device->recv_section = kmalloc(net_device->recv_section_cnt +- * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL); ++ net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections, ++ net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section), ++ GFP_KERNEL); + if (net_device->recv_section == NULL) { + ret = -EINVAL; + goto cleanup; + } + +- memcpy(net_device->recv_section, +- init_packet->msg.v1_msg. +- send_recv_buf_complete.sections, +- net_device->recv_section_cnt * +- sizeof(struct nvsp_1_receive_buffer_section)); +- + /* + * For 1st release, there should only be 1 section that represents the + * entire receive buffer +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0015-Staging-hv-vmbus-Support-building-the-vmbus-driver-a.patch b/debian/patches/features/x86/hyperv/0015-Staging-hv-vmbus-Support-building-the-vmbus-driver-a.patch new file mode 100644 index 000000000..74d0e5b6c --- /dev/null +++ b/debian/patches/features/x86/hyperv/0015-Staging-hv-vmbus-Support-building-the-vmbus-driver-a.patch @@ -0,0 +1,32 @@ +From: "K. Y. Srinivasan" +Date: Mon, 24 Oct 2011 11:28:12 -0700 +Subject: [PATCH 15/77] Staging: hv: vmbus: Support building the vmbus driver + as part of the kernel + +commit 43d4e119f944a41a7694552353635ad9b2cd8b37 upstream. + +Modify the way we initialize the vmbus driver so that all the hyper-v drivers +can be linked with the kernel and still ensure that the vmbus driver +is fully initialized before the drivers that depend upon the vmbus +driver attempt to initialize. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/vmbus_drv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index d2d0a2a..05168eb 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -796,4 +796,4 @@ cleanup: + MODULE_LICENSE("GPL"); + MODULE_VERSION(HV_DRV_VERSION); + +-module_init(hv_acpi_init); ++subsys_initcall(hv_acpi_init); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0016-hv-Add-Kconfig-menu-entry.patch b/debian/patches/features/x86/hyperv/0016-hv-Add-Kconfig-menu-entry.patch new file mode 100644 index 000000000..d2ed02e05 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0016-hv-Add-Kconfig-menu-entry.patch @@ -0,0 +1,38 @@ +From: Bart Van Assche +Date: Fri, 18 Nov 2011 18:40:52 +0100 +Subject: [PATCH 16/77] hv: Add Kconfig menu entry + +commit 124a6b4c4dae0317a0689a42ed0c68fabeb10c8d upstream. + +Most of the drivers/*/Kconfig files define a menu entry. Define +a menu item for hv too such that it becomes uniform with e.g. +virtio for at least "make xconfig" and "make menuconfig" users. + +Signed-off-by: Bart Van Assche +Cc: Greg Kroah-Hartman +Cc: James Bottomley +Cc: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/Kconfig | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig +index 9fa09ac..70f5dde 100644 +--- a/drivers/hv/Kconfig ++++ b/drivers/hv/Kconfig +@@ -1,3 +1,5 @@ ++menu "Microsoft Hyper-V guest support" ++ + config HYPERV + tristate "Microsoft Hyper-V client drivers" + depends on X86 && ACPI && PCI +@@ -11,4 +13,4 @@ config HYPERV_UTILS + help + Select this option to enable the Hyper-V Utilities. + +- ++endmenu +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0017-Staging-hv-mousevsc-Remove-the-mouse-driver-from-the.patch b/debian/patches/features/x86/hyperv/0017-Staging-hv-mousevsc-Remove-the-mouse-driver-from-the.patch new file mode 100644 index 000000000..7328df6e2 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0017-Staging-hv-mousevsc-Remove-the-mouse-driver-from-the.patch @@ -0,0 +1,656 @@ +From: "K. Y. Srinivasan" +Date: Fri, 25 Nov 2011 21:28:28 -0800 +Subject: [PATCH 17/77] Staging: hv: mousevsc: Remove the mouse driver from + the staging tree + +commit 045b1684bc21575793a099490938d197555eb128 upstream. + +The mouse driver for Hyper-V is a HID compliant mouse driver. +Now that all relevant review comments have been addressed, +Jiri has agreed to merge this mouse driver into 3.3 hid.git. +This patch takes care of removing the mouse driver from the +staging area. Greg, if you can ack this patch, Jiri will proceed +with his merge. + +Signed-off-by: K. Y. Srinivasan +Acked-by: Greg Kroah-Hartman +Signed-off-by: Jiri Kosina +[bwh: Backported to 3.2, dropping the intermediate changes] +--- + drivers/staging/hv/Kconfig | 6 - + drivers/staging/hv/Makefile | 1 - + drivers/staging/hv/hv_mouse.c | 597 ----------------------------------------- + 3 files changed, 604 deletions(-) + delete mode 100644 drivers/staging/hv/hv_mouse.c + +diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig +index 072185e..6c0dc30 100644 +--- a/drivers/staging/hv/Kconfig ++++ b/drivers/staging/hv/Kconfig +@@ -9,9 +9,3 @@ config HYPERV_NET + depends on HYPERV && NET + help + Select this option to enable the Hyper-V virtual network driver. +- +-config HYPERV_MOUSE +- tristate "Microsoft Hyper-V mouse driver" +- depends on HYPERV && HID +- help +- Select this option to enable the Hyper-V mouse driver. +diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile +index 0f55cee..fbe9a42 100644 +--- a/drivers/staging/hv/Makefile ++++ b/drivers/staging/hv/Makefile +@@ -1,6 +1,5 @@ + obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o + obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o +-obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o + + hv_storvsc-y := storvsc_drv.o + hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +diff --git a/drivers/staging/hv/hv_mouse.c b/drivers/staging/hv/hv_mouse.c +deleted file mode 100644 +index 3689217..0000000 +--- a/drivers/staging/hv/hv_mouse.c ++++ /dev/null +@@ -1,597 +0,0 @@ +-/* +- * Copyright (c) 2009, Citrix Systems, Inc. +- * Copyright (c) 2010, Microsoft Corporation. +- * Copyright (c) 2011, Novell Inc. +- * +- * 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. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +- +-struct hv_input_dev_info { +- unsigned int size; +- unsigned short vendor; +- unsigned short product; +- unsigned short version; +- unsigned short reserved[11]; +-}; +- +-/* The maximum size of a synthetic input message. */ +-#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 +- +-/* +- * Current version +- * +- * History: +- * Beta, RC < 2008/1/22 1,0 +- * RC > 2008/1/22 2,0 +- */ +-#define SYNTHHID_INPUT_VERSION_MAJOR 2 +-#define SYNTHHID_INPUT_VERSION_MINOR 0 +-#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ +- (SYNTHHID_INPUT_VERSION_MAJOR << 16)) +- +- +-#pragma pack(push, 1) +-/* +- * Message types in the synthetic input protocol +- */ +-enum synthhid_msg_type { +- SYNTH_HID_PROTOCOL_REQUEST, +- SYNTH_HID_PROTOCOL_RESPONSE, +- SYNTH_HID_INITIAL_DEVICE_INFO, +- SYNTH_HID_INITIAL_DEVICE_INFO_ACK, +- SYNTH_HID_INPUT_REPORT, +- SYNTH_HID_MAX +-}; +- +-/* +- * Basic message structures. +- */ +-struct synthhid_msg_hdr { +- enum synthhid_msg_type type; +- u32 size; +-}; +- +-struct synthhid_msg { +- struct synthhid_msg_hdr header; +- char data[1]; /* Enclosed message */ +-}; +- +-union synthhid_version { +- struct { +- u16 minor_version; +- u16 major_version; +- }; +- u32 version; +-}; +- +-/* +- * Protocol messages +- */ +-struct synthhid_protocol_request { +- struct synthhid_msg_hdr header; +- union synthhid_version version_requested; +-}; +- +-struct synthhid_protocol_response { +- struct synthhid_msg_hdr header; +- union synthhid_version version_requested; +- unsigned char approved; +-}; +- +-struct synthhid_device_info { +- struct synthhid_msg_hdr header; +- struct hv_input_dev_info hid_dev_info; +- struct hid_descriptor hid_descriptor; +-}; +- +-struct synthhid_device_info_ack { +- struct synthhid_msg_hdr header; +- unsigned char reserved; +-}; +- +-struct synthhid_input_report { +- struct synthhid_msg_hdr header; +- char buffer[1]; +-}; +- +-#pragma pack(pop) +- +-#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) +-#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) +- +-#define NBITS(x) (((x)/BITS_PER_LONG)+1) +- +-enum pipe_prot_msg_type { +- PIPE_MESSAGE_INVALID, +- PIPE_MESSAGE_DATA, +- PIPE_MESSAGE_MAXIMUM +-}; +- +- +-struct pipe_prt_msg { +- enum pipe_prot_msg_type type; +- u32 size; +- char data[1]; +-}; +- +-struct mousevsc_prt_msg { +- enum pipe_prot_msg_type type; +- u32 size; +- union { +- struct synthhid_protocol_request request; +- struct synthhid_protocol_response response; +- struct synthhid_device_info_ack ack; +- }; +-}; +- +-/* +- * Represents an mousevsc device +- */ +-struct mousevsc_dev { +- struct hv_device *device; +- unsigned char init_complete; +- struct mousevsc_prt_msg protocol_req; +- struct mousevsc_prt_msg protocol_resp; +- /* Synchronize the request/response if needed */ +- struct completion wait_event; +- int dev_info_status; +- +- struct hid_descriptor *hid_desc; +- unsigned char *report_desc; +- u32 report_desc_size; +- struct hv_input_dev_info hid_dev_info; +- int connected; +- struct hid_device *hid_device; +-}; +- +- +-static struct mousevsc_dev *alloc_input_device(struct hv_device *device) +-{ +- struct mousevsc_dev *input_dev; +- +- input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); +- +- if (!input_dev) +- return NULL; +- +- input_dev->device = device; +- hv_set_drvdata(device, input_dev); +- init_completion(&input_dev->wait_event); +- +- return input_dev; +-} +- +-static void free_input_device(struct mousevsc_dev *device) +-{ +- kfree(device->hid_desc); +- kfree(device->report_desc); +- hv_set_drvdata(device->device, NULL); +- kfree(device); +-} +- +- +-static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, +- struct synthhid_device_info *device_info) +-{ +- int ret = 0; +- struct hid_descriptor *desc; +- struct mousevsc_prt_msg ack; +- +- /* Assume success for now */ +- input_device->dev_info_status = 0; +- +- memcpy(&input_device->hid_dev_info, &device_info->hid_dev_info, +- sizeof(struct hv_input_dev_info)); +- +- desc = &device_info->hid_descriptor; +- WARN_ON(desc->bLength == 0); +- +- input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); +- +- if (!input_device->hid_desc) +- goto cleanup; +- +- input_device->report_desc_size = desc->desc[0].wDescriptorLength; +- if (input_device->report_desc_size == 0) +- goto cleanup; +- input_device->report_desc = kzalloc(input_device->report_desc_size, +- GFP_ATOMIC); +- +- if (!input_device->report_desc) +- goto cleanup; +- +- memcpy(input_device->report_desc, +- ((unsigned char *)desc) + desc->bLength, +- desc->desc[0].wDescriptorLength); +- +- /* Send the ack */ +- memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); +- +- ack.type = PIPE_MESSAGE_DATA; +- ack.size = sizeof(struct synthhid_device_info_ack); +- +- ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; +- ack.ack.header.size = 1; +- ack.ack.reserved = 0; +- +- ret = vmbus_sendpacket(input_device->device->channel, +- &ack, +- sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + +- sizeof(struct synthhid_device_info_ack), +- (unsigned long)&ack, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) +- goto cleanup; +- +- complete(&input_device->wait_event); +- +- return; +- +-cleanup: +- kfree(input_device->hid_desc); +- input_device->hid_desc = NULL; +- +- kfree(input_device->report_desc); +- input_device->report_desc = NULL; +- +- input_device->dev_info_status = -1; +- complete(&input_device->wait_event); +-} +- +-static void mousevsc_on_receive(struct hv_device *device, +- struct vmpacket_descriptor *packet) +-{ +- struct pipe_prt_msg *pipe_msg; +- struct synthhid_msg *hid_msg; +- struct mousevsc_dev *input_dev = hv_get_drvdata(device); +- struct synthhid_input_report *input_report; +- +- pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + +- (packet->offset8 << 3)); +- +- if (pipe_msg->type != PIPE_MESSAGE_DATA) +- return; +- +- hid_msg = (struct synthhid_msg *)&pipe_msg->data[0]; +- +- switch (hid_msg->header.type) { +- case SYNTH_HID_PROTOCOL_RESPONSE: +- memcpy(&input_dev->protocol_resp, pipe_msg, +- pipe_msg->size + sizeof(struct pipe_prt_msg) - +- sizeof(unsigned char)); +- complete(&input_dev->wait_event); +- break; +- +- case SYNTH_HID_INITIAL_DEVICE_INFO: +- WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); +- +- /* +- * Parse out the device info into device attr, +- * hid desc and report desc +- */ +- mousevsc_on_receive_device_info(input_dev, +- (struct synthhid_device_info *)&pipe_msg->data[0]); +- break; +- case SYNTH_HID_INPUT_REPORT: +- input_report = +- (struct synthhid_input_report *)&pipe_msg->data[0]; +- if (!input_dev->init_complete) +- break; +- hid_input_report(input_dev->hid_device, +- HID_INPUT_REPORT, input_report->buffer, +- input_report->header.size, 1); +- break; +- default: +- pr_err("unsupported hid msg type - type %d len %d", +- hid_msg->header.type, hid_msg->header.size); +- break; +- } +- +-} +- +-static void mousevsc_on_channel_callback(void *context) +-{ +- const int packetSize = 0x100; +- int ret = 0; +- struct hv_device *device = (struct hv_device *)context; +- +- u32 bytes_recvd; +- u64 req_id; +- unsigned char packet[0x100]; +- struct vmpacket_descriptor *desc; +- unsigned char *buffer = packet; +- int bufferlen = packetSize; +- +- +- do { +- ret = vmbus_recvpacket_raw(device->channel, buffer, +- bufferlen, &bytes_recvd, &req_id); +- +- if (ret == 0) { +- if (bytes_recvd > 0) { +- desc = (struct vmpacket_descriptor *)buffer; +- +- switch (desc->type) { +- case VM_PKT_COMP: +- break; +- +- case VM_PKT_DATA_INBAND: +- mousevsc_on_receive( +- device, desc); +- break; +- +- default: +- pr_err("unhandled packet type %d, tid %llx len %d\n", +- desc->type, +- req_id, +- bytes_recvd); +- break; +- } +- +- /* reset */ +- if (bufferlen > packetSize) { +- kfree(buffer); +- +- buffer = packet; +- bufferlen = packetSize; +- } +- } else { +- if (bufferlen > packetSize) { +- kfree(buffer); +- +- buffer = packet; +- bufferlen = packetSize; +- } +- break; +- } +- } else if (ret == -ENOBUFS) { +- /* Handle large packet */ +- bufferlen = bytes_recvd; +- buffer = kzalloc(bytes_recvd, GFP_ATOMIC); +- +- if (buffer == NULL) { +- buffer = packet; +- bufferlen = packetSize; +- break; +- } +- } +- } while (1); +- +- return; +-} +- +-static int mousevsc_connect_to_vsp(struct hv_device *device) +-{ +- int ret = 0; +- int t; +- struct mousevsc_dev *input_dev = hv_get_drvdata(device); +- struct mousevsc_prt_msg *request; +- struct mousevsc_prt_msg *response; +- +- +- request = &input_dev->protocol_req; +- +- memset(request, 0, sizeof(struct mousevsc_prt_msg)); +- +- request->type = PIPE_MESSAGE_DATA; +- request->size = sizeof(struct synthhid_protocol_request); +- +- request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; +- request->request.header.size = sizeof(unsigned int); +- request->request.version_requested.version = SYNTHHID_INPUT_VERSION; +- +- +- ret = vmbus_sendpacket(device->channel, request, +- sizeof(struct pipe_prt_msg) - +- sizeof(unsigned char) + +- sizeof(struct synthhid_protocol_request), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- response = &input_dev->protocol_resp; +- +- if (!response->response.approved) { +- pr_err("synthhid protocol request failed (version %d)", +- SYNTHHID_INPUT_VERSION); +- ret = -ENODEV; +- goto cleanup; +- } +- +- t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- /* +- * We should have gotten the device attr, hid desc and report +- * desc at this point +- */ +- if (input_dev->dev_info_status) +- ret = -ENOMEM; +- +-cleanup: +- +- return ret; +-} +- +-static int mousevsc_hid_open(struct hid_device *hid) +-{ +- return 0; +-} +- +-static void mousevsc_hid_close(struct hid_device *hid) +-{ +-} +- +-static struct hid_ll_driver mousevsc_ll_driver = { +- .open = mousevsc_hid_open, +- .close = mousevsc_hid_close, +-}; +- +-static struct hid_driver mousevsc_hid_driver; +- +-static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len) +-{ +- struct hid_device *hid_dev; +- struct mousevsc_dev *input_device = hv_get_drvdata(dev); +- +- hid_dev = hid_allocate_device(); +- if (IS_ERR(hid_dev)) +- return; +- +- hid_dev->ll_driver = &mousevsc_ll_driver; +- hid_dev->driver = &mousevsc_hid_driver; +- +- if (hid_parse_report(hid_dev, packet, len)) +- return; +- +- hid_dev->bus = BUS_VIRTUAL; +- hid_dev->vendor = input_device->hid_dev_info.vendor; +- hid_dev->product = input_device->hid_dev_info.product; +- hid_dev->version = input_device->hid_dev_info.version; +- +- sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); +- +- if (!hidinput_connect(hid_dev, 0)) { +- hid_dev->claimed |= HID_CLAIMED_INPUT; +- +- input_device->connected = 1; +- +- } +- +- input_device->hid_device = hid_dev; +-} +- +-static int mousevsc_on_device_add(struct hv_device *device) +-{ +- int ret = 0; +- struct mousevsc_dev *input_dev; +- +- input_dev = alloc_input_device(device); +- +- if (!input_dev) +- return -ENOMEM; +- +- input_dev->init_complete = false; +- +- ret = vmbus_open(device->channel, +- INPUTVSC_SEND_RING_BUFFER_SIZE, +- INPUTVSC_RECV_RING_BUFFER_SIZE, +- NULL, +- 0, +- mousevsc_on_channel_callback, +- device +- ); +- +- if (ret != 0) { +- free_input_device(input_dev); +- return ret; +- } +- +- +- ret = mousevsc_connect_to_vsp(device); +- +- if (ret != 0) { +- vmbus_close(device->channel); +- free_input_device(input_dev); +- return ret; +- } +- +- +- /* workaround SA-167 */ +- if (input_dev->report_desc[14] == 0x25) +- input_dev->report_desc[14] = 0x29; +- +- reportdesc_callback(device, input_dev->report_desc, +- input_dev->report_desc_size); +- +- input_dev->init_complete = true; +- +- return ret; +-} +- +-static int mousevsc_probe(struct hv_device *dev, +- const struct hv_vmbus_device_id *dev_id) +-{ +- +- return mousevsc_on_device_add(dev); +- +-} +- +-static int mousevsc_remove(struct hv_device *dev) +-{ +- struct mousevsc_dev *input_dev = hv_get_drvdata(dev); +- +- vmbus_close(dev->channel); +- +- if (input_dev->connected) { +- hidinput_disconnect(input_dev->hid_device); +- input_dev->connected = 0; +- hid_destroy_device(input_dev->hid_device); +- } +- +- free_input_device(input_dev); +- +- return 0; +-} +- +-static const struct hv_vmbus_device_id id_table[] = { +- /* Mouse guid */ +- { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, +- 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, +- { }, +-}; +- +-MODULE_DEVICE_TABLE(vmbus, id_table); +- +-static struct hv_driver mousevsc_drv = { +- .name = "mousevsc", +- .id_table = id_table, +- .probe = mousevsc_probe, +- .remove = mousevsc_remove, +-}; +- +-static int __init mousevsc_init(void) +-{ +- return vmbus_driver_register(&mousevsc_drv); +-} +- +-static void __exit mousevsc_exit(void) +-{ +- vmbus_driver_unregister(&mousevsc_drv); +-} +- +-MODULE_LICENSE("GPL"); +-MODULE_VERSION(HV_DRV_VERSION); +-module_init(mousevsc_init); +-module_exit(mousevsc_exit); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0018-staging-hv-move-hv_netvsc-out-of-staging-area.patch b/debian/patches/features/x86/hyperv/0018-staging-hv-move-hv_netvsc-out-of-staging-area.patch new file mode 100644 index 000000000..682da69f0 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0018-staging-hv-move-hv_netvsc-out-of-staging-area.patch @@ -0,0 +1,6789 @@ +From: Haiyang Zhang +Date: Mon, 28 Nov 2011 13:35:35 -0800 +Subject: [PATCH 18/77] staging: hv: move hv_netvsc out of staging area + +commit 95fa0405c5991726e06c08ffcd8ff872f7fb4f2d upstream. + +hv_netvsc has been reviewed on netdev mailing list on 6/09/2011. +All recommended changes have been made. We are requesting to move +it out of staging area. + +Signed-off-by: Haiyang Zhang +Signed-off-by: KY Srinivasan +Signed-off-by: Mike Sterling +Acked-by: Stephen Hemminger +Acked-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +[bwh: Adjust context to apply after commit + 045b1684bc21575793a099490938d197555eb128 ('Staging: hv: mousevsc: Remove the + mouse driver from the staging tree') which makes adjacent deletions.] +--- + drivers/net/Kconfig | 2 + + drivers/net/Makefile | 2 + + drivers/net/hyperv/Kconfig | 5 + + drivers/net/hyperv/Makefile | 3 + + drivers/net/hyperv/hyperv_net.h | 1058 +++++++++++++++++++++++++++++++++++++ + drivers/net/hyperv/netvsc.c | 939 ++++++++++++++++++++++++++++++++ + drivers/net/hyperv/netvsc_drv.c | 456 ++++++++++++++++ + drivers/net/hyperv/rndis_filter.c | 855 ++++++++++++++++++++++++++++++ + drivers/staging/hv/Kconfig | 6 - + drivers/staging/hv/Makefile | 2 - + drivers/staging/hv/TODO | 1 - + drivers/staging/hv/hyperv_net.h | 1058 ------------------------------------- + drivers/staging/hv/netvsc.c | 939 -------------------------------- + drivers/staging/hv/netvsc_drv.c | 456 ---------------- + drivers/staging/hv/rndis_filter.c | 855 ------------------------------ + 15 files changed, 3320 insertions(+), 3317 deletions(-) + create mode 100644 drivers/net/hyperv/Kconfig + create mode 100644 drivers/net/hyperv/Makefile + create mode 100644 drivers/net/hyperv/hyperv_net.h + create mode 100644 drivers/net/hyperv/netvsc.c + create mode 100644 drivers/net/hyperv/netvsc_drv.c + create mode 100644 drivers/net/hyperv/rndis_filter.c + delete mode 100644 drivers/staging/hv/hyperv_net.h + delete mode 100644 drivers/staging/hv/netvsc.c + delete mode 100644 drivers/staging/hv/netvsc_drv.c + delete mode 100644 drivers/staging/hv/rndis_filter.c + +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index 654a5e9..99aa7fa 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -338,4 +338,6 @@ config VMXNET3 + To compile this driver as a module, choose M here: the + module will be called vmxnet3. + ++source "drivers/net/hyperv/Kconfig" ++ + endif # NETDEVICES +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index fa877cd..a81192b 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -66,3 +66,5 @@ obj-$(CONFIG_USB_USBNET) += usb/ + obj-$(CONFIG_USB_ZD1201) += usb/ + obj-$(CONFIG_USB_IPHETH) += usb/ + obj-$(CONFIG_USB_CDC_PHONET) += usb/ ++ ++obj-$(CONFIG_HYPERV_NET) += hyperv/ +diff --git a/drivers/net/hyperv/Kconfig b/drivers/net/hyperv/Kconfig +new file mode 100644 +index 0000000..936968d +--- /dev/null ++++ b/drivers/net/hyperv/Kconfig +@@ -0,0 +1,5 @@ ++config HYPERV_NET ++ tristate "Microsoft Hyper-V virtual network driver" ++ depends on HYPERV ++ help ++ Select this option to enable the Hyper-V virtual network driver. +diff --git a/drivers/net/hyperv/Makefile b/drivers/net/hyperv/Makefile +new file mode 100644 +index 0000000..c8a6682 +--- /dev/null ++++ b/drivers/net/hyperv/Makefile +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o ++ ++hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +new file mode 100644 +index 0000000..ac1ec84 +--- /dev/null ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -0,0 +1,1058 @@ ++/* ++ * ++ * 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_NET_H ++#define _HYPERV_NET_H ++ ++#include ++#include ++ ++/* Fwd declaration */ ++struct hv_netvsc_packet; ++ ++/* Represent the xfer page packet which contains 1 or more netvsc packet */ ++struct xferpage_packet { ++ struct list_head list_ent; ++ ++ /* # of netvsc packets this xfer packet contains */ ++ u32 count; ++}; ++ ++/* The number of pages which are enough to cover jumbo frame buffer. */ ++#define NETVSC_PACKET_MAXPAGE 4 ++ ++/* ++ * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame ++ * within the RNDIS ++ */ ++struct hv_netvsc_packet { ++ /* Bookkeeping stuff */ ++ struct list_head list_ent; ++ ++ struct hv_device *device; ++ bool is_data_pkt; ++ ++ /* ++ * Valid only for receives when we break a xfer page packet ++ * into multiple netvsc packets ++ */ ++ struct xferpage_packet *xfer_page_pkt; ++ ++ union { ++ struct { ++ u64 recv_completion_tid; ++ void *recv_completion_ctx; ++ void (*recv_completion)(void *context); ++ } recv; ++ struct { ++ u64 send_completion_tid; ++ void *send_completion_ctx; ++ void (*send_completion)(void *context); ++ } send; ++ } completion; ++ ++ /* This points to the memory after page_buf */ ++ void *extension; ++ ++ u32 total_data_buflen; ++ /* Points to the send/receive buffer where the ethernet frame is */ ++ u32 page_buf_cnt; ++ struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; ++}; ++ ++struct netvsc_device_info { ++ unsigned char mac_adr[6]; ++ bool link_state; /* 0 - link up, 1 - link down */ ++ int ring_size; ++}; ++ ++/* Interface */ ++int netvsc_device_add(struct hv_device *device, void *additional_info); ++int netvsc_device_remove(struct hv_device *device); ++int netvsc_send(struct hv_device *device, ++ struct hv_netvsc_packet *packet); ++void netvsc_linkstatus_callback(struct hv_device *device_obj, ++ unsigned int status); ++int netvsc_recv_callback(struct hv_device *device_obj, ++ struct hv_netvsc_packet *packet); ++int rndis_filter_open(struct hv_device *dev); ++int rndis_filter_close(struct hv_device *dev); ++int rndis_filter_device_add(struct hv_device *dev, ++ void *additional_info); ++void rndis_filter_device_remove(struct hv_device *dev); ++int rndis_filter_receive(struct hv_device *dev, ++ struct hv_netvsc_packet *pkt); ++ ++ ++ ++int rndis_filter_send(struct hv_device *dev, ++ struct hv_netvsc_packet *pkt); ++ ++#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) ++ ++#define NVSP_PROTOCOL_VERSION_1 2 ++#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 ++#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 ++ ++enum { ++ NVSP_MSG_TYPE_NONE = 0, ++ ++ /* Init Messages */ ++ NVSP_MSG_TYPE_INIT = 1, ++ NVSP_MSG_TYPE_INIT_COMPLETE = 2, ++ ++ NVSP_VERSION_MSG_START = 100, ++ ++ /* Version 1 Messages */ ++ NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, ++ ++ NVSP_MSG1_TYPE_SEND_RECV_BUF, ++ NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, ++ NVSP_MSG1_TYPE_REVOKE_RECV_BUF, ++ ++ NVSP_MSG1_TYPE_SEND_SEND_BUF, ++ NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, ++ NVSP_MSG1_TYPE_REVOKE_SEND_BUF, ++ ++ NVSP_MSG1_TYPE_SEND_RNDIS_PKT, ++ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, ++ ++ /* ++ * This should be set to the number of messages for the version with ++ * the maximum number of messages. ++ */ ++ NVSP_NUM_MSG_PER_VERSION = 9, ++}; ++ ++enum { ++ NVSP_STAT_NONE = 0, ++ NVSP_STAT_SUCCESS, ++ NVSP_STAT_FAIL, ++ NVSP_STAT_PROTOCOL_TOO_NEW, ++ NVSP_STAT_PROTOCOL_TOO_OLD, ++ NVSP_STAT_INVALID_RNDIS_PKT, ++ NVSP_STAT_BUSY, ++ NVSP_STAT_MAX, ++}; ++ ++struct nvsp_message_header { ++ u32 msg_type; ++}; ++ ++/* Init Messages */ ++ ++/* ++ * This message is used by the VSC to initialize the channel after the channels ++ * has been opened. This message should never include anything other then ++ * versioning (i.e. this message will be the same for ever). ++ */ ++struct nvsp_message_init { ++ u32 min_protocol_ver; ++ u32 max_protocol_ver; ++} __packed; ++ ++/* ++ * This message is used by the VSP to complete the initialization of the ++ * channel. This message should never include anything other then versioning ++ * (i.e. this message will be the same for ever). ++ */ ++struct nvsp_message_init_complete { ++ u32 negotiated_protocol_ver; ++ u32 max_mdl_chain_len; ++ u32 status; ++} __packed; ++ ++union nvsp_message_init_uber { ++ struct nvsp_message_init init; ++ struct nvsp_message_init_complete init_complete; ++} __packed; ++ ++/* Version 1 Messages */ ++ ++/* ++ * This message is used by the VSC to send the NDIS version to the VSP. The VSP ++ * can use this information when handling OIDs sent by the VSC. ++ */ ++struct nvsp_1_message_send_ndis_version { ++ u32 ndis_major_ver; ++ u32 ndis_minor_ver; ++} __packed; ++ ++/* ++ * This message is used by the VSC to send a receive buffer to the VSP. The VSP ++ * can then use the receive buffer to send data to the VSC. ++ */ ++struct nvsp_1_message_send_receive_buffer { ++ u32 gpadl_handle; ++ u16 id; ++} __packed; ++ ++struct nvsp_1_receive_buffer_section { ++ u32 offset; ++ u32 sub_alloc_size; ++ u32 num_sub_allocs; ++ u32 end_offset; ++} __packed; ++ ++/* ++ * This message is used by the VSP to acknowledge a receive buffer send by the ++ * VSC. This message must be sent by the VSP before the VSP uses the receive ++ * buffer. ++ */ ++struct nvsp_1_message_send_receive_buffer_complete { ++ u32 status; ++ u32 num_sections; ++ ++ /* ++ * The receive buffer is split into two parts, a large suballocation ++ * section and a small suballocation section. These sections are then ++ * suballocated by a certain size. ++ */ ++ ++ /* ++ * For example, the following break up of the receive buffer has 6 ++ * large suballocations and 10 small suballocations. ++ */ ++ ++ /* ++ * | Large Section | | Small Section | ++ * ------------------------------------------------------------ ++ * | | | | | | | | | | | | | | | | | | ++ * | | ++ * LargeOffset SmallOffset ++ */ ++ ++ struct nvsp_1_receive_buffer_section sections[1]; ++} __packed; ++ ++/* ++ * This message is sent by the VSC to revoke the receive buffer. After the VSP ++ * completes this transaction, the vsp should never use the receive buffer ++ * again. ++ */ ++struct nvsp_1_message_revoke_receive_buffer { ++ u16 id; ++}; ++ ++/* ++ * This message is used by the VSC to send a send buffer to the VSP. The VSC ++ * can then use the send buffer to send data to the VSP. ++ */ ++struct nvsp_1_message_send_send_buffer { ++ u32 gpadl_handle; ++ u16 id; ++} __packed; ++ ++/* ++ * This message is used by the VSP to acknowledge a send buffer sent by the ++ * VSC. This message must be sent by the VSP before the VSP uses the sent ++ * buffer. ++ */ ++struct nvsp_1_message_send_send_buffer_complete { ++ u32 status; ++ ++ /* ++ * The VSC gets to choose the size of the send buffer and the VSP gets ++ * to choose the sections size of the buffer. This was done to enable ++ * dynamic reconfigurations when the cost of GPA-direct buffers ++ * decreases. ++ */ ++ u32 section_size; ++} __packed; ++ ++/* ++ * This message is sent by the VSC to revoke the send buffer. After the VSP ++ * completes this transaction, the vsp should never use the send buffer again. ++ */ ++struct nvsp_1_message_revoke_send_buffer { ++ u16 id; ++}; ++ ++/* ++ * This message is used by both the VSP and the VSC to send a RNDIS message to ++ * the opposite channel endpoint. ++ */ ++struct nvsp_1_message_send_rndis_packet { ++ /* ++ * This field is specified by RNIDS. They assume there's two different ++ * channels of communication. However, the Network VSP only has one. ++ * Therefore, the channel travels with the RNDIS packet. ++ */ ++ u32 channel_type; ++ ++ /* ++ * This field is used to send part or all of the data through a send ++ * buffer. This values specifies an index into the send buffer. If the ++ * index is 0xFFFFFFFF, then the send buffer is not being used and all ++ * of the data was sent through other VMBus mechanisms. ++ */ ++ u32 send_buf_section_index; ++ u32 send_buf_section_size; ++} __packed; ++ ++/* ++ * This message is used by both the VSP and the VSC to complete a RNDIS message ++ * to the opposite channel endpoint. At this point, the initiator of this ++ * message cannot use any resources associated with the original RNDIS packet. ++ */ ++struct nvsp_1_message_send_rndis_packet_complete { ++ u32 status; ++}; ++ ++union nvsp_1_message_uber { ++ struct nvsp_1_message_send_ndis_version send_ndis_ver; ++ ++ struct nvsp_1_message_send_receive_buffer send_recv_buf; ++ struct nvsp_1_message_send_receive_buffer_complete ++ send_recv_buf_complete; ++ struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; ++ ++ struct nvsp_1_message_send_send_buffer send_send_buf; ++ struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; ++ struct nvsp_1_message_revoke_send_buffer revoke_send_buf; ++ ++ struct nvsp_1_message_send_rndis_packet send_rndis_pkt; ++ struct nvsp_1_message_send_rndis_packet_complete ++ send_rndis_pkt_complete; ++} __packed; ++ ++union nvsp_all_messages { ++ union nvsp_message_init_uber init_msg; ++ union nvsp_1_message_uber v1_msg; ++} __packed; ++ ++/* ALL Messages */ ++struct nvsp_message { ++ struct nvsp_message_header hdr; ++ union nvsp_all_messages msg; ++} __packed; ++ ++ ++ ++ ++/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ ++/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ ++ ++#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ ++ ++#define NETVSC_RECEIVE_BUFFER_ID 0xcafe ++ ++#define NETVSC_RECEIVE_SG_COUNT 1 ++ ++/* Preallocated receive packets */ ++#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 ++ ++#define NETVSC_PACKET_SIZE 2048 ++ ++/* Per netvsc channel-specific */ ++struct netvsc_device { ++ struct hv_device *dev; ++ ++ atomic_t num_outstanding_sends; ++ bool destroy; ++ /* ++ * List of free preallocated hv_netvsc_packet to represent receive ++ * packet ++ */ ++ struct list_head recv_pkt_list; ++ spinlock_t recv_pkt_list_lock; ++ ++ /* Receive buffer allocated by us but manages by NetVSP */ ++ void *recv_buf; ++ u32 recv_buf_size; ++ u32 recv_buf_gpadl_handle; ++ u32 recv_section_cnt; ++ struct nvsp_1_receive_buffer_section *recv_section; ++ ++ /* Used for NetVSP initialization protocol */ ++ struct completion channel_init_wait; ++ struct nvsp_message channel_init_pkt; ++ ++ struct nvsp_message revoke_packet; ++ /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ ++ ++ struct net_device *ndev; ++ ++ /* Holds rndis device info */ ++ void *extension; ++}; ++ ++ ++/* Status codes */ ++ ++ ++#ifndef STATUS_SUCCESS ++#define STATUS_SUCCESS (0x00000000L) ++#endif ++ ++#ifndef STATUS_UNSUCCESSFUL ++#define STATUS_UNSUCCESSFUL (0xC0000001L) ++#endif ++ ++#ifndef STATUS_PENDING ++#define STATUS_PENDING (0x00000103L) ++#endif ++ ++#ifndef STATUS_INSUFFICIENT_RESOURCES ++#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL) ++#endif ++ ++#ifndef STATUS_BUFFER_OVERFLOW ++#define STATUS_BUFFER_OVERFLOW (0x80000005L) ++#endif ++ ++#ifndef STATUS_NOT_SUPPORTED ++#define STATUS_NOT_SUPPORTED (0xC00000BBL) ++#endif ++ ++#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS) ++#define RNDIS_STATUS_PENDING (STATUS_PENDING) ++#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L) ++#define RNDIS_STATUS_NOT_COPIED (0x00010002L) ++#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L) ++#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L) ++ ++#define RNDIS_STATUS_ONLINE (0x40010003L) ++#define RNDIS_STATUS_RESET_START (0x40010004L) ++#define RNDIS_STATUS_RESET_END (0x40010005L) ++#define RNDIS_STATUS_RING_STATUS (0x40010006L) ++#define RNDIS_STATUS_CLOSED (0x40010007L) ++#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L) ++#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L) ++#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL) ++#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL) ++#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL) ++#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL) ++#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL) ++#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL) ++#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L) ++#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L) ++#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L) ++#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION ++#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L) ++ ++#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L) ++#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L) ++#define RNDIS_STATUS_HARD_ERRORS (0x80010004L) ++#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW) ++ ++#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL) ++#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES) ++#define RNDIS_STATUS_CLOSING (0xC0010002L) ++#define RNDIS_STATUS_BAD_VERSION (0xC0010004L) ++#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L) ++#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L) ++#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L) ++#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L) ++#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L) ++#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL) ++#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL) ++#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL) ++#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL) ++#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL) ++#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED) ++#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL) ++#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L) ++#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L) ++#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L) ++#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L) ++#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L) ++#define RNDIS_STATUS_INVALID_DATA (0xC0010015L) ++#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L) ++#define RNDIS_STATUS_INVALID_OID (0xC0010017L) ++#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L) ++#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L) ++#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL) ++#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL) ++#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL) ++#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL) ++#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL) ++#define RNDIS_STATUS_NO_CABLE (0xC001001FL) ++ ++#define RNDIS_STATUS_INVALID_SAP (0xC0010020L) ++#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L) ++#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L) ++#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L) ++#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L) ++#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L) ++#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L) ++#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L) ++#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L) ++#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L) ++ ++#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L) ++ ++/* Object Identifiers used by NdisRequest Query/Set Information */ ++/* General Objects */ ++#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 ++#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 ++#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 ++#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 ++#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 ++#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 ++#define RNDIS_OID_GEN_LINK_SPEED 0x00010107 ++#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 ++#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 ++#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A ++#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B ++#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C ++#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D ++#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E ++#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F ++#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 ++#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 ++#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 ++#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 ++#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 ++#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 ++#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 ++#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 ++#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 ++#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A ++#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B ++ ++#define RNDIS_OID_GEN_XMIT_OK 0x00020101 ++#define RNDIS_OID_GEN_RCV_OK 0x00020102 ++#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 ++#define RNDIS_OID_GEN_RCV_ERROR 0x00020104 ++#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 ++ ++#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 ++#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 ++#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 ++#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 ++#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 ++#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 ++#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 ++#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 ++#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 ++#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A ++#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B ++#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C ++ ++#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D ++#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E ++ ++#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F ++#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 ++ ++/* These are connection-oriented general OIDs. */ ++/* These replace the above OIDs for connection-oriented media. */ ++#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 ++#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 ++#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 ++#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 ++#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 ++#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 ++#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 ++#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 ++#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 ++#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A ++#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B ++#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C ++#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D ++ ++#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 ++#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 ++ ++/* These are connection-oriented statistics OIDs. */ ++#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 ++#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 ++#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 ++#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 ++#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 ++ ++ ++#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 ++#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 ++#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 ++#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 ++#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 ++#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 ++ ++/* These are objects for Connection-oriented media call-managers. */ ++#define RNDIS_OID_CO_ADD_PVC 0xFF000001 ++#define RNDIS_OID_CO_DELETE_PVC 0xFF000002 ++#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 ++#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 ++#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 ++#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 ++#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 ++#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 ++#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 ++ ++/* 802.3 Objects (Ethernet) */ ++#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 ++#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 ++#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 ++#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 ++#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 ++ ++#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 ++ ++#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 ++#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 ++#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 ++ ++#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 ++#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 ++#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 ++#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 ++#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 ++#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 ++#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 ++ ++/* Remote NDIS message types */ ++#define REMOTE_NDIS_PACKET_MSG 0x00000001 ++#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002 ++#define REMOTE_NDIS_HALT_MSG 0x00000003 ++#define REMOTE_NDIS_QUERY_MSG 0x00000004 ++#define REMOTE_NDIS_SET_MSG 0x00000005 ++#define REMOTE_NDIS_RESET_MSG 0x00000006 ++#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007 ++#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008 ++ ++#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001 ++#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002 ++#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005 ++#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 ++#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007 ++ ++/* Remote NDIS message completion types */ ++#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002 ++#define REMOTE_NDIS_QUERY_CMPLT 0x80000004 ++#define REMOTE_NDIS_SET_CMPLT 0x80000005 ++#define REMOTE_NDIS_RESET_CMPLT 0x80000006 ++#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008 ++ ++#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001 ++#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002 ++#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 ++#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 ++ ++/* ++ * Reserved message type for private communication between lower-layer host ++ * driver and remote device, if necessary. ++ */ ++#define REMOTE_NDIS_BUS_MSG 0xff000001 ++ ++/* Defines for DeviceFlags in struct rndis_initialize_complete */ ++#define RNDIS_DF_CONNECTIONLESS 0x00000001 ++#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 ++#define RNDIS_DF_RAW_DATA 0x00000004 ++ ++/* Remote NDIS medium types. */ ++#define RNDIS_MEDIUM_802_3 0x00000000 ++#define RNDIS_MEDIUM_802_5 0x00000001 ++#define RNDIS_MEDIUM_FDDI 0x00000002 ++#define RNDIS_MEDIUM_WAN 0x00000003 ++#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 ++#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 ++#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 ++#define RNDIS_MEDIUM_ATM 0x00000008 ++#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 ++#define RNDIS_MEDIUM_IRDA 0x0000000a ++#define RNDIS_MEDIUM_CO_WAN 0x0000000b ++/* Not a real medium, defined as an upper-bound */ ++#define RNDIS_MEDIUM_MAX 0x0000000d ++ ++ ++/* Remote NDIS medium connection states. */ ++#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 ++#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 ++ ++/* Remote NDIS version numbers */ ++#define RNDIS_MAJOR_VERSION 0x00000001 ++#define RNDIS_MINOR_VERSION 0x00000000 ++ ++ ++/* NdisInitialize message */ ++struct rndis_initialize_request { ++ u32 req_id; ++ u32 major_ver; ++ u32 minor_ver; ++ u32 max_xfer_size; ++}; ++ ++/* Response to NdisInitialize */ ++struct rndis_initialize_complete { ++ u32 req_id; ++ u32 status; ++ u32 major_ver; ++ u32 minor_ver; ++ u32 dev_flags; ++ u32 medium; ++ u32 max_pkt_per_msg; ++ u32 max_xfer_size; ++ u32 pkt_alignment_factor; ++ u32 af_list_offset; ++ u32 af_list_size; ++}; ++ ++/* Call manager devices only: Information about an address family */ ++/* supported by the device is appended to the response to NdisInitialize. */ ++struct rndis_co_address_family { ++ u32 address_family; ++ u32 major_ver; ++ u32 minor_ver; ++}; ++ ++/* NdisHalt message */ ++struct rndis_halt_request { ++ u32 req_id; ++}; ++ ++/* NdisQueryRequest message */ ++struct rndis_query_request { ++ u32 req_id; ++ u32 oid; ++ u32 info_buflen; ++ u32 info_buf_offset; ++ u32 dev_vc_handle; ++}; ++ ++/* Response to NdisQueryRequest */ ++struct rndis_query_complete { ++ u32 req_id; ++ u32 status; ++ u32 info_buflen; ++ u32 info_buf_offset; ++}; ++ ++/* NdisSetRequest message */ ++struct rndis_set_request { ++ u32 req_id; ++ u32 oid; ++ u32 info_buflen; ++ u32 info_buf_offset; ++ u32 dev_vc_handle; ++}; ++ ++/* Response to NdisSetRequest */ ++struct rndis_set_complete { ++ u32 req_id; ++ u32 status; ++}; ++ ++/* NdisReset message */ ++struct rndis_reset_request { ++ u32 reserved; ++}; ++ ++/* Response to NdisReset */ ++struct rndis_reset_complete { ++ u32 status; ++ u32 addressing_reset; ++}; ++ ++/* NdisMIndicateStatus message */ ++struct rndis_indicate_status { ++ u32 status; ++ u32 status_buflen; ++ u32 status_buf_offset; ++}; ++ ++/* Diagnostic information passed as the status buffer in */ ++/* struct rndis_indicate_status messages signifying error conditions. */ ++struct rndis_diagnostic_info { ++ u32 diag_status; ++ u32 error_offset; ++}; ++ ++/* NdisKeepAlive message */ ++struct rndis_keepalive_request { ++ u32 req_id; ++}; ++ ++/* Response to NdisKeepAlive */ ++struct rndis_keepalive_complete { ++ u32 req_id; ++ u32 status; ++}; ++ ++/* ++ * Data message. All Offset fields contain byte offsets from the beginning of ++ * struct rndis_packet. All Length fields are in bytes. VcHandle is set ++ * to 0 for connectionless data, otherwise it contains the VC handle. ++ */ ++struct rndis_packet { ++ u32 data_offset; ++ u32 data_len; ++ u32 oob_data_offset; ++ u32 oob_data_len; ++ u32 num_oob_data_elements; ++ u32 per_pkt_info_offset; ++ u32 per_pkt_info_len; ++ u32 vc_handle; ++ u32 reserved; ++}; ++ ++/* Optional Out of Band data associated with a Data message. */ ++struct rndis_oobd { ++ u32 size; ++ u32 type; ++ u32 class_info_offset; ++}; ++ ++/* Packet extension field contents associated with a Data message. */ ++struct rndis_per_packet_info { ++ u32 size; ++ u32 type; ++ u32 per_pkt_info_offset; ++}; ++ ++/* Format of Information buffer passed in a SetRequest for the OID */ ++/* OID_GEN_RNDIS_CONFIG_PARAMETER. */ ++struct rndis_config_parameter_info { ++ u32 parameter_name_offset; ++ u32 parameter_name_length; ++ u32 parameter_type; ++ u32 parameter_value_offset; ++ u32 parameter_value_length; ++}; ++ ++/* Values for ParameterType in struct rndis_config_parameter_info */ ++#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0 ++#define RNDIS_CONFIG_PARAM_TYPE_STRING 2 ++ ++/* CONDIS Miniport messages for connection oriented devices */ ++/* that do not implement a call manager. */ ++ ++/* CoNdisMiniportCreateVc message */ ++struct rcondis_mp_create_vc { ++ u32 req_id; ++ u32 ndis_vc_handle; ++}; ++ ++/* Response to CoNdisMiniportCreateVc */ ++struct rcondis_mp_create_vc_complete { ++ u32 req_id; ++ u32 dev_vc_handle; ++ u32 status; ++}; ++ ++/* CoNdisMiniportDeleteVc message */ ++struct rcondis_mp_delete_vc { ++ u32 req_id; ++ u32 dev_vc_handle; ++}; ++ ++/* Response to CoNdisMiniportDeleteVc */ ++struct rcondis_mp_delete_vc_complete { ++ u32 req_id; ++ u32 status; ++}; ++ ++/* CoNdisMiniportQueryRequest message */ ++struct rcondis_mp_query_request { ++ u32 req_id; ++ u32 request_type; ++ u32 oid; ++ u32 dev_vc_handle; ++ u32 info_buflen; ++ u32 info_buf_offset; ++}; ++ ++/* CoNdisMiniportSetRequest message */ ++struct rcondis_mp_set_request { ++ u32 req_id; ++ u32 request_type; ++ u32 oid; ++ u32 dev_vc_handle; ++ u32 info_buflen; ++ u32 info_buf_offset; ++}; ++ ++/* CoNdisIndicateStatus message */ ++struct rcondis_indicate_status { ++ u32 ndis_vc_handle; ++ u32 status; ++ u32 status_buflen; ++ u32 status_buf_offset; ++}; ++ ++/* CONDIS Call/VC parameters */ ++struct rcondis_specific_parameters { ++ u32 parameter_type; ++ u32 parameter_length; ++ u32 parameter_lffset; ++}; ++ ++struct rcondis_media_parameters { ++ u32 flags; ++ u32 reserved1; ++ u32 reserved2; ++ struct rcondis_specific_parameters media_specific; ++}; ++ ++struct rndis_flowspec { ++ u32 token_rate; ++ u32 token_bucket_size; ++ u32 peak_bandwidth; ++ u32 latency; ++ u32 delay_variation; ++ u32 service_type; ++ u32 max_sdu_size; ++ u32 minimum_policed_size; ++}; ++ ++struct rcondis_call_manager_parameters { ++ struct rndis_flowspec transmit; ++ struct rndis_flowspec receive; ++ struct rcondis_specific_parameters call_mgr_specific; ++}; ++ ++/* CoNdisMiniportActivateVc message */ ++struct rcondis_mp_activate_vc_request { ++ u32 req_id; ++ u32 flags; ++ u32 dev_vc_handle; ++ u32 media_params_offset; ++ u32 media_params_length; ++ u32 call_mgr_params_offset; ++ u32 call_mgr_params_length; ++}; ++ ++/* Response to CoNdisMiniportActivateVc */ ++struct rcondis_mp_activate_vc_complete { ++ u32 req_id; ++ u32 status; ++}; ++ ++/* CoNdisMiniportDeactivateVc message */ ++struct rcondis_mp_deactivate_vc_request { ++ u32 req_id; ++ u32 flags; ++ u32 dev_vc_handle; ++}; ++ ++/* Response to CoNdisMiniportDeactivateVc */ ++struct rcondis_mp_deactivate_vc_complete { ++ u32 req_id; ++ u32 status; ++}; ++ ++ ++/* union with all of the RNDIS messages */ ++union rndis_message_container { ++ struct rndis_packet pkt; ++ struct rndis_initialize_request init_req; ++ struct rndis_halt_request halt_req; ++ struct rndis_query_request query_req; ++ struct rndis_set_request set_req; ++ struct rndis_reset_request reset_req; ++ struct rndis_keepalive_request keep_alive_req; ++ struct rndis_indicate_status indicate_status; ++ struct rndis_initialize_complete init_complete; ++ struct rndis_query_complete query_complete; ++ struct rndis_set_complete set_complete; ++ struct rndis_reset_complete reset_complete; ++ struct rndis_keepalive_complete keep_alive_complete; ++ struct rcondis_mp_create_vc co_miniport_create_vc; ++ struct rcondis_mp_delete_vc co_miniport_delete_vc; ++ struct rcondis_indicate_status co_indicate_status; ++ struct rcondis_mp_activate_vc_request co_miniport_activate_vc; ++ struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc; ++ struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete; ++ struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete; ++ struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete; ++ struct rcondis_mp_deactivate_vc_complete ++ co_miniport_deactivate_vc_complete; ++}; ++ ++/* Remote NDIS message format */ ++struct rndis_message { ++ u32 ndis_msg_type; ++ ++ /* Total length of this message, from the beginning */ ++ /* of the sruct rndis_message, in bytes. */ ++ u32 msg_len; ++ ++ /* Actual message */ ++ union rndis_message_container msg; ++}; ++ ++ ++struct rndis_filter_packet { ++ void *completion_ctx; ++ void (*completion)(void *context); ++ struct rndis_message msg; ++}; ++ ++/* Handy macros */ ++ ++/* get the size of an RNDIS message. Pass in the message type, */ ++/* struct rndis_set_request, struct rndis_packet for example */ ++#define RNDIS_MESSAGE_SIZE(msg) \ ++ (sizeof(msg) + (sizeof(struct rndis_message) - \ ++ sizeof(union rndis_message_container))) ++ ++/* get pointer to info buffer with message pointer */ ++#define MESSAGE_TO_INFO_BUFFER(msg) \ ++ (((unsigned char *)(msg)) + msg->info_buf_offset) ++ ++/* get pointer to status buffer with message pointer */ ++#define MESSAGE_TO_STATUS_BUFFER(msg) \ ++ (((unsigned char *)(msg)) + msg->status_buf_offset) ++ ++/* get pointer to OOBD buffer with message pointer */ ++#define MESSAGE_TO_OOBD_BUFFER(msg) \ ++ (((unsigned char *)(msg)) + msg->oob_data_offset) ++ ++/* get pointer to data buffer with message pointer */ ++#define MESSAGE_TO_DATA_BUFFER(msg) \ ++ (((unsigned char *)(msg)) + msg->per_pkt_info_offset) ++ ++/* get pointer to contained message from NDIS_MESSAGE pointer */ ++#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \ ++ ((void *) &rndis_msg->msg) ++ ++/* get pointer to contained message from NDIS_MESSAGE pointer */ ++#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \ ++ ((void *) rndis_msg) ++ ++ ++#define __struct_bcount(x) ++ ++ ++ ++#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ ++ sizeof(union rndis_message_container)) ++ ++#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 ++#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 ++#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 ++#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 ++#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 ++#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 ++#define NDIS_PACKET_TYPE_SMT 0x00000040 ++#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 ++#define NDIS_PACKET_TYPE_GROUP 0x00000100 ++#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 ++#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 ++#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 ++ ++ ++ ++#endif /* _HYPERV_NET_H */ +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +new file mode 100644 +index 0000000..28e69a6 +--- /dev/null ++++ b/drivers/net/hyperv/netvsc.c +@@ -0,0 +1,939 @@ ++/* ++ * 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_net.h" ++ ++ ++static struct netvsc_device *alloc_net_device(struct hv_device *device) ++{ ++ struct netvsc_device *net_device; ++ struct net_device *ndev = hv_get_drvdata(device); ++ ++ net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); ++ if (!net_device) ++ return NULL; ++ ++ ++ net_device->destroy = false; ++ net_device->dev = device; ++ net_device->ndev = ndev; ++ ++ hv_set_drvdata(device, net_device); ++ return net_device; ++} ++ ++static struct netvsc_device *get_outbound_net_device(struct hv_device *device) ++{ ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(device); ++ if (net_device && net_device->destroy) ++ net_device = NULL; ++ ++ return net_device; ++} ++ ++static struct netvsc_device *get_inbound_net_device(struct hv_device *device) ++{ ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(device); ++ ++ if (!net_device) ++ goto get_in_err; ++ ++ if (net_device->destroy && ++ atomic_read(&net_device->num_outstanding_sends) == 0) ++ net_device = NULL; ++ ++get_in_err: ++ return net_device; ++} ++ ++ ++static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) ++{ ++ struct nvsp_message *revoke_packet; ++ int ret = 0; ++ struct net_device *ndev = net_device->ndev; ++ ++ /* ++ * If we got a section count, it means we received a ++ * SendReceiveBufferComplete msg (ie sent ++ * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need ++ * to send a revoke msg here ++ */ ++ if (net_device->recv_section_cnt) { ++ /* Send the revoke receive buffer */ ++ revoke_packet = &net_device->revoke_packet; ++ memset(revoke_packet, 0, sizeof(struct nvsp_message)); ++ ++ revoke_packet->hdr.msg_type = ++ NVSP_MSG1_TYPE_REVOKE_RECV_BUF; ++ revoke_packet->msg.v1_msg. ++ revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; ++ ++ ret = vmbus_sendpacket(net_device->dev->channel, ++ revoke_packet, ++ sizeof(struct nvsp_message), ++ (unsigned long)revoke_packet, ++ VM_PKT_DATA_INBAND, 0); ++ /* ++ * If we failed here, we might as well return and ++ * have a leak rather than continue and a bugchk ++ */ ++ if (ret != 0) { ++ netdev_err(ndev, "unable to send " ++ "revoke receive buffer to netvsp\n"); ++ return ret; ++ } ++ } ++ ++ /* Teardown the gpadl on the vsp end */ ++ if (net_device->recv_buf_gpadl_handle) { ++ ret = vmbus_teardown_gpadl(net_device->dev->channel, ++ net_device->recv_buf_gpadl_handle); ++ ++ /* If we failed here, we might as well return and have a leak ++ * rather than continue and a bugchk ++ */ ++ if (ret != 0) { ++ netdev_err(ndev, ++ "unable to teardown receive buffer's gpadl\n"); ++ return ret; ++ } ++ net_device->recv_buf_gpadl_handle = 0; ++ } ++ ++ if (net_device->recv_buf) { ++ /* Free up the receive buffer */ ++ free_pages((unsigned long)net_device->recv_buf, ++ get_order(net_device->recv_buf_size)); ++ net_device->recv_buf = NULL; ++ } ++ ++ if (net_device->recv_section) { ++ net_device->recv_section_cnt = 0; ++ kfree(net_device->recv_section); ++ net_device->recv_section = NULL; ++ } ++ ++ return ret; ++} ++ ++static int netvsc_init_recv_buf(struct hv_device *device) ++{ ++ int ret = 0; ++ int t; ++ struct netvsc_device *net_device; ++ struct nvsp_message *init_packet; ++ struct net_device *ndev; ++ ++ net_device = get_outbound_net_device(device); ++ if (!net_device) ++ return -ENODEV; ++ ndev = net_device->ndev; ++ ++ net_device->recv_buf = ++ (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, ++ get_order(net_device->recv_buf_size)); ++ if (!net_device->recv_buf) { ++ netdev_err(ndev, "unable to allocate receive " ++ "buffer of size %d\n", net_device->recv_buf_size); ++ ret = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* ++ * Establish the gpadl handle for this buffer on this ++ * channel. Note: This call uses the vmbus connection rather ++ * than the channel to establish the gpadl handle. ++ */ ++ ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf, ++ net_device->recv_buf_size, ++ &net_device->recv_buf_gpadl_handle); ++ if (ret != 0) { ++ netdev_err(ndev, ++ "unable to establish receive buffer's gpadl\n"); ++ goto cleanup; ++ } ++ ++ ++ /* Notify the NetVsp of the gpadl handle */ ++ init_packet = &net_device->channel_init_pkt; ++ ++ memset(init_packet, 0, sizeof(struct nvsp_message)); ++ ++ init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF; ++ init_packet->msg.v1_msg.send_recv_buf. ++ gpadl_handle = net_device->recv_buf_gpadl_handle; ++ init_packet->msg.v1_msg. ++ send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; ++ ++ /* Send the gpadl notification request */ ++ ret = vmbus_sendpacket(device->channel, init_packet, ++ sizeof(struct nvsp_message), ++ (unsigned long)init_packet, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ if (ret != 0) { ++ netdev_err(ndev, ++ "unable to send receive buffer's gpadl to netvsp\n"); ++ goto cleanup; ++ } ++ ++ t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); ++ BUG_ON(t == 0); ++ ++ ++ /* Check the response */ ++ if (init_packet->msg.v1_msg. ++ send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { ++ netdev_err(ndev, "Unable to complete receive buffer " ++ "initialization with NetVsp - status %d\n", ++ init_packet->msg.v1_msg. ++ send_recv_buf_complete.status); ++ ret = -EINVAL; ++ goto cleanup; ++ } ++ ++ /* Parse the response */ ++ ++ net_device->recv_section_cnt = init_packet->msg. ++ v1_msg.send_recv_buf_complete.num_sections; ++ ++ net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections, ++ net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section), ++ GFP_KERNEL); ++ if (net_device->recv_section == NULL) { ++ ret = -EINVAL; ++ goto cleanup; ++ } ++ ++ /* ++ * For 1st release, there should only be 1 section that represents the ++ * entire receive buffer ++ */ ++ if (net_device->recv_section_cnt != 1 || ++ net_device->recv_section->offset != 0) { ++ ret = -EINVAL; ++ goto cleanup; ++ } ++ ++ goto exit; ++ ++cleanup: ++ netvsc_destroy_recv_buf(net_device); ++ ++exit: ++ return ret; ++} ++ ++ ++static int netvsc_connect_vsp(struct hv_device *device) ++{ ++ int ret, t; ++ struct netvsc_device *net_device; ++ struct nvsp_message *init_packet; ++ int ndis_version; ++ struct net_device *ndev; ++ ++ net_device = get_outbound_net_device(device); ++ if (!net_device) ++ return -ENODEV; ++ ndev = net_device->ndev; ++ ++ init_packet = &net_device->channel_init_pkt; ++ ++ memset(init_packet, 0, sizeof(struct nvsp_message)); ++ init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; ++ init_packet->msg.init_msg.init.min_protocol_ver = ++ NVSP_MIN_PROTOCOL_VERSION; ++ init_packet->msg.init_msg.init.max_protocol_ver = ++ NVSP_MAX_PROTOCOL_VERSION; ++ ++ /* Send the init request */ ++ ret = vmbus_sendpacket(device->channel, init_packet, ++ sizeof(struct nvsp_message), ++ (unsigned long)init_packet, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); ++ ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ if (init_packet->msg.init_msg.init_complete.status != ++ NVSP_STAT_SUCCESS) { ++ ret = -EINVAL; ++ goto cleanup; ++ } ++ ++ if (init_packet->msg.init_msg.init_complete. ++ negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { ++ ret = -EPROTO; ++ goto cleanup; ++ } ++ /* Send the ndis version */ ++ memset(init_packet, 0, sizeof(struct nvsp_message)); ++ ++ ndis_version = 0x00050000; ++ ++ init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; ++ init_packet->msg.v1_msg. ++ send_ndis_ver.ndis_major_ver = ++ (ndis_version & 0xFFFF0000) >> 16; ++ init_packet->msg.v1_msg. ++ send_ndis_ver.ndis_minor_ver = ++ ndis_version & 0xFFFF; ++ ++ /* Send the init request */ ++ ret = vmbus_sendpacket(device->channel, init_packet, ++ sizeof(struct nvsp_message), ++ (unsigned long)init_packet, ++ VM_PKT_DATA_INBAND, 0); ++ if (ret != 0) ++ goto cleanup; ++ ++ /* Post the big receive buffer to NetVSP */ ++ ret = netvsc_init_recv_buf(device); ++ ++cleanup: ++ return ret; ++} ++ ++static void netvsc_disconnect_vsp(struct netvsc_device *net_device) ++{ ++ netvsc_destroy_recv_buf(net_device); ++} ++ ++/* ++ * netvsc_device_remove - Callback when the root bus device is removed ++ */ ++int netvsc_device_remove(struct hv_device *device) ++{ ++ struct netvsc_device *net_device; ++ struct hv_netvsc_packet *netvsc_packet, *pos; ++ unsigned long flags; ++ ++ net_device = hv_get_drvdata(device); ++ spin_lock_irqsave(&device->channel->inbound_lock, flags); ++ net_device->destroy = true; ++ spin_unlock_irqrestore(&device->channel->inbound_lock, flags); ++ ++ /* Wait for all send completions */ ++ while (atomic_read(&net_device->num_outstanding_sends)) { ++ dev_info(&device->device, ++ "waiting for %d requests to complete...\n", ++ atomic_read(&net_device->num_outstanding_sends)); ++ udelay(100); ++ } ++ ++ netvsc_disconnect_vsp(net_device); ++ ++ /* ++ * Since we have already drained, we don't need to busy wait ++ * as was done in final_release_stor_device() ++ * Note that we cannot set the ext pointer to NULL until ++ * we have drained - to drain the outgoing packets, we need to ++ * allow incoming packets. ++ */ ++ ++ spin_lock_irqsave(&device->channel->inbound_lock, flags); ++ hv_set_drvdata(device, NULL); ++ spin_unlock_irqrestore(&device->channel->inbound_lock, flags); ++ ++ /* ++ * At this point, no one should be accessing net_device ++ * except in here ++ */ ++ dev_notice(&device->device, "net device safe to remove\n"); ++ ++ /* Now, we can close the channel safely */ ++ vmbus_close(device->channel); ++ ++ /* Release all resources */ ++ list_for_each_entry_safe(netvsc_packet, pos, ++ &net_device->recv_pkt_list, list_ent) { ++ list_del(&netvsc_packet->list_ent); ++ kfree(netvsc_packet); ++ } ++ ++ kfree(net_device); ++ return 0; ++} ++ ++static void netvsc_send_completion(struct hv_device *device, ++ struct vmpacket_descriptor *packet) ++{ ++ struct netvsc_device *net_device; ++ struct nvsp_message *nvsp_packet; ++ struct hv_netvsc_packet *nvsc_packet; ++ struct net_device *ndev; ++ ++ net_device = get_inbound_net_device(device); ++ if (!net_device) ++ return; ++ ndev = net_device->ndev; ++ ++ nvsp_packet = (struct nvsp_message *)((unsigned long)packet + ++ (packet->offset8 << 3)); ++ ++ if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) || ++ (nvsp_packet->hdr.msg_type == ++ NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || ++ (nvsp_packet->hdr.msg_type == ++ NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) { ++ /* Copy the response back */ ++ memcpy(&net_device->channel_init_pkt, nvsp_packet, ++ sizeof(struct nvsp_message)); ++ complete(&net_device->channel_init_wait); ++ } else if (nvsp_packet->hdr.msg_type == ++ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { ++ /* Get the send context */ ++ nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) ++ packet->trans_id; ++ ++ /* Notify the layer above us */ ++ nvsc_packet->completion.send.send_completion( ++ nvsc_packet->completion.send.send_completion_ctx); ++ ++ atomic_dec(&net_device->num_outstanding_sends); ++ } else { ++ netdev_err(ndev, "Unknown send completion packet type- " ++ "%d received!!\n", nvsp_packet->hdr.msg_type); ++ } ++ ++} ++ ++int netvsc_send(struct hv_device *device, ++ struct hv_netvsc_packet *packet) ++{ ++ struct netvsc_device *net_device; ++ int ret = 0; ++ struct nvsp_message sendMessage; ++ struct net_device *ndev; ++ ++ net_device = get_outbound_net_device(device); ++ if (!net_device) ++ return -ENODEV; ++ ndev = net_device->ndev; ++ ++ sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; ++ if (packet->is_data_pkt) { ++ /* 0 is RMC_DATA; */ ++ sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0; ++ } else { ++ /* 1 is RMC_CONTROL; */ ++ sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1; ++ } ++ ++ /* Not using send buffer section */ ++ sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index = ++ 0xFFFFFFFF; ++ sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0; ++ ++ if (packet->page_buf_cnt) { ++ ret = vmbus_sendpacket_pagebuffer(device->channel, ++ packet->page_buf, ++ packet->page_buf_cnt, ++ &sendMessage, ++ sizeof(struct nvsp_message), ++ (unsigned long)packet); ++ } else { ++ ret = vmbus_sendpacket(device->channel, &sendMessage, ++ sizeof(struct nvsp_message), ++ (unsigned long)packet, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ ++ } ++ ++ if (ret != 0) ++ netdev_err(ndev, "Unable to send packet %p ret %d\n", ++ packet, ret); ++ else ++ atomic_inc(&net_device->num_outstanding_sends); ++ ++ return ret; ++} ++ ++static void netvsc_send_recv_completion(struct hv_device *device, ++ u64 transaction_id) ++{ ++ struct nvsp_message recvcompMessage; ++ int retries = 0; ++ int ret; ++ struct net_device *ndev; ++ struct netvsc_device *net_device = hv_get_drvdata(device); ++ ++ ndev = net_device->ndev; ++ ++ recvcompMessage.hdr.msg_type = ++ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; ++ ++ /* FIXME: Pass in the status */ ++ recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = ++ NVSP_STAT_SUCCESS; ++ ++retry_send_cmplt: ++ /* Send the completion */ ++ ret = vmbus_sendpacket(device->channel, &recvcompMessage, ++ sizeof(struct nvsp_message), transaction_id, ++ VM_PKT_COMP, 0); ++ if (ret == 0) { ++ /* success */ ++ /* no-op */ ++ } else if (ret == -EAGAIN) { ++ /* no more room...wait a bit and attempt to retry 3 times */ ++ retries++; ++ netdev_err(ndev, "unable to send receive completion pkt" ++ " (tid %llx)...retrying %d\n", transaction_id, retries); ++ ++ if (retries < 4) { ++ udelay(100); ++ goto retry_send_cmplt; ++ } else { ++ netdev_err(ndev, "unable to send receive " ++ "completion pkt (tid %llx)...give up retrying\n", ++ transaction_id); ++ } ++ } else { ++ netdev_err(ndev, "unable to send receive " ++ "completion pkt - %llx\n", transaction_id); ++ } ++} ++ ++/* Send a receive completion packet to RNDIS device (ie NetVsp) */ ++static void netvsc_receive_completion(void *context) ++{ ++ struct hv_netvsc_packet *packet = context; ++ struct hv_device *device = (struct hv_device *)packet->device; ++ struct netvsc_device *net_device; ++ u64 transaction_id = 0; ++ bool fsend_receive_comp = false; ++ unsigned long flags; ++ struct net_device *ndev; ++ ++ /* ++ * Even though it seems logical to do a GetOutboundNetDevice() here to ++ * send out receive completion, we are using GetInboundNetDevice() ++ * since we may have disable outbound traffic already. ++ */ ++ net_device = get_inbound_net_device(device); ++ if (!net_device) ++ return; ++ ndev = net_device->ndev; ++ ++ /* Overloading use of the lock. */ ++ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); ++ ++ packet->xfer_page_pkt->count--; ++ ++ /* ++ * Last one in the line that represent 1 xfer page packet. ++ * Return the xfer page packet itself to the freelist ++ */ ++ if (packet->xfer_page_pkt->count == 0) { ++ fsend_receive_comp = true; ++ transaction_id = packet->completion.recv.recv_completion_tid; ++ list_add_tail(&packet->xfer_page_pkt->list_ent, ++ &net_device->recv_pkt_list); ++ ++ } ++ ++ /* Put the packet back */ ++ list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); ++ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); ++ ++ /* Send a receive completion for the xfer page packet */ ++ if (fsend_receive_comp) ++ netvsc_send_recv_completion(device, transaction_id); ++ ++} ++ ++static void netvsc_receive(struct hv_device *device, ++ struct vmpacket_descriptor *packet) ++{ ++ struct netvsc_device *net_device; ++ struct vmtransfer_page_packet_header *vmxferpage_packet; ++ struct nvsp_message *nvsp_packet; ++ struct hv_netvsc_packet *netvsc_packet = NULL; ++ unsigned long start; ++ unsigned long end, end_virtual; ++ /* struct netvsc_driver *netvscDriver; */ ++ struct xferpage_packet *xferpage_packet = NULL; ++ int i, j; ++ int count = 0, bytes_remain = 0; ++ unsigned long flags; ++ struct net_device *ndev; ++ ++ LIST_HEAD(listHead); ++ ++ net_device = get_inbound_net_device(device); ++ if (!net_device) ++ return; ++ ndev = net_device->ndev; ++ ++ /* ++ * All inbound packets other than send completion should be xfer page ++ * packet ++ */ ++ if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) { ++ netdev_err(ndev, "Unknown packet type received - %d\n", ++ packet->type); ++ return; ++ } ++ ++ nvsp_packet = (struct nvsp_message *)((unsigned long)packet + ++ (packet->offset8 << 3)); ++ ++ /* Make sure this is a valid nvsp packet */ ++ if (nvsp_packet->hdr.msg_type != ++ NVSP_MSG1_TYPE_SEND_RNDIS_PKT) { ++ netdev_err(ndev, "Unknown nvsp packet type received-" ++ " %d\n", nvsp_packet->hdr.msg_type); ++ return; ++ } ++ ++ vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet; ++ ++ if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) { ++ netdev_err(ndev, "Invalid xfer page set id - " ++ "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID, ++ vmxferpage_packet->xfer_pageset_id); ++ return; ++ } ++ ++ /* ++ * Grab free packets (range count + 1) to represent this xfer ++ * page packet. +1 to represent the xfer page packet itself. ++ * We grab it here so that we know exactly how many we can ++ * fulfil ++ */ ++ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); ++ while (!list_empty(&net_device->recv_pkt_list)) { ++ list_move_tail(net_device->recv_pkt_list.next, &listHead); ++ if (++count == vmxferpage_packet->range_cnt + 1) ++ break; ++ } ++ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); ++ ++ /* ++ * We need at least 2 netvsc pkts (1 to represent the xfer ++ * page and at least 1 for the range) i.e. we can handled ++ * some of the xfer page packet ranges... ++ */ ++ if (count < 2) { ++ netdev_err(ndev, "Got only %d netvsc pkt...needed " ++ "%d pkts. Dropping this xfer page packet completely!\n", ++ count, vmxferpage_packet->range_cnt + 1); ++ ++ /* Return it to the freelist */ ++ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); ++ for (i = count; i != 0; i--) { ++ list_move_tail(listHead.next, ++ &net_device->recv_pkt_list); ++ } ++ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, ++ flags); ++ ++ netvsc_send_recv_completion(device, ++ vmxferpage_packet->d.trans_id); ++ ++ return; ++ } ++ ++ /* Remove the 1st packet to represent the xfer page packet itself */ ++ xferpage_packet = (struct xferpage_packet *)listHead.next; ++ list_del(&xferpage_packet->list_ent); ++ ++ /* This is how much we can satisfy */ ++ xferpage_packet->count = count - 1; ++ ++ if (xferpage_packet->count != vmxferpage_packet->range_cnt) { ++ netdev_err(ndev, "Needed %d netvsc pkts to satisfy " ++ "this xfer page...got %d\n", ++ vmxferpage_packet->range_cnt, xferpage_packet->count); ++ } ++ ++ /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ ++ for (i = 0; i < (count - 1); i++) { ++ netvsc_packet = (struct hv_netvsc_packet *)listHead.next; ++ list_del(&netvsc_packet->list_ent); ++ ++ /* Initialize the netvsc packet */ ++ netvsc_packet->xfer_page_pkt = xferpage_packet; ++ netvsc_packet->completion.recv.recv_completion = ++ netvsc_receive_completion; ++ netvsc_packet->completion.recv.recv_completion_ctx = ++ netvsc_packet; ++ netvsc_packet->device = device; ++ /* Save this so that we can send it back */ ++ netvsc_packet->completion.recv.recv_completion_tid = ++ vmxferpage_packet->d.trans_id; ++ ++ netvsc_packet->total_data_buflen = ++ vmxferpage_packet->ranges[i].byte_count; ++ netvsc_packet->page_buf_cnt = 1; ++ ++ netvsc_packet->page_buf[0].len = ++ vmxferpage_packet->ranges[i].byte_count; ++ ++ start = virt_to_phys((void *)((unsigned long)net_device-> ++ recv_buf + vmxferpage_packet->ranges[i].byte_offset)); ++ ++ netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT; ++ end_virtual = (unsigned long)net_device->recv_buf ++ + vmxferpage_packet->ranges[i].byte_offset ++ + vmxferpage_packet->ranges[i].byte_count - 1; ++ end = virt_to_phys((void *)end_virtual); ++ ++ /* Calculate the page relative offset */ ++ netvsc_packet->page_buf[0].offset = ++ vmxferpage_packet->ranges[i].byte_offset & ++ (PAGE_SIZE - 1); ++ if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { ++ /* Handle frame across multiple pages: */ ++ netvsc_packet->page_buf[0].len = ++ (netvsc_packet->page_buf[0].pfn << ++ PAGE_SHIFT) ++ + PAGE_SIZE - start; ++ bytes_remain = netvsc_packet->total_data_buflen - ++ netvsc_packet->page_buf[0].len; ++ for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { ++ netvsc_packet->page_buf[j].offset = 0; ++ if (bytes_remain <= PAGE_SIZE) { ++ netvsc_packet->page_buf[j].len = ++ bytes_remain; ++ bytes_remain = 0; ++ } else { ++ netvsc_packet->page_buf[j].len = ++ PAGE_SIZE; ++ bytes_remain -= PAGE_SIZE; ++ } ++ netvsc_packet->page_buf[j].pfn = ++ virt_to_phys((void *)(end_virtual - ++ bytes_remain)) >> PAGE_SHIFT; ++ netvsc_packet->page_buf_cnt++; ++ if (bytes_remain == 0) ++ break; ++ } ++ } ++ ++ /* Pass it to the upper layer */ ++ rndis_filter_receive(device, netvsc_packet); ++ ++ netvsc_receive_completion(netvsc_packet-> ++ completion.recv.recv_completion_ctx); ++ } ++ ++} ++ ++static void netvsc_channel_cb(void *context) ++{ ++ int ret; ++ struct hv_device *device = context; ++ struct netvsc_device *net_device; ++ u32 bytes_recvd; ++ u64 request_id; ++ unsigned char *packet; ++ struct vmpacket_descriptor *desc; ++ unsigned char *buffer; ++ int bufferlen = NETVSC_PACKET_SIZE; ++ struct net_device *ndev; ++ ++ packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char), ++ GFP_ATOMIC); ++ if (!packet) ++ return; ++ buffer = packet; ++ ++ net_device = get_inbound_net_device(device); ++ if (!net_device) ++ goto out; ++ ndev = net_device->ndev; ++ ++ do { ++ ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, ++ &bytes_recvd, &request_id); ++ if (ret == 0) { ++ if (bytes_recvd > 0) { ++ desc = (struct vmpacket_descriptor *)buffer; ++ switch (desc->type) { ++ case VM_PKT_COMP: ++ netvsc_send_completion(device, desc); ++ break; ++ ++ case VM_PKT_DATA_USING_XFER_PAGES: ++ netvsc_receive(device, desc); ++ break; ++ ++ default: ++ netdev_err(ndev, ++ "unhandled packet type %d, " ++ "tid %llx len %d\n", ++ desc->type, request_id, ++ bytes_recvd); ++ break; ++ } ++ ++ /* reset */ ++ if (bufferlen > NETVSC_PACKET_SIZE) { ++ kfree(buffer); ++ buffer = packet; ++ bufferlen = NETVSC_PACKET_SIZE; ++ } ++ } else { ++ /* reset */ ++ if (bufferlen > NETVSC_PACKET_SIZE) { ++ kfree(buffer); ++ buffer = packet; ++ bufferlen = NETVSC_PACKET_SIZE; ++ } ++ ++ break; ++ } ++ } else if (ret == -ENOBUFS) { ++ /* Handle large packet */ ++ buffer = kmalloc(bytes_recvd, GFP_ATOMIC); ++ if (buffer == NULL) { ++ /* Try again next time around */ ++ netdev_err(ndev, ++ "unable to allocate buffer of size " ++ "(%d)!!\n", bytes_recvd); ++ break; ++ } ++ ++ bufferlen = bytes_recvd; ++ } ++ } while (1); ++ ++out: ++ kfree(buffer); ++ return; ++} ++ ++/* ++ * netvsc_device_add - Callback when the device belonging to this ++ * driver is added ++ */ ++int netvsc_device_add(struct hv_device *device, void *additional_info) ++{ ++ int ret = 0; ++ int i; ++ int ring_size = ++ ((struct netvsc_device_info *)additional_info)->ring_size; ++ struct netvsc_device *net_device; ++ struct hv_netvsc_packet *packet, *pos; ++ struct net_device *ndev; ++ ++ net_device = alloc_net_device(device); ++ if (!net_device) { ++ ret = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* ++ * Coming into this function, struct net_device * is ++ * registered as the driver private data. ++ * In alloc_net_device(), we register struct netvsc_device * ++ * as the driver private data and stash away struct net_device * ++ * in struct netvsc_device *. ++ */ ++ ndev = net_device->ndev; ++ ++ /* Initialize the NetVSC channel extension */ ++ net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; ++ spin_lock_init(&net_device->recv_pkt_list_lock); ++ ++ INIT_LIST_HEAD(&net_device->recv_pkt_list); ++ ++ for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { ++ packet = kzalloc(sizeof(struct hv_netvsc_packet) + ++ (NETVSC_RECEIVE_SG_COUNT * ++ sizeof(struct hv_page_buffer)), GFP_KERNEL); ++ if (!packet) ++ break; ++ ++ list_add_tail(&packet->list_ent, ++ &net_device->recv_pkt_list); ++ } ++ init_completion(&net_device->channel_init_wait); ++ ++ /* Open the channel */ ++ ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, ++ ring_size * PAGE_SIZE, NULL, 0, ++ netvsc_channel_cb, device); ++ ++ if (ret != 0) { ++ netdev_err(ndev, "unable to open channel: %d\n", ret); ++ goto cleanup; ++ } ++ ++ /* Channel is opened */ ++ pr_info("hv_netvsc channel opened successfully\n"); ++ ++ /* Connect with the NetVsp */ ++ ret = netvsc_connect_vsp(device); ++ if (ret != 0) { ++ netdev_err(ndev, ++ "unable to connect to NetVSP - %d\n", ret); ++ goto close; ++ } ++ ++ return ret; ++ ++close: ++ /* Now, we can close the channel safely */ ++ vmbus_close(device->channel); ++ ++cleanup: ++ ++ if (net_device) { ++ list_for_each_entry_safe(packet, pos, ++ &net_device->recv_pkt_list, ++ list_ent) { ++ list_del(&packet->list_ent); ++ kfree(packet); ++ } ++ ++ kfree(net_device); ++ } ++ ++ return ret; ++} +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +new file mode 100644 +index 0000000..93b0e91 +--- /dev/null ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -0,0 +1,456 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hyperv_net.h" ++ ++struct net_device_context { ++ /* point back to our device context */ ++ struct hv_device *device_ctx; ++ atomic_t avail; ++ struct delayed_work dwork; ++}; ++ ++ ++#define PACKET_PAGES_LOWATER 8 ++/* Need this many pages to handle worst case fragmented packet */ ++#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2) ++ ++static int ring_size = 128; ++module_param(ring_size, int, S_IRUGO); ++MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); ++ ++/* no-op so the netdev core doesn't return -EINVAL when modifying the the ++ * multicast address list in SIOCADDMULTI. hv is setup to get all multicast ++ * when it calls RndisFilterOnOpen() */ ++static void netvsc_set_multicast_list(struct net_device *net) ++{ ++} ++ ++static int netvsc_open(struct net_device *net) ++{ ++ struct net_device_context *net_device_ctx = netdev_priv(net); ++ struct hv_device *device_obj = net_device_ctx->device_ctx; ++ int ret = 0; ++ ++ /* Open up the device */ ++ ret = rndis_filter_open(device_obj); ++ if (ret != 0) { ++ netdev_err(net, "unable to open device (ret %d).\n", ret); ++ return ret; ++ } ++ ++ netif_start_queue(net); ++ ++ return ret; ++} ++ ++static int netvsc_close(struct net_device *net) ++{ ++ struct net_device_context *net_device_ctx = netdev_priv(net); ++ struct hv_device *device_obj = net_device_ctx->device_ctx; ++ int ret; ++ ++ netif_stop_queue(net); ++ ++ ret = rndis_filter_close(device_obj); ++ if (ret != 0) ++ netdev_err(net, "unable to close device (ret %d).\n", ret); ++ ++ return ret; ++} ++ ++static void netvsc_xmit_completion(void *context) ++{ ++ struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; ++ struct sk_buff *skb = (struct sk_buff *) ++ (unsigned long)packet->completion.send.send_completion_tid; ++ ++ kfree(packet); ++ ++ if (skb) { ++ struct net_device *net = skb->dev; ++ struct net_device_context *net_device_ctx = netdev_priv(net); ++ unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2; ++ ++ dev_kfree_skb_any(skb); ++ ++ atomic_add(num_pages, &net_device_ctx->avail); ++ if (atomic_read(&net_device_ctx->avail) >= ++ PACKET_PAGES_HIWATER) ++ netif_wake_queue(net); ++ } ++} ++ ++static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ++{ ++ struct net_device_context *net_device_ctx = netdev_priv(net); ++ struct hv_netvsc_packet *packet; ++ int ret; ++ unsigned int i, num_pages; ++ ++ /* Add 1 for skb->data and additional one for RNDIS */ ++ num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; ++ if (num_pages > atomic_read(&net_device_ctx->avail)) ++ return NETDEV_TX_BUSY; ++ ++ /* Allocate a netvsc packet based on # of frags. */ ++ packet = kzalloc(sizeof(struct hv_netvsc_packet) + ++ (num_pages * sizeof(struct hv_page_buffer)) + ++ sizeof(struct rndis_filter_packet), GFP_ATOMIC); ++ if (!packet) { ++ /* out of memory, drop packet */ ++ netdev_err(net, "unable to allocate hv_netvsc_packet\n"); ++ ++ dev_kfree_skb(skb); ++ net->stats.tx_dropped++; ++ return NETDEV_TX_BUSY; ++ } ++ ++ packet->extension = (void *)(unsigned long)packet + ++ sizeof(struct hv_netvsc_packet) + ++ (num_pages * sizeof(struct hv_page_buffer)); ++ ++ /* Setup the rndis header */ ++ packet->page_buf_cnt = num_pages; ++ ++ /* Initialize it from the skb */ ++ packet->total_data_buflen = skb->len; ++ ++ /* Start filling in the page buffers starting after RNDIS buffer. */ ++ packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; ++ packet->page_buf[1].offset ++ = (unsigned long)skb->data & (PAGE_SIZE - 1); ++ packet->page_buf[1].len = skb_headlen(skb); ++ ++ /* Additional fragments are after SKB data */ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; ++ ++ packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f)); ++ packet->page_buf[i+2].offset = f->page_offset; ++ packet->page_buf[i+2].len = skb_frag_size(f); ++ } ++ ++ /* Set the completion routine */ ++ packet->completion.send.send_completion = netvsc_xmit_completion; ++ packet->completion.send.send_completion_ctx = packet; ++ packet->completion.send.send_completion_tid = (unsigned long)skb; ++ ++ ret = rndis_filter_send(net_device_ctx->device_ctx, ++ packet); ++ if (ret == 0) { ++ net->stats.tx_bytes += skb->len; ++ net->stats.tx_packets++; ++ ++ atomic_sub(num_pages, &net_device_ctx->avail); ++ if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER) ++ netif_stop_queue(net); ++ } else { ++ /* we are shutting down or bus overloaded, just drop packet */ ++ net->stats.tx_dropped++; ++ kfree(packet); ++ dev_kfree_skb_any(skb); ++ } ++ ++ return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; ++} ++ ++/* ++ * netvsc_linkstatus_callback - Link up/down notification ++ */ ++void netvsc_linkstatus_callback(struct hv_device *device_obj, ++ unsigned int status) ++{ ++ struct net_device *net; ++ struct net_device_context *ndev_ctx; ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(device_obj); ++ net = net_device->ndev; ++ ++ if (!net) { ++ netdev_err(net, "got link status but net device " ++ "not initialized yet\n"); ++ return; ++ } ++ ++ if (status == 1) { ++ netif_carrier_on(net); ++ netif_wake_queue(net); ++ ndev_ctx = netdev_priv(net); ++ schedule_delayed_work(&ndev_ctx->dwork, 0); ++ schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); ++ } else { ++ netif_carrier_off(net); ++ netif_stop_queue(net); ++ } ++} ++ ++/* ++ * netvsc_recv_callback - Callback when we receive a packet from the ++ * "wire" on the specified device. ++ */ ++int netvsc_recv_callback(struct hv_device *device_obj, ++ struct hv_netvsc_packet *packet) ++{ ++ struct net_device *net = dev_get_drvdata(&device_obj->device); ++ struct sk_buff *skb; ++ void *data; ++ int i; ++ unsigned long flags; ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(device_obj); ++ net = net_device->ndev; ++ ++ if (!net) { ++ netdev_err(net, "got receive callback but net device" ++ " not initialized yet\n"); ++ return 0; ++ } ++ ++ /* Allocate a skb - TODO direct I/O to pages? */ ++ skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); ++ if (unlikely(!skb)) { ++ ++net->stats.rx_dropped; ++ return 0; ++ } ++ ++ /* for kmap_atomic */ ++ local_irq_save(flags); ++ ++ /* ++ * Copy to skb. This copy is needed here since the memory pointed by ++ * hv_netvsc_packet cannot be deallocated ++ */ ++ for (i = 0; i < packet->page_buf_cnt; i++) { ++ data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn), ++ KM_IRQ1); ++ data = (void *)(unsigned long)data + ++ packet->page_buf[i].offset; ++ ++ memcpy(skb_put(skb, packet->page_buf[i].len), data, ++ packet->page_buf[i].len); ++ ++ kunmap_atomic((void *)((unsigned long)data - ++ packet->page_buf[i].offset), KM_IRQ1); ++ } ++ ++ local_irq_restore(flags); ++ ++ skb->protocol = eth_type_trans(skb, net); ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ net->stats.rx_packets++; ++ net->stats.rx_bytes += skb->len; ++ ++ /* ++ * Pass the skb back up. Network stack will deallocate the skb when it ++ * is done. ++ * TODO - use NAPI? ++ */ ++ netif_rx(skb); ++ ++ return 0; ++} ++ ++static void netvsc_get_drvinfo(struct net_device *net, ++ struct ethtool_drvinfo *info) ++{ ++ strcpy(info->driver, "hv_netvsc"); ++ strcpy(info->version, HV_DRV_VERSION); ++ strcpy(info->fw_version, "N/A"); ++} ++ ++static const struct ethtool_ops ethtool_ops = { ++ .get_drvinfo = netvsc_get_drvinfo, ++ .get_link = ethtool_op_get_link, ++}; ++ ++static const struct net_device_ops device_ops = { ++ .ndo_open = netvsc_open, ++ .ndo_stop = netvsc_close, ++ .ndo_start_xmit = netvsc_start_xmit, ++ .ndo_set_rx_mode = netvsc_set_multicast_list, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_set_mac_address = eth_mac_addr, ++}; ++ ++/* ++ * Send GARP packet to network peers after migrations. ++ * After Quick Migration, the network is not immediately operational in the ++ * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add ++ * another netif_notify_peers() into a delayed work, otherwise GARP packet ++ * will not be sent after quick migration, and cause network disconnection. ++ */ ++static void netvsc_send_garp(struct work_struct *w) ++{ ++ struct net_device_context *ndev_ctx; ++ struct net_device *net; ++ struct netvsc_device *net_device; ++ ++ ndev_ctx = container_of(w, struct net_device_context, dwork.work); ++ net_device = hv_get_drvdata(ndev_ctx->device_ctx); ++ net = net_device->ndev; ++ netif_notify_peers(net); ++} ++ ++ ++static int netvsc_probe(struct hv_device *dev, ++ const struct hv_vmbus_device_id *dev_id) ++{ ++ struct net_device *net = NULL; ++ struct net_device_context *net_device_ctx; ++ struct netvsc_device_info device_info; ++ int ret; ++ ++ net = alloc_etherdev(sizeof(struct net_device_context)); ++ if (!net) ++ return -ENOMEM; ++ ++ /* Set initial state */ ++ netif_carrier_off(net); ++ ++ net_device_ctx = netdev_priv(net); ++ net_device_ctx->device_ctx = dev; ++ atomic_set(&net_device_ctx->avail, ring_size); ++ hv_set_drvdata(dev, net); ++ INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); ++ ++ net->netdev_ops = &device_ops; ++ ++ /* TODO: Add GSO and Checksum offload */ ++ net->hw_features = NETIF_F_SG; ++ net->features = NETIF_F_SG; ++ ++ SET_ETHTOOL_OPS(net, ðtool_ops); ++ SET_NETDEV_DEV(net, &dev->device); ++ ++ ret = register_netdev(net); ++ if (ret != 0) { ++ pr_err("Unable to register netdev.\n"); ++ free_netdev(net); ++ goto out; ++ } ++ ++ /* Notify the netvsc driver of the new device */ ++ device_info.ring_size = ring_size; ++ ret = rndis_filter_device_add(dev, &device_info); ++ if (ret != 0) { ++ netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); ++ unregister_netdev(net); ++ free_netdev(net); ++ hv_set_drvdata(dev, NULL); ++ return ret; ++ } ++ memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); ++ ++ netif_carrier_on(net); ++ ++out: ++ return ret; ++} ++ ++static int netvsc_remove(struct hv_device *dev) ++{ ++ struct net_device *net; ++ struct net_device_context *ndev_ctx; ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(dev); ++ net = net_device->ndev; ++ ++ if (net == NULL) { ++ dev_err(&dev->device, "No net device to remove\n"); ++ return 0; ++ } ++ ++ ndev_ctx = netdev_priv(net); ++ cancel_delayed_work_sync(&ndev_ctx->dwork); ++ ++ /* Stop outbound asap */ ++ netif_stop_queue(net); ++ ++ unregister_netdev(net); ++ ++ /* ++ * Call to the vsc driver to let it know that the device is being ++ * removed ++ */ ++ rndis_filter_device_remove(dev); ++ ++ free_netdev(net); ++ return 0; ++} ++ ++static const struct hv_vmbus_device_id id_table[] = { ++ /* Network guid */ ++ { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, ++ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(vmbus, id_table); ++ ++/* The one and only one */ ++static struct hv_driver netvsc_drv = { ++ .name = "netvsc", ++ .id_table = id_table, ++ .probe = netvsc_probe, ++ .remove = netvsc_remove, ++}; ++ ++static void __exit netvsc_drv_exit(void) ++{ ++ vmbus_driver_unregister(&netvsc_drv); ++} ++ ++static int __init netvsc_drv_init(void) ++{ ++ return vmbus_driver_register(&netvsc_drv); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(HV_DRV_VERSION); ++MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); ++ ++module_init(netvsc_drv_init); ++module_exit(netvsc_drv_exit); +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +new file mode 100644 +index 0000000..bafccb3 +--- /dev/null ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -0,0 +1,855 @@ ++/* ++ * 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 ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hyperv_net.h" ++ ++ ++enum rndis_device_state { ++ RNDIS_DEV_UNINITIALIZED = 0, ++ RNDIS_DEV_INITIALIZING, ++ RNDIS_DEV_INITIALIZED, ++ RNDIS_DEV_DATAINITIALIZED, ++}; ++ ++struct rndis_device { ++ struct netvsc_device *net_dev; ++ ++ enum rndis_device_state state; ++ bool link_state; ++ atomic_t new_req_id; ++ ++ spinlock_t request_lock; ++ struct list_head req_list; ++ ++ unsigned char hw_mac_adr[ETH_ALEN]; ++}; ++ ++struct rndis_request { ++ struct list_head list_ent; ++ struct completion wait_event; ++ ++ /* ++ * FIXME: We assumed a fixed size response here. If we do ever need to ++ * handle a bigger response, we can either define a max response ++ * message or add a response buffer variable above this field ++ */ ++ struct rndis_message response_msg; ++ ++ /* Simplify allocation by having a netvsc packet inline */ ++ struct hv_netvsc_packet pkt; ++ struct hv_page_buffer buf; ++ /* FIXME: We assumed a fixed size request here. */ ++ struct rndis_message request_msg; ++}; ++ ++static void rndis_filter_send_completion(void *ctx); ++ ++static void rndis_filter_send_request_completion(void *ctx); ++ ++ ++ ++static struct rndis_device *get_rndis_device(void) ++{ ++ struct rndis_device *device; ++ ++ device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); ++ if (!device) ++ return NULL; ++ ++ spin_lock_init(&device->request_lock); ++ ++ INIT_LIST_HEAD(&device->req_list); ++ ++ device->state = RNDIS_DEV_UNINITIALIZED; ++ ++ return device; ++} ++ ++static struct rndis_request *get_rndis_request(struct rndis_device *dev, ++ u32 msg_type, ++ u32 msg_len) ++{ ++ struct rndis_request *request; ++ struct rndis_message *rndis_msg; ++ struct rndis_set_request *set; ++ unsigned long flags; ++ ++ request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); ++ if (!request) ++ return NULL; ++ ++ init_completion(&request->wait_event); ++ ++ rndis_msg = &request->request_msg; ++ rndis_msg->ndis_msg_type = msg_type; ++ rndis_msg->msg_len = msg_len; ++ ++ /* ++ * Set the request id. This field is always after the rndis header for ++ * request/response packet types so we just used the SetRequest as a ++ * template ++ */ ++ set = &rndis_msg->msg.set_req; ++ set->req_id = atomic_inc_return(&dev->new_req_id); ++ ++ /* Add to the request list */ ++ spin_lock_irqsave(&dev->request_lock, flags); ++ list_add_tail(&request->list_ent, &dev->req_list); ++ spin_unlock_irqrestore(&dev->request_lock, flags); ++ ++ return request; ++} ++ ++static void put_rndis_request(struct rndis_device *dev, ++ struct rndis_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->request_lock, flags); ++ list_del(&req->list_ent); ++ spin_unlock_irqrestore(&dev->request_lock, flags); ++ ++ kfree(req); ++} ++ ++static void dump_rndis_message(struct hv_device *hv_dev, ++ struct rndis_message *rndis_msg) ++{ ++ struct net_device *netdev; ++ struct netvsc_device *net_device; ++ ++ net_device = hv_get_drvdata(hv_dev); ++ netdev = net_device->ndev; ++ ++ switch (rndis_msg->ndis_msg_type) { ++ case REMOTE_NDIS_PACKET_MSG: ++ netdev_dbg(netdev, "REMOTE_NDIS_PACKET_MSG (len %u, " ++ "data offset %u data len %u, # oob %u, " ++ "oob offset %u, oob len %u, pkt offset %u, " ++ "pkt len %u\n", ++ rndis_msg->msg_len, ++ rndis_msg->msg.pkt.data_offset, ++ rndis_msg->msg.pkt.data_len, ++ rndis_msg->msg.pkt.num_oob_data_elements, ++ rndis_msg->msg.pkt.oob_data_offset, ++ rndis_msg->msg.pkt.oob_data_len, ++ rndis_msg->msg.pkt.per_pkt_info_offset, ++ rndis_msg->msg.pkt.per_pkt_info_len); ++ break; ++ ++ case REMOTE_NDIS_INITIALIZE_CMPLT: ++ netdev_dbg(netdev, "REMOTE_NDIS_INITIALIZE_CMPLT " ++ "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " ++ "device flags %d, max xfer size 0x%x, max pkts %u, " ++ "pkt aligned %u)\n", ++ rndis_msg->msg_len, ++ rndis_msg->msg.init_complete.req_id, ++ rndis_msg->msg.init_complete.status, ++ rndis_msg->msg.init_complete.major_ver, ++ rndis_msg->msg.init_complete.minor_ver, ++ rndis_msg->msg.init_complete.dev_flags, ++ rndis_msg->msg.init_complete.max_xfer_size, ++ rndis_msg->msg.init_complete. ++ max_pkt_per_msg, ++ rndis_msg->msg.init_complete. ++ pkt_alignment_factor); ++ break; ++ ++ case REMOTE_NDIS_QUERY_CMPLT: ++ netdev_dbg(netdev, "REMOTE_NDIS_QUERY_CMPLT " ++ "(len %u, id 0x%x, status 0x%x, buf len %u, " ++ "buf offset %u)\n", ++ rndis_msg->msg_len, ++ rndis_msg->msg.query_complete.req_id, ++ rndis_msg->msg.query_complete.status, ++ rndis_msg->msg.query_complete. ++ info_buflen, ++ rndis_msg->msg.query_complete. ++ info_buf_offset); ++ break; ++ ++ case REMOTE_NDIS_SET_CMPLT: ++ netdev_dbg(netdev, ++ "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)\n", ++ rndis_msg->msg_len, ++ rndis_msg->msg.set_complete.req_id, ++ rndis_msg->msg.set_complete.status); ++ break; ++ ++ case REMOTE_NDIS_INDICATE_STATUS_MSG: ++ netdev_dbg(netdev, "REMOTE_NDIS_INDICATE_STATUS_MSG " ++ "(len %u, status 0x%x, buf len %u, buf offset %u)\n", ++ rndis_msg->msg_len, ++ rndis_msg->msg.indicate_status.status, ++ rndis_msg->msg.indicate_status.status_buflen, ++ rndis_msg->msg.indicate_status.status_buf_offset); ++ break; ++ ++ default: ++ netdev_dbg(netdev, "0x%x (len %u)\n", ++ rndis_msg->ndis_msg_type, ++ rndis_msg->msg_len); ++ break; ++ } ++} ++ ++static int rndis_filter_send_request(struct rndis_device *dev, ++ struct rndis_request *req) ++{ ++ int ret; ++ struct hv_netvsc_packet *packet; ++ ++ /* Setup the packet to send it */ ++ packet = &req->pkt; ++ ++ packet->is_data_pkt = false; ++ packet->total_data_buflen = req->request_msg.msg_len; ++ packet->page_buf_cnt = 1; ++ ++ packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> ++ PAGE_SHIFT; ++ packet->page_buf[0].len = req->request_msg.msg_len; ++ packet->page_buf[0].offset = ++ (unsigned long)&req->request_msg & (PAGE_SIZE - 1); ++ ++ packet->completion.send.send_completion_ctx = req;/* packet; */ ++ packet->completion.send.send_completion = ++ rndis_filter_send_request_completion; ++ packet->completion.send.send_completion_tid = (unsigned long)dev; ++ ++ ret = netvsc_send(dev->net_dev->dev, packet); ++ return ret; ++} ++ ++static void rndis_filter_receive_response(struct rndis_device *dev, ++ struct rndis_message *resp) ++{ ++ struct rndis_request *request = NULL; ++ bool found = false; ++ unsigned long flags; ++ struct net_device *ndev; ++ ++ ndev = dev->net_dev->ndev; ++ ++ spin_lock_irqsave(&dev->request_lock, flags); ++ list_for_each_entry(request, &dev->req_list, list_ent) { ++ /* ++ * All request/response message contains RequestId as the 1st ++ * field ++ */ ++ if (request->request_msg.msg.init_req.req_id ++ == resp->msg.init_complete.req_id) { ++ found = true; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&dev->request_lock, flags); ++ ++ if (found) { ++ if (resp->msg_len <= sizeof(struct rndis_message)) { ++ memcpy(&request->response_msg, resp, ++ resp->msg_len); ++ } else { ++ netdev_err(ndev, ++ "rndis response buffer overflow " ++ "detected (size %u max %zu)\n", ++ resp->msg_len, ++ sizeof(struct rndis_filter_packet)); ++ ++ if (resp->ndis_msg_type == ++ REMOTE_NDIS_RESET_CMPLT) { ++ /* does not have a request id field */ ++ request->response_msg.msg.reset_complete. ++ status = STATUS_BUFFER_OVERFLOW; ++ } else { ++ request->response_msg.msg. ++ init_complete.status = ++ STATUS_BUFFER_OVERFLOW; ++ } ++ } ++ ++ complete(&request->wait_event); ++ } else { ++ netdev_err(ndev, ++ "no rndis request found for this response " ++ "(id 0x%x res type 0x%x)\n", ++ resp->msg.init_complete.req_id, ++ resp->ndis_msg_type); ++ } ++} ++ ++static void rndis_filter_receive_indicate_status(struct rndis_device *dev, ++ struct rndis_message *resp) ++{ ++ struct rndis_indicate_status *indicate = ++ &resp->msg.indicate_status; ++ ++ if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { ++ netvsc_linkstatus_callback( ++ dev->net_dev->dev, 1); ++ } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { ++ netvsc_linkstatus_callback( ++ dev->net_dev->dev, 0); ++ } else { ++ /* ++ * TODO: ++ */ ++ } ++} ++ ++static void rndis_filter_receive_data(struct rndis_device *dev, ++ struct rndis_message *msg, ++ struct hv_netvsc_packet *pkt) ++{ ++ struct rndis_packet *rndis_pkt; ++ u32 data_offset; ++ int i; ++ ++ rndis_pkt = &msg->msg.pkt; ++ ++ /* ++ * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this ++ * netvsc packet (ie TotalDataBufferLength != MessageLength) ++ */ ++ ++ /* Remove the rndis header and pass it back up the stack */ ++ data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; ++ ++ pkt->total_data_buflen -= data_offset; ++ pkt->page_buf[0].offset += data_offset; ++ pkt->page_buf[0].len -= data_offset; ++ ++ /* Drop the 0th page, if rndis data go beyond page boundary */ ++ if (pkt->page_buf[0].offset >= PAGE_SIZE) { ++ pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE; ++ pkt->page_buf[1].len -= pkt->page_buf[1].offset; ++ pkt->page_buf_cnt--; ++ for (i = 0; i < pkt->page_buf_cnt; i++) ++ pkt->page_buf[i] = pkt->page_buf[i+1]; ++ } ++ ++ pkt->is_data_pkt = true; ++ ++ netvsc_recv_callback(dev->net_dev->dev, pkt); ++} ++ ++int rndis_filter_receive(struct hv_device *dev, ++ struct hv_netvsc_packet *pkt) ++{ ++ struct netvsc_device *net_dev = hv_get_drvdata(dev); ++ struct rndis_device *rndis_dev; ++ struct rndis_message rndis_msg; ++ struct rndis_message *rndis_hdr; ++ struct net_device *ndev; ++ ++ if (!net_dev) ++ return -EINVAL; ++ ++ ndev = net_dev->ndev; ++ ++ /* Make sure the rndis device state is initialized */ ++ if (!net_dev->extension) { ++ netdev_err(ndev, "got rndis message but no rndis device - " ++ "dropping this message!\n"); ++ return -ENODEV; ++ } ++ ++ rndis_dev = (struct rndis_device *)net_dev->extension; ++ if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { ++ netdev_err(ndev, "got rndis message but rndis device " ++ "uninitialized...dropping this message!\n"); ++ return -ENODEV; ++ } ++ ++ rndis_hdr = (struct rndis_message *)kmap_atomic( ++ pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0); ++ ++ rndis_hdr = (void *)((unsigned long)rndis_hdr + ++ pkt->page_buf[0].offset); ++ ++ /* Make sure we got a valid rndis message */ ++ if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && ++ (rndis_hdr->msg_len > sizeof(struct rndis_message))) { ++ netdev_err(ndev, "incoming rndis message buffer overflow " ++ "detected (got %u, max %zu)..marking it an error!\n", ++ rndis_hdr->msg_len, ++ sizeof(struct rndis_message)); ++ } ++ ++ memcpy(&rndis_msg, rndis_hdr, ++ (rndis_hdr->msg_len > sizeof(struct rndis_message)) ? ++ sizeof(struct rndis_message) : ++ rndis_hdr->msg_len); ++ ++ kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); ++ ++ dump_rndis_message(dev, &rndis_msg); ++ ++ switch (rndis_msg.ndis_msg_type) { ++ case REMOTE_NDIS_PACKET_MSG: ++ /* data msg */ ++ rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt); ++ break; ++ ++ case REMOTE_NDIS_INITIALIZE_CMPLT: ++ case REMOTE_NDIS_QUERY_CMPLT: ++ case REMOTE_NDIS_SET_CMPLT: ++ /* completion msgs */ ++ rndis_filter_receive_response(rndis_dev, &rndis_msg); ++ break; ++ ++ case REMOTE_NDIS_INDICATE_STATUS_MSG: ++ /* notification msgs */ ++ rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); ++ break; ++ default: ++ netdev_err(ndev, ++ "unhandled rndis message (type %u len %u)\n", ++ rndis_msg.ndis_msg_type, ++ rndis_msg.msg_len); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, ++ void *result, u32 *result_size) ++{ ++ struct rndis_request *request; ++ u32 inresult_size = *result_size; ++ struct rndis_query_request *query; ++ struct rndis_query_complete *query_complete; ++ int ret = 0; ++ int t; ++ ++ if (!result) ++ return -EINVAL; ++ ++ *result_size = 0; ++ request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG, ++ RNDIS_MESSAGE_SIZE(struct rndis_query_request)); ++ if (!request) { ++ ret = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* Setup the rndis query */ ++ query = &request->request_msg.msg.query_req; ++ query->oid = oid; ++ query->info_buf_offset = sizeof(struct rndis_query_request); ++ query->info_buflen = 0; ++ query->dev_vc_handle = 0; ++ ++ ret = rndis_filter_send_request(dev, request); ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ /* Copy the response back */ ++ query_complete = &request->response_msg.msg.query_complete; ++ ++ if (query_complete->info_buflen > inresult_size) { ++ ret = -1; ++ goto cleanup; ++ } ++ ++ memcpy(result, ++ (void *)((unsigned long)query_complete + ++ query_complete->info_buf_offset), ++ query_complete->info_buflen); ++ ++ *result_size = query_complete->info_buflen; ++ ++cleanup: ++ if (request) ++ put_rndis_request(dev, request); ++ ++ return ret; ++} ++ ++static int rndis_filter_query_device_mac(struct rndis_device *dev) ++{ ++ u32 size = ETH_ALEN; ++ ++ return rndis_filter_query_device(dev, ++ RNDIS_OID_802_3_PERMANENT_ADDRESS, ++ dev->hw_mac_adr, &size); ++} ++ ++static int rndis_filter_query_device_link_status(struct rndis_device *dev) ++{ ++ u32 size = sizeof(u32); ++ u32 link_status; ++ int ret; ++ ++ ret = rndis_filter_query_device(dev, ++ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, ++ &link_status, &size); ++ dev->link_state = (link_status != 0) ? true : false; ++ ++ return ret; ++} ++ ++static int rndis_filter_set_packet_filter(struct rndis_device *dev, ++ u32 new_filter) ++{ ++ struct rndis_request *request; ++ struct rndis_set_request *set; ++ struct rndis_set_complete *set_complete; ++ u32 status; ++ int ret, t; ++ struct net_device *ndev; ++ ++ ndev = dev->net_dev->ndev; ++ ++ request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, ++ RNDIS_MESSAGE_SIZE(struct rndis_set_request) + ++ sizeof(u32)); ++ if (!request) { ++ ret = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* Setup the rndis set */ ++ set = &request->request_msg.msg.set_req; ++ set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; ++ set->info_buflen = sizeof(u32); ++ set->info_buf_offset = sizeof(struct rndis_set_request); ++ ++ memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), ++ &new_filter, sizeof(u32)); ++ ++ ret = rndis_filter_send_request(dev, request); ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ ++ if (t == 0) { ++ netdev_err(ndev, ++ "timeout before we got a set response...\n"); ++ /* ++ * We can't deallocate the request since we may still receive a ++ * send completion for it. ++ */ ++ goto exit; ++ } else { ++ set_complete = &request->response_msg.msg.set_complete; ++ status = set_complete->status; ++ } ++ ++cleanup: ++ if (request) ++ put_rndis_request(dev, request); ++exit: ++ return ret; ++} ++ ++ ++static int rndis_filter_init_device(struct rndis_device *dev) ++{ ++ struct rndis_request *request; ++ struct rndis_initialize_request *init; ++ struct rndis_initialize_complete *init_complete; ++ u32 status; ++ int ret, t; ++ ++ request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, ++ RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); ++ if (!request) { ++ ret = -ENOMEM; ++ goto cleanup; ++ } ++ ++ /* Setup the rndis set */ ++ init = &request->request_msg.msg.init_req; ++ init->major_ver = RNDIS_MAJOR_VERSION; ++ init->minor_ver = RNDIS_MINOR_VERSION; ++ /* FIXME: Use 1536 - rounded ethernet frame size */ ++ init->max_xfer_size = 2048; ++ ++ dev->state = RNDIS_DEV_INITIALIZING; ++ ++ ret = rndis_filter_send_request(dev, request); ++ if (ret != 0) { ++ dev->state = RNDIS_DEV_UNINITIALIZED; ++ goto cleanup; ++ } ++ ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ init_complete = &request->response_msg.msg.init_complete; ++ status = init_complete->status; ++ if (status == RNDIS_STATUS_SUCCESS) { ++ dev->state = RNDIS_DEV_INITIALIZED; ++ ret = 0; ++ } else { ++ dev->state = RNDIS_DEV_UNINITIALIZED; ++ ret = -EINVAL; ++ } ++ ++cleanup: ++ if (request) ++ put_rndis_request(dev, request); ++ ++ return ret; ++} ++ ++static void rndis_filter_halt_device(struct rndis_device *dev) ++{ ++ struct rndis_request *request; ++ struct rndis_halt_request *halt; ++ ++ /* Attempt to do a rndis device halt */ ++ request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG, ++ RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); ++ if (!request) ++ goto cleanup; ++ ++ /* Setup the rndis set */ ++ halt = &request->request_msg.msg.halt_req; ++ halt->req_id = atomic_inc_return(&dev->new_req_id); ++ ++ /* Ignore return since this msg is optional. */ ++ rndis_filter_send_request(dev, request); ++ ++ dev->state = RNDIS_DEV_UNINITIALIZED; ++ ++cleanup: ++ if (request) ++ put_rndis_request(dev, request); ++ return; ++} ++ ++static int rndis_filter_open_device(struct rndis_device *dev) ++{ ++ int ret; ++ ++ if (dev->state != RNDIS_DEV_INITIALIZED) ++ return 0; ++ ++ ret = rndis_filter_set_packet_filter(dev, ++ NDIS_PACKET_TYPE_BROADCAST | ++ NDIS_PACKET_TYPE_ALL_MULTICAST | ++ NDIS_PACKET_TYPE_DIRECTED); ++ if (ret == 0) ++ dev->state = RNDIS_DEV_DATAINITIALIZED; ++ ++ return ret; ++} ++ ++static int rndis_filter_close_device(struct rndis_device *dev) ++{ ++ int ret; ++ ++ if (dev->state != RNDIS_DEV_DATAINITIALIZED) ++ return 0; ++ ++ ret = rndis_filter_set_packet_filter(dev, 0); ++ if (ret == 0) ++ dev->state = RNDIS_DEV_INITIALIZED; ++ ++ return ret; ++} ++ ++int rndis_filter_device_add(struct hv_device *dev, ++ void *additional_info) ++{ ++ int ret; ++ struct netvsc_device *net_device; ++ struct rndis_device *rndis_device; ++ struct netvsc_device_info *device_info = additional_info; ++ ++ rndis_device = get_rndis_device(); ++ if (!rndis_device) ++ return -ENODEV; ++ ++ /* ++ * Let the inner driver handle this first to create the netvsc channel ++ * NOTE! Once the channel is created, we may get a receive callback ++ * (RndisFilterOnReceive()) before this call is completed ++ */ ++ ret = netvsc_device_add(dev, additional_info); ++ if (ret != 0) { ++ kfree(rndis_device); ++ return ret; ++ } ++ ++ ++ /* Initialize the rndis device */ ++ net_device = hv_get_drvdata(dev); ++ ++ net_device->extension = rndis_device; ++ rndis_device->net_dev = net_device; ++ ++ /* Send the rndis initialization message */ ++ ret = rndis_filter_init_device(rndis_device); ++ if (ret != 0) { ++ /* ++ * TODO: If rndis init failed, we will need to shut down the ++ * channel ++ */ ++ } ++ ++ /* Get the mac address */ ++ ret = rndis_filter_query_device_mac(rndis_device); ++ if (ret != 0) { ++ /* ++ * TODO: shutdown rndis device and the channel ++ */ ++ } ++ ++ memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); ++ ++ rndis_filter_query_device_link_status(rndis_device); ++ ++ device_info->link_state = rndis_device->link_state; ++ ++ dev_info(&dev->device, "Device MAC %pM link state %s\n", ++ rndis_device->hw_mac_adr, ++ device_info->link_state ? "down" : "up"); ++ ++ return ret; ++} ++ ++void rndis_filter_device_remove(struct hv_device *dev) ++{ ++ struct netvsc_device *net_dev = hv_get_drvdata(dev); ++ struct rndis_device *rndis_dev = net_dev->extension; ++ ++ /* Halt and release the rndis device */ ++ rndis_filter_halt_device(rndis_dev); ++ ++ kfree(rndis_dev); ++ net_dev->extension = NULL; ++ ++ netvsc_device_remove(dev); ++} ++ ++ ++int rndis_filter_open(struct hv_device *dev) ++{ ++ struct netvsc_device *net_device = hv_get_drvdata(dev); ++ ++ if (!net_device) ++ return -EINVAL; ++ ++ return rndis_filter_open_device(net_device->extension); ++} ++ ++int rndis_filter_close(struct hv_device *dev) ++{ ++ struct netvsc_device *netDevice = hv_get_drvdata(dev); ++ ++ if (!netDevice) ++ return -EINVAL; ++ ++ return rndis_filter_close_device(netDevice->extension); ++} ++ ++int rndis_filter_send(struct hv_device *dev, ++ struct hv_netvsc_packet *pkt) ++{ ++ int ret; ++ struct rndis_filter_packet *filterPacket; ++ struct rndis_message *rndisMessage; ++ struct rndis_packet *rndisPacket; ++ u32 rndisMessageSize; ++ ++ /* Add the rndis header */ ++ filterPacket = (struct rndis_filter_packet *)pkt->extension; ++ ++ memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); ++ ++ rndisMessage = &filterPacket->msg; ++ rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); ++ ++ rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; ++ rndisMessage->msg_len = pkt->total_data_buflen + ++ rndisMessageSize; ++ ++ rndisPacket = &rndisMessage->msg.pkt; ++ rndisPacket->data_offset = sizeof(struct rndis_packet); ++ rndisPacket->data_len = pkt->total_data_buflen; ++ ++ pkt->is_data_pkt = true; ++ pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; ++ pkt->page_buf[0].offset = ++ (unsigned long)rndisMessage & (PAGE_SIZE-1); ++ pkt->page_buf[0].len = rndisMessageSize; ++ ++ /* Save the packet send completion and context */ ++ filterPacket->completion = pkt->completion.send.send_completion; ++ filterPacket->completion_ctx = ++ pkt->completion.send.send_completion_ctx; ++ ++ /* Use ours */ ++ pkt->completion.send.send_completion = rndis_filter_send_completion; ++ pkt->completion.send.send_completion_ctx = filterPacket; ++ ++ ret = netvsc_send(dev, pkt); ++ if (ret != 0) { ++ /* ++ * Reset the completion to originals to allow retries from ++ * above ++ */ ++ pkt->completion.send.send_completion = ++ filterPacket->completion; ++ pkt->completion.send.send_completion_ctx = ++ filterPacket->completion_ctx; ++ } ++ ++ return ret; ++} ++ ++static void rndis_filter_send_completion(void *ctx) ++{ ++ struct rndis_filter_packet *filterPacket = ctx; ++ ++ /* Pass it back to the original handler */ ++ filterPacket->completion(filterPacket->completion_ctx); ++} ++ ++ ++static void rndis_filter_send_request_completion(void *ctx) ++{ ++ /* Noop */ ++} +diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig +index 6c0dc30..60ac479 100644 +--- a/drivers/staging/hv/Kconfig ++++ b/drivers/staging/hv/Kconfig +@@ -3,9 +3,3 @@ config HYPERV_STORAGE + 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 HYPERV && NET +- help +- Select this option to enable the Hyper-V virtual network driver. +diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile +index fbe9a42..af95a6b 100644 +--- a/drivers/staging/hv/Makefile ++++ b/drivers/staging/hv/Makefile +@@ -1,5 +1,3 @@ + obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o +-obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o + + hv_storvsc-y := storvsc_drv.o +-hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO +index ed4d636..fd080cb 100644 +--- a/drivers/staging/hv/TODO ++++ b/drivers/staging/hv/TODO +@@ -1,5 +1,4 @@ + TODO: +- - audit the network driver + - audit the scsi driver + + Please send patches for this code to Greg Kroah-Hartman , +diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h +deleted file mode 100644 +index ac1ec84..0000000 +--- a/drivers/staging/hv/hyperv_net.h ++++ /dev/null +@@ -1,1058 +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_NET_H +-#define _HYPERV_NET_H +- +-#include +-#include +- +-/* Fwd declaration */ +-struct hv_netvsc_packet; +- +-/* Represent the xfer page packet which contains 1 or more netvsc packet */ +-struct xferpage_packet { +- struct list_head list_ent; +- +- /* # of netvsc packets this xfer packet contains */ +- u32 count; +-}; +- +-/* The number of pages which are enough to cover jumbo frame buffer. */ +-#define NETVSC_PACKET_MAXPAGE 4 +- +-/* +- * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame +- * within the RNDIS +- */ +-struct hv_netvsc_packet { +- /* Bookkeeping stuff */ +- struct list_head list_ent; +- +- struct hv_device *device; +- bool is_data_pkt; +- +- /* +- * Valid only for receives when we break a xfer page packet +- * into multiple netvsc packets +- */ +- struct xferpage_packet *xfer_page_pkt; +- +- union { +- struct { +- u64 recv_completion_tid; +- void *recv_completion_ctx; +- void (*recv_completion)(void *context); +- } recv; +- struct { +- u64 send_completion_tid; +- void *send_completion_ctx; +- void (*send_completion)(void *context); +- } send; +- } completion; +- +- /* This points to the memory after page_buf */ +- void *extension; +- +- u32 total_data_buflen; +- /* Points to the send/receive buffer where the ethernet frame is */ +- u32 page_buf_cnt; +- struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; +-}; +- +-struct netvsc_device_info { +- unsigned char mac_adr[6]; +- bool link_state; /* 0 - link up, 1 - link down */ +- int ring_size; +-}; +- +-/* Interface */ +-int netvsc_device_add(struct hv_device *device, void *additional_info); +-int netvsc_device_remove(struct hv_device *device); +-int netvsc_send(struct hv_device *device, +- struct hv_netvsc_packet *packet); +-void netvsc_linkstatus_callback(struct hv_device *device_obj, +- unsigned int status); +-int netvsc_recv_callback(struct hv_device *device_obj, +- struct hv_netvsc_packet *packet); +-int rndis_filter_open(struct hv_device *dev); +-int rndis_filter_close(struct hv_device *dev); +-int rndis_filter_device_add(struct hv_device *dev, +- void *additional_info); +-void rndis_filter_device_remove(struct hv_device *dev); +-int rndis_filter_receive(struct hv_device *dev, +- struct hv_netvsc_packet *pkt); +- +- +- +-int rndis_filter_send(struct hv_device *dev, +- struct hv_netvsc_packet *pkt); +- +-#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) +- +-#define NVSP_PROTOCOL_VERSION_1 2 +-#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 +-#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 +- +-enum { +- NVSP_MSG_TYPE_NONE = 0, +- +- /* Init Messages */ +- NVSP_MSG_TYPE_INIT = 1, +- NVSP_MSG_TYPE_INIT_COMPLETE = 2, +- +- NVSP_VERSION_MSG_START = 100, +- +- /* Version 1 Messages */ +- NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START, +- +- NVSP_MSG1_TYPE_SEND_RECV_BUF, +- NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, +- NVSP_MSG1_TYPE_REVOKE_RECV_BUF, +- +- NVSP_MSG1_TYPE_SEND_SEND_BUF, +- NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, +- NVSP_MSG1_TYPE_REVOKE_SEND_BUF, +- +- NVSP_MSG1_TYPE_SEND_RNDIS_PKT, +- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, +- +- /* +- * This should be set to the number of messages for the version with +- * the maximum number of messages. +- */ +- NVSP_NUM_MSG_PER_VERSION = 9, +-}; +- +-enum { +- NVSP_STAT_NONE = 0, +- NVSP_STAT_SUCCESS, +- NVSP_STAT_FAIL, +- NVSP_STAT_PROTOCOL_TOO_NEW, +- NVSP_STAT_PROTOCOL_TOO_OLD, +- NVSP_STAT_INVALID_RNDIS_PKT, +- NVSP_STAT_BUSY, +- NVSP_STAT_MAX, +-}; +- +-struct nvsp_message_header { +- u32 msg_type; +-}; +- +-/* Init Messages */ +- +-/* +- * This message is used by the VSC to initialize the channel after the channels +- * has been opened. This message should never include anything other then +- * versioning (i.e. this message will be the same for ever). +- */ +-struct nvsp_message_init { +- u32 min_protocol_ver; +- u32 max_protocol_ver; +-} __packed; +- +-/* +- * This message is used by the VSP to complete the initialization of the +- * channel. This message should never include anything other then versioning +- * (i.e. this message will be the same for ever). +- */ +-struct nvsp_message_init_complete { +- u32 negotiated_protocol_ver; +- u32 max_mdl_chain_len; +- u32 status; +-} __packed; +- +-union nvsp_message_init_uber { +- struct nvsp_message_init init; +- struct nvsp_message_init_complete init_complete; +-} __packed; +- +-/* Version 1 Messages */ +- +-/* +- * This message is used by the VSC to send the NDIS version to the VSP. The VSP +- * can use this information when handling OIDs sent by the VSC. +- */ +-struct nvsp_1_message_send_ndis_version { +- u32 ndis_major_ver; +- u32 ndis_minor_ver; +-} __packed; +- +-/* +- * This message is used by the VSC to send a receive buffer to the VSP. The VSP +- * can then use the receive buffer to send data to the VSC. +- */ +-struct nvsp_1_message_send_receive_buffer { +- u32 gpadl_handle; +- u16 id; +-} __packed; +- +-struct nvsp_1_receive_buffer_section { +- u32 offset; +- u32 sub_alloc_size; +- u32 num_sub_allocs; +- u32 end_offset; +-} __packed; +- +-/* +- * This message is used by the VSP to acknowledge a receive buffer send by the +- * VSC. This message must be sent by the VSP before the VSP uses the receive +- * buffer. +- */ +-struct nvsp_1_message_send_receive_buffer_complete { +- u32 status; +- u32 num_sections; +- +- /* +- * The receive buffer is split into two parts, a large suballocation +- * section and a small suballocation section. These sections are then +- * suballocated by a certain size. +- */ +- +- /* +- * For example, the following break up of the receive buffer has 6 +- * large suballocations and 10 small suballocations. +- */ +- +- /* +- * | Large Section | | Small Section | +- * ------------------------------------------------------------ +- * | | | | | | | | | | | | | | | | | | +- * | | +- * LargeOffset SmallOffset +- */ +- +- struct nvsp_1_receive_buffer_section sections[1]; +-} __packed; +- +-/* +- * This message is sent by the VSC to revoke the receive buffer. After the VSP +- * completes this transaction, the vsp should never use the receive buffer +- * again. +- */ +-struct nvsp_1_message_revoke_receive_buffer { +- u16 id; +-}; +- +-/* +- * This message is used by the VSC to send a send buffer to the VSP. The VSC +- * can then use the send buffer to send data to the VSP. +- */ +-struct nvsp_1_message_send_send_buffer { +- u32 gpadl_handle; +- u16 id; +-} __packed; +- +-/* +- * This message is used by the VSP to acknowledge a send buffer sent by the +- * VSC. This message must be sent by the VSP before the VSP uses the sent +- * buffer. +- */ +-struct nvsp_1_message_send_send_buffer_complete { +- u32 status; +- +- /* +- * The VSC gets to choose the size of the send buffer and the VSP gets +- * to choose the sections size of the buffer. This was done to enable +- * dynamic reconfigurations when the cost of GPA-direct buffers +- * decreases. +- */ +- u32 section_size; +-} __packed; +- +-/* +- * This message is sent by the VSC to revoke the send buffer. After the VSP +- * completes this transaction, the vsp should never use the send buffer again. +- */ +-struct nvsp_1_message_revoke_send_buffer { +- u16 id; +-}; +- +-/* +- * This message is used by both the VSP and the VSC to send a RNDIS message to +- * the opposite channel endpoint. +- */ +-struct nvsp_1_message_send_rndis_packet { +- /* +- * This field is specified by RNIDS. They assume there's two different +- * channels of communication. However, the Network VSP only has one. +- * Therefore, the channel travels with the RNDIS packet. +- */ +- u32 channel_type; +- +- /* +- * This field is used to send part or all of the data through a send +- * buffer. This values specifies an index into the send buffer. If the +- * index is 0xFFFFFFFF, then the send buffer is not being used and all +- * of the data was sent through other VMBus mechanisms. +- */ +- u32 send_buf_section_index; +- u32 send_buf_section_size; +-} __packed; +- +-/* +- * This message is used by both the VSP and the VSC to complete a RNDIS message +- * to the opposite channel endpoint. At this point, the initiator of this +- * message cannot use any resources associated with the original RNDIS packet. +- */ +-struct nvsp_1_message_send_rndis_packet_complete { +- u32 status; +-}; +- +-union nvsp_1_message_uber { +- struct nvsp_1_message_send_ndis_version send_ndis_ver; +- +- struct nvsp_1_message_send_receive_buffer send_recv_buf; +- struct nvsp_1_message_send_receive_buffer_complete +- send_recv_buf_complete; +- struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf; +- +- struct nvsp_1_message_send_send_buffer send_send_buf; +- struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete; +- struct nvsp_1_message_revoke_send_buffer revoke_send_buf; +- +- struct nvsp_1_message_send_rndis_packet send_rndis_pkt; +- struct nvsp_1_message_send_rndis_packet_complete +- send_rndis_pkt_complete; +-} __packed; +- +-union nvsp_all_messages { +- union nvsp_message_init_uber init_msg; +- union nvsp_1_message_uber v1_msg; +-} __packed; +- +-/* ALL Messages */ +-struct nvsp_message { +- struct nvsp_message_header hdr; +- union nvsp_all_messages msg; +-} __packed; +- +- +- +- +-/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ +-/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ +- +-#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ +- +-#define NETVSC_RECEIVE_BUFFER_ID 0xcafe +- +-#define NETVSC_RECEIVE_SG_COUNT 1 +- +-/* Preallocated receive packets */ +-#define NETVSC_RECEIVE_PACKETLIST_COUNT 256 +- +-#define NETVSC_PACKET_SIZE 2048 +- +-/* Per netvsc channel-specific */ +-struct netvsc_device { +- struct hv_device *dev; +- +- atomic_t num_outstanding_sends; +- bool destroy; +- /* +- * List of free preallocated hv_netvsc_packet to represent receive +- * packet +- */ +- struct list_head recv_pkt_list; +- spinlock_t recv_pkt_list_lock; +- +- /* Receive buffer allocated by us but manages by NetVSP */ +- void *recv_buf; +- u32 recv_buf_size; +- u32 recv_buf_gpadl_handle; +- u32 recv_section_cnt; +- struct nvsp_1_receive_buffer_section *recv_section; +- +- /* Used for NetVSP initialization protocol */ +- struct completion channel_init_wait; +- struct nvsp_message channel_init_pkt; +- +- struct nvsp_message revoke_packet; +- /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ +- +- struct net_device *ndev; +- +- /* Holds rndis device info */ +- void *extension; +-}; +- +- +-/* Status codes */ +- +- +-#ifndef STATUS_SUCCESS +-#define STATUS_SUCCESS (0x00000000L) +-#endif +- +-#ifndef STATUS_UNSUCCESSFUL +-#define STATUS_UNSUCCESSFUL (0xC0000001L) +-#endif +- +-#ifndef STATUS_PENDING +-#define STATUS_PENDING (0x00000103L) +-#endif +- +-#ifndef STATUS_INSUFFICIENT_RESOURCES +-#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL) +-#endif +- +-#ifndef STATUS_BUFFER_OVERFLOW +-#define STATUS_BUFFER_OVERFLOW (0x80000005L) +-#endif +- +-#ifndef STATUS_NOT_SUPPORTED +-#define STATUS_NOT_SUPPORTED (0xC00000BBL) +-#endif +- +-#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS) +-#define RNDIS_STATUS_PENDING (STATUS_PENDING) +-#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L) +-#define RNDIS_STATUS_NOT_COPIED (0x00010002L) +-#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L) +-#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L) +- +-#define RNDIS_STATUS_ONLINE (0x40010003L) +-#define RNDIS_STATUS_RESET_START (0x40010004L) +-#define RNDIS_STATUS_RESET_END (0x40010005L) +-#define RNDIS_STATUS_RING_STATUS (0x40010006L) +-#define RNDIS_STATUS_CLOSED (0x40010007L) +-#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L) +-#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L) +-#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL) +-#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL) +-#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL) +-#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL) +-#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL) +-#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL) +-#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L) +-#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L) +-#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L) +-#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION +-#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L) +- +-#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L) +-#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L) +-#define RNDIS_STATUS_HARD_ERRORS (0x80010004L) +-#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW) +- +-#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL) +-#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES) +-#define RNDIS_STATUS_CLOSING (0xC0010002L) +-#define RNDIS_STATUS_BAD_VERSION (0xC0010004L) +-#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L) +-#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L) +-#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L) +-#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L) +-#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L) +-#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL) +-#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL) +-#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL) +-#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL) +-#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL) +-#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED) +-#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL) +-#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L) +-#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L) +-#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L) +-#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L) +-#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L) +-#define RNDIS_STATUS_INVALID_DATA (0xC0010015L) +-#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L) +-#define RNDIS_STATUS_INVALID_OID (0xC0010017L) +-#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L) +-#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L) +-#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL) +-#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL) +-#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL) +-#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL) +-#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL) +-#define RNDIS_STATUS_NO_CABLE (0xC001001FL) +- +-#define RNDIS_STATUS_INVALID_SAP (0xC0010020L) +-#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L) +-#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L) +-#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L) +-#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L) +-#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L) +-#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L) +-#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L) +-#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L) +-#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L) +- +-#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L) +- +-/* Object Identifiers used by NdisRequest Query/Set Information */ +-/* General Objects */ +-#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 +-#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 +-#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 +-#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 +-#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +-#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +-#define RNDIS_OID_GEN_LINK_SPEED 0x00010107 +-#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +-#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +-#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +-#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +-#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C +-#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D +-#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +-#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +-#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 +-#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +-#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 +-#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 +-#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +-#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +-#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +-#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +-#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +-#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A +-#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +- +-#define RNDIS_OID_GEN_XMIT_OK 0x00020101 +-#define RNDIS_OID_GEN_RCV_OK 0x00020102 +-#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 +-#define RNDIS_OID_GEN_RCV_ERROR 0x00020104 +-#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 +- +-#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +-#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +-#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +-#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +-#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +-#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +-#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +-#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +-#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +-#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +-#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +-#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +- +-#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D +-#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +- +-#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F +-#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 +- +-/* These are connection-oriented general OIDs. */ +-/* These replace the above OIDs for connection-oriented media. */ +-#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 +-#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 +-#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 +-#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 +-#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 +-#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 +-#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 +-#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 +-#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 +-#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A +-#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B +-#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C +-#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D +- +-#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 +-#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 +- +-/* These are connection-oriented statistics OIDs. */ +-#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 +-#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 +-#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 +-#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 +-#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 +- +- +-#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 +-#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 +-#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 +-#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 +-#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 +-#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 +- +-/* These are objects for Connection-oriented media call-managers. */ +-#define RNDIS_OID_CO_ADD_PVC 0xFF000001 +-#define RNDIS_OID_CO_DELETE_PVC 0xFF000002 +-#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 +-#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 +-#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 +-#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 +-#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 +-#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 +-#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 +- +-/* 802.3 Objects (Ethernet) */ +-#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 +-#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 +-#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 +-#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +-#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 +- +-#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 +- +-#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +-#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 +-#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +- +-#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 +-#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +-#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 +-#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 +-#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +-#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +-#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 +- +-/* Remote NDIS message types */ +-#define REMOTE_NDIS_PACKET_MSG 0x00000001 +-#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002 +-#define REMOTE_NDIS_HALT_MSG 0x00000003 +-#define REMOTE_NDIS_QUERY_MSG 0x00000004 +-#define REMOTE_NDIS_SET_MSG 0x00000005 +-#define REMOTE_NDIS_RESET_MSG 0x00000006 +-#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007 +-#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008 +- +-#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001 +-#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002 +-#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005 +-#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 +-#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007 +- +-/* Remote NDIS message completion types */ +-#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002 +-#define REMOTE_NDIS_QUERY_CMPLT 0x80000004 +-#define REMOTE_NDIS_SET_CMPLT 0x80000005 +-#define REMOTE_NDIS_RESET_CMPLT 0x80000006 +-#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008 +- +-#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001 +-#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002 +-#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 +-#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 +- +-/* +- * Reserved message type for private communication between lower-layer host +- * driver and remote device, if necessary. +- */ +-#define REMOTE_NDIS_BUS_MSG 0xff000001 +- +-/* Defines for DeviceFlags in struct rndis_initialize_complete */ +-#define RNDIS_DF_CONNECTIONLESS 0x00000001 +-#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +-#define RNDIS_DF_RAW_DATA 0x00000004 +- +-/* Remote NDIS medium types. */ +-#define RNDIS_MEDIUM_802_3 0x00000000 +-#define RNDIS_MEDIUM_802_5 0x00000001 +-#define RNDIS_MEDIUM_FDDI 0x00000002 +-#define RNDIS_MEDIUM_WAN 0x00000003 +-#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 +-#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 +-#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 +-#define RNDIS_MEDIUM_ATM 0x00000008 +-#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 +-#define RNDIS_MEDIUM_IRDA 0x0000000a +-#define RNDIS_MEDIUM_CO_WAN 0x0000000b +-/* Not a real medium, defined as an upper-bound */ +-#define RNDIS_MEDIUM_MAX 0x0000000d +- +- +-/* Remote NDIS medium connection states. */ +-#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 +-#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 +- +-/* Remote NDIS version numbers */ +-#define RNDIS_MAJOR_VERSION 0x00000001 +-#define RNDIS_MINOR_VERSION 0x00000000 +- +- +-/* NdisInitialize message */ +-struct rndis_initialize_request { +- u32 req_id; +- u32 major_ver; +- u32 minor_ver; +- u32 max_xfer_size; +-}; +- +-/* Response to NdisInitialize */ +-struct rndis_initialize_complete { +- u32 req_id; +- u32 status; +- u32 major_ver; +- u32 minor_ver; +- u32 dev_flags; +- u32 medium; +- u32 max_pkt_per_msg; +- u32 max_xfer_size; +- u32 pkt_alignment_factor; +- u32 af_list_offset; +- u32 af_list_size; +-}; +- +-/* Call manager devices only: Information about an address family */ +-/* supported by the device is appended to the response to NdisInitialize. */ +-struct rndis_co_address_family { +- u32 address_family; +- u32 major_ver; +- u32 minor_ver; +-}; +- +-/* NdisHalt message */ +-struct rndis_halt_request { +- u32 req_id; +-}; +- +-/* NdisQueryRequest message */ +-struct rndis_query_request { +- u32 req_id; +- u32 oid; +- u32 info_buflen; +- u32 info_buf_offset; +- u32 dev_vc_handle; +-}; +- +-/* Response to NdisQueryRequest */ +-struct rndis_query_complete { +- u32 req_id; +- u32 status; +- u32 info_buflen; +- u32 info_buf_offset; +-}; +- +-/* NdisSetRequest message */ +-struct rndis_set_request { +- u32 req_id; +- u32 oid; +- u32 info_buflen; +- u32 info_buf_offset; +- u32 dev_vc_handle; +-}; +- +-/* Response to NdisSetRequest */ +-struct rndis_set_complete { +- u32 req_id; +- u32 status; +-}; +- +-/* NdisReset message */ +-struct rndis_reset_request { +- u32 reserved; +-}; +- +-/* Response to NdisReset */ +-struct rndis_reset_complete { +- u32 status; +- u32 addressing_reset; +-}; +- +-/* NdisMIndicateStatus message */ +-struct rndis_indicate_status { +- u32 status; +- u32 status_buflen; +- u32 status_buf_offset; +-}; +- +-/* Diagnostic information passed as the status buffer in */ +-/* struct rndis_indicate_status messages signifying error conditions. */ +-struct rndis_diagnostic_info { +- u32 diag_status; +- u32 error_offset; +-}; +- +-/* NdisKeepAlive message */ +-struct rndis_keepalive_request { +- u32 req_id; +-}; +- +-/* Response to NdisKeepAlive */ +-struct rndis_keepalive_complete { +- u32 req_id; +- u32 status; +-}; +- +-/* +- * Data message. All Offset fields contain byte offsets from the beginning of +- * struct rndis_packet. All Length fields are in bytes. VcHandle is set +- * to 0 for connectionless data, otherwise it contains the VC handle. +- */ +-struct rndis_packet { +- u32 data_offset; +- u32 data_len; +- u32 oob_data_offset; +- u32 oob_data_len; +- u32 num_oob_data_elements; +- u32 per_pkt_info_offset; +- u32 per_pkt_info_len; +- u32 vc_handle; +- u32 reserved; +-}; +- +-/* Optional Out of Band data associated with a Data message. */ +-struct rndis_oobd { +- u32 size; +- u32 type; +- u32 class_info_offset; +-}; +- +-/* Packet extension field contents associated with a Data message. */ +-struct rndis_per_packet_info { +- u32 size; +- u32 type; +- u32 per_pkt_info_offset; +-}; +- +-/* Format of Information buffer passed in a SetRequest for the OID */ +-/* OID_GEN_RNDIS_CONFIG_PARAMETER. */ +-struct rndis_config_parameter_info { +- u32 parameter_name_offset; +- u32 parameter_name_length; +- u32 parameter_type; +- u32 parameter_value_offset; +- u32 parameter_value_length; +-}; +- +-/* Values for ParameterType in struct rndis_config_parameter_info */ +-#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0 +-#define RNDIS_CONFIG_PARAM_TYPE_STRING 2 +- +-/* CONDIS Miniport messages for connection oriented devices */ +-/* that do not implement a call manager. */ +- +-/* CoNdisMiniportCreateVc message */ +-struct rcondis_mp_create_vc { +- u32 req_id; +- u32 ndis_vc_handle; +-}; +- +-/* Response to CoNdisMiniportCreateVc */ +-struct rcondis_mp_create_vc_complete { +- u32 req_id; +- u32 dev_vc_handle; +- u32 status; +-}; +- +-/* CoNdisMiniportDeleteVc message */ +-struct rcondis_mp_delete_vc { +- u32 req_id; +- u32 dev_vc_handle; +-}; +- +-/* Response to CoNdisMiniportDeleteVc */ +-struct rcondis_mp_delete_vc_complete { +- u32 req_id; +- u32 status; +-}; +- +-/* CoNdisMiniportQueryRequest message */ +-struct rcondis_mp_query_request { +- u32 req_id; +- u32 request_type; +- u32 oid; +- u32 dev_vc_handle; +- u32 info_buflen; +- u32 info_buf_offset; +-}; +- +-/* CoNdisMiniportSetRequest message */ +-struct rcondis_mp_set_request { +- u32 req_id; +- u32 request_type; +- u32 oid; +- u32 dev_vc_handle; +- u32 info_buflen; +- u32 info_buf_offset; +-}; +- +-/* CoNdisIndicateStatus message */ +-struct rcondis_indicate_status { +- u32 ndis_vc_handle; +- u32 status; +- u32 status_buflen; +- u32 status_buf_offset; +-}; +- +-/* CONDIS Call/VC parameters */ +-struct rcondis_specific_parameters { +- u32 parameter_type; +- u32 parameter_length; +- u32 parameter_lffset; +-}; +- +-struct rcondis_media_parameters { +- u32 flags; +- u32 reserved1; +- u32 reserved2; +- struct rcondis_specific_parameters media_specific; +-}; +- +-struct rndis_flowspec { +- u32 token_rate; +- u32 token_bucket_size; +- u32 peak_bandwidth; +- u32 latency; +- u32 delay_variation; +- u32 service_type; +- u32 max_sdu_size; +- u32 minimum_policed_size; +-}; +- +-struct rcondis_call_manager_parameters { +- struct rndis_flowspec transmit; +- struct rndis_flowspec receive; +- struct rcondis_specific_parameters call_mgr_specific; +-}; +- +-/* CoNdisMiniportActivateVc message */ +-struct rcondis_mp_activate_vc_request { +- u32 req_id; +- u32 flags; +- u32 dev_vc_handle; +- u32 media_params_offset; +- u32 media_params_length; +- u32 call_mgr_params_offset; +- u32 call_mgr_params_length; +-}; +- +-/* Response to CoNdisMiniportActivateVc */ +-struct rcondis_mp_activate_vc_complete { +- u32 req_id; +- u32 status; +-}; +- +-/* CoNdisMiniportDeactivateVc message */ +-struct rcondis_mp_deactivate_vc_request { +- u32 req_id; +- u32 flags; +- u32 dev_vc_handle; +-}; +- +-/* Response to CoNdisMiniportDeactivateVc */ +-struct rcondis_mp_deactivate_vc_complete { +- u32 req_id; +- u32 status; +-}; +- +- +-/* union with all of the RNDIS messages */ +-union rndis_message_container { +- struct rndis_packet pkt; +- struct rndis_initialize_request init_req; +- struct rndis_halt_request halt_req; +- struct rndis_query_request query_req; +- struct rndis_set_request set_req; +- struct rndis_reset_request reset_req; +- struct rndis_keepalive_request keep_alive_req; +- struct rndis_indicate_status indicate_status; +- struct rndis_initialize_complete init_complete; +- struct rndis_query_complete query_complete; +- struct rndis_set_complete set_complete; +- struct rndis_reset_complete reset_complete; +- struct rndis_keepalive_complete keep_alive_complete; +- struct rcondis_mp_create_vc co_miniport_create_vc; +- struct rcondis_mp_delete_vc co_miniport_delete_vc; +- struct rcondis_indicate_status co_indicate_status; +- struct rcondis_mp_activate_vc_request co_miniport_activate_vc; +- struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc; +- struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete; +- struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete; +- struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete; +- struct rcondis_mp_deactivate_vc_complete +- co_miniport_deactivate_vc_complete; +-}; +- +-/* Remote NDIS message format */ +-struct rndis_message { +- u32 ndis_msg_type; +- +- /* Total length of this message, from the beginning */ +- /* of the sruct rndis_message, in bytes. */ +- u32 msg_len; +- +- /* Actual message */ +- union rndis_message_container msg; +-}; +- +- +-struct rndis_filter_packet { +- void *completion_ctx; +- void (*completion)(void *context); +- struct rndis_message msg; +-}; +- +-/* Handy macros */ +- +-/* get the size of an RNDIS message. Pass in the message type, */ +-/* struct rndis_set_request, struct rndis_packet for example */ +-#define RNDIS_MESSAGE_SIZE(msg) \ +- (sizeof(msg) + (sizeof(struct rndis_message) - \ +- sizeof(union rndis_message_container))) +- +-/* get pointer to info buffer with message pointer */ +-#define MESSAGE_TO_INFO_BUFFER(msg) \ +- (((unsigned char *)(msg)) + msg->info_buf_offset) +- +-/* get pointer to status buffer with message pointer */ +-#define MESSAGE_TO_STATUS_BUFFER(msg) \ +- (((unsigned char *)(msg)) + msg->status_buf_offset) +- +-/* get pointer to OOBD buffer with message pointer */ +-#define MESSAGE_TO_OOBD_BUFFER(msg) \ +- (((unsigned char *)(msg)) + msg->oob_data_offset) +- +-/* get pointer to data buffer with message pointer */ +-#define MESSAGE_TO_DATA_BUFFER(msg) \ +- (((unsigned char *)(msg)) + msg->per_pkt_info_offset) +- +-/* get pointer to contained message from NDIS_MESSAGE pointer */ +-#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \ +- ((void *) &rndis_msg->msg) +- +-/* get pointer to contained message from NDIS_MESSAGE pointer */ +-#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \ +- ((void *) rndis_msg) +- +- +-#define __struct_bcount(x) +- +- +- +-#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ +- sizeof(union rndis_message_container)) +- +-#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +-#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +-#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +-#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +-#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +-#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +-#define NDIS_PACKET_TYPE_SMT 0x00000040 +-#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +-#define NDIS_PACKET_TYPE_GROUP 0x00000100 +-#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +-#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +-#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 +- +- +- +-#endif /* _HYPERV_NET_H */ +diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c +deleted file mode 100644 +index 28e69a6..0000000 +--- a/drivers/staging/hv/netvsc.c ++++ /dev/null +@@ -1,939 +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_net.h" +- +- +-static struct netvsc_device *alloc_net_device(struct hv_device *device) +-{ +- struct netvsc_device *net_device; +- struct net_device *ndev = hv_get_drvdata(device); +- +- net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); +- if (!net_device) +- return NULL; +- +- +- net_device->destroy = false; +- net_device->dev = device; +- net_device->ndev = ndev; +- +- hv_set_drvdata(device, net_device); +- return net_device; +-} +- +-static struct netvsc_device *get_outbound_net_device(struct hv_device *device) +-{ +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(device); +- if (net_device && net_device->destroy) +- net_device = NULL; +- +- return net_device; +-} +- +-static struct netvsc_device *get_inbound_net_device(struct hv_device *device) +-{ +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(device); +- +- if (!net_device) +- goto get_in_err; +- +- if (net_device->destroy && +- atomic_read(&net_device->num_outstanding_sends) == 0) +- net_device = NULL; +- +-get_in_err: +- return net_device; +-} +- +- +-static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) +-{ +- struct nvsp_message *revoke_packet; +- int ret = 0; +- struct net_device *ndev = net_device->ndev; +- +- /* +- * If we got a section count, it means we received a +- * SendReceiveBufferComplete msg (ie sent +- * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need +- * to send a revoke msg here +- */ +- if (net_device->recv_section_cnt) { +- /* Send the revoke receive buffer */ +- revoke_packet = &net_device->revoke_packet; +- memset(revoke_packet, 0, sizeof(struct nvsp_message)); +- +- revoke_packet->hdr.msg_type = +- NVSP_MSG1_TYPE_REVOKE_RECV_BUF; +- revoke_packet->msg.v1_msg. +- revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; +- +- ret = vmbus_sendpacket(net_device->dev->channel, +- revoke_packet, +- sizeof(struct nvsp_message), +- (unsigned long)revoke_packet, +- VM_PKT_DATA_INBAND, 0); +- /* +- * If we failed here, we might as well return and +- * have a leak rather than continue and a bugchk +- */ +- if (ret != 0) { +- netdev_err(ndev, "unable to send " +- "revoke receive buffer to netvsp\n"); +- return ret; +- } +- } +- +- /* Teardown the gpadl on the vsp end */ +- if (net_device->recv_buf_gpadl_handle) { +- ret = vmbus_teardown_gpadl(net_device->dev->channel, +- net_device->recv_buf_gpadl_handle); +- +- /* If we failed here, we might as well return and have a leak +- * rather than continue and a bugchk +- */ +- if (ret != 0) { +- netdev_err(ndev, +- "unable to teardown receive buffer's gpadl\n"); +- return ret; +- } +- net_device->recv_buf_gpadl_handle = 0; +- } +- +- if (net_device->recv_buf) { +- /* Free up the receive buffer */ +- free_pages((unsigned long)net_device->recv_buf, +- get_order(net_device->recv_buf_size)); +- net_device->recv_buf = NULL; +- } +- +- if (net_device->recv_section) { +- net_device->recv_section_cnt = 0; +- kfree(net_device->recv_section); +- net_device->recv_section = NULL; +- } +- +- return ret; +-} +- +-static int netvsc_init_recv_buf(struct hv_device *device) +-{ +- int ret = 0; +- int t; +- struct netvsc_device *net_device; +- struct nvsp_message *init_packet; +- struct net_device *ndev; +- +- net_device = get_outbound_net_device(device); +- if (!net_device) +- return -ENODEV; +- ndev = net_device->ndev; +- +- net_device->recv_buf = +- (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, +- get_order(net_device->recv_buf_size)); +- if (!net_device->recv_buf) { +- netdev_err(ndev, "unable to allocate receive " +- "buffer of size %d\n", net_device->recv_buf_size); +- ret = -ENOMEM; +- goto cleanup; +- } +- +- /* +- * Establish the gpadl handle for this buffer on this +- * channel. Note: This call uses the vmbus connection rather +- * than the channel to establish the gpadl handle. +- */ +- ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf, +- net_device->recv_buf_size, +- &net_device->recv_buf_gpadl_handle); +- if (ret != 0) { +- netdev_err(ndev, +- "unable to establish receive buffer's gpadl\n"); +- goto cleanup; +- } +- +- +- /* Notify the NetVsp of the gpadl handle */ +- init_packet = &net_device->channel_init_pkt; +- +- memset(init_packet, 0, sizeof(struct nvsp_message)); +- +- init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF; +- init_packet->msg.v1_msg.send_recv_buf. +- gpadl_handle = net_device->recv_buf_gpadl_handle; +- init_packet->msg.v1_msg. +- send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; +- +- /* Send the gpadl notification request */ +- ret = vmbus_sendpacket(device->channel, init_packet, +- sizeof(struct nvsp_message), +- (unsigned long)init_packet, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) { +- netdev_err(ndev, +- "unable to send receive buffer's gpadl to netvsp\n"); +- goto cleanup; +- } +- +- t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); +- BUG_ON(t == 0); +- +- +- /* Check the response */ +- if (init_packet->msg.v1_msg. +- send_recv_buf_complete.status != NVSP_STAT_SUCCESS) { +- netdev_err(ndev, "Unable to complete receive buffer " +- "initialization with NetVsp - status %d\n", +- init_packet->msg.v1_msg. +- send_recv_buf_complete.status); +- ret = -EINVAL; +- goto cleanup; +- } +- +- /* Parse the response */ +- +- net_device->recv_section_cnt = init_packet->msg. +- v1_msg.send_recv_buf_complete.num_sections; +- +- net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections, +- net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section), +- GFP_KERNEL); +- if (net_device->recv_section == NULL) { +- ret = -EINVAL; +- goto cleanup; +- } +- +- /* +- * For 1st release, there should only be 1 section that represents the +- * entire receive buffer +- */ +- if (net_device->recv_section_cnt != 1 || +- net_device->recv_section->offset != 0) { +- ret = -EINVAL; +- goto cleanup; +- } +- +- goto exit; +- +-cleanup: +- netvsc_destroy_recv_buf(net_device); +- +-exit: +- return ret; +-} +- +- +-static int netvsc_connect_vsp(struct hv_device *device) +-{ +- int ret, t; +- struct netvsc_device *net_device; +- struct nvsp_message *init_packet; +- int ndis_version; +- struct net_device *ndev; +- +- net_device = get_outbound_net_device(device); +- if (!net_device) +- return -ENODEV; +- ndev = net_device->ndev; +- +- init_packet = &net_device->channel_init_pkt; +- +- memset(init_packet, 0, sizeof(struct nvsp_message)); +- init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; +- init_packet->msg.init_msg.init.min_protocol_ver = +- NVSP_MIN_PROTOCOL_VERSION; +- init_packet->msg.init_msg.init.max_protocol_ver = +- NVSP_MAX_PROTOCOL_VERSION; +- +- /* Send the init request */ +- ret = vmbus_sendpacket(device->channel, init_packet, +- sizeof(struct nvsp_message), +- (unsigned long)init_packet, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); +- +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- if (init_packet->msg.init_msg.init_complete.status != +- NVSP_STAT_SUCCESS) { +- ret = -EINVAL; +- goto cleanup; +- } +- +- if (init_packet->msg.init_msg.init_complete. +- negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { +- ret = -EPROTO; +- goto cleanup; +- } +- /* Send the ndis version */ +- memset(init_packet, 0, sizeof(struct nvsp_message)); +- +- ndis_version = 0x00050000; +- +- init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; +- init_packet->msg.v1_msg. +- send_ndis_ver.ndis_major_ver = +- (ndis_version & 0xFFFF0000) >> 16; +- init_packet->msg.v1_msg. +- send_ndis_ver.ndis_minor_ver = +- ndis_version & 0xFFFF; +- +- /* Send the init request */ +- ret = vmbus_sendpacket(device->channel, init_packet, +- sizeof(struct nvsp_message), +- (unsigned long)init_packet, +- VM_PKT_DATA_INBAND, 0); +- if (ret != 0) +- goto cleanup; +- +- /* Post the big receive buffer to NetVSP */ +- ret = netvsc_init_recv_buf(device); +- +-cleanup: +- return ret; +-} +- +-static void netvsc_disconnect_vsp(struct netvsc_device *net_device) +-{ +- netvsc_destroy_recv_buf(net_device); +-} +- +-/* +- * netvsc_device_remove - Callback when the root bus device is removed +- */ +-int netvsc_device_remove(struct hv_device *device) +-{ +- struct netvsc_device *net_device; +- struct hv_netvsc_packet *netvsc_packet, *pos; +- unsigned long flags; +- +- net_device = hv_get_drvdata(device); +- spin_lock_irqsave(&device->channel->inbound_lock, flags); +- net_device->destroy = true; +- spin_unlock_irqrestore(&device->channel->inbound_lock, flags); +- +- /* Wait for all send completions */ +- while (atomic_read(&net_device->num_outstanding_sends)) { +- dev_info(&device->device, +- "waiting for %d requests to complete...\n", +- atomic_read(&net_device->num_outstanding_sends)); +- udelay(100); +- } +- +- netvsc_disconnect_vsp(net_device); +- +- /* +- * Since we have already drained, we don't need to busy wait +- * as was done in final_release_stor_device() +- * Note that we cannot set the ext pointer to NULL until +- * we have drained - to drain the outgoing packets, we need to +- * allow incoming packets. +- */ +- +- spin_lock_irqsave(&device->channel->inbound_lock, flags); +- hv_set_drvdata(device, NULL); +- spin_unlock_irqrestore(&device->channel->inbound_lock, flags); +- +- /* +- * At this point, no one should be accessing net_device +- * except in here +- */ +- dev_notice(&device->device, "net device safe to remove\n"); +- +- /* Now, we can close the channel safely */ +- vmbus_close(device->channel); +- +- /* Release all resources */ +- list_for_each_entry_safe(netvsc_packet, pos, +- &net_device->recv_pkt_list, list_ent) { +- list_del(&netvsc_packet->list_ent); +- kfree(netvsc_packet); +- } +- +- kfree(net_device); +- return 0; +-} +- +-static void netvsc_send_completion(struct hv_device *device, +- struct vmpacket_descriptor *packet) +-{ +- struct netvsc_device *net_device; +- struct nvsp_message *nvsp_packet; +- struct hv_netvsc_packet *nvsc_packet; +- struct net_device *ndev; +- +- net_device = get_inbound_net_device(device); +- if (!net_device) +- return; +- ndev = net_device->ndev; +- +- nvsp_packet = (struct nvsp_message *)((unsigned long)packet + +- (packet->offset8 << 3)); +- +- if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) || +- (nvsp_packet->hdr.msg_type == +- NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || +- (nvsp_packet->hdr.msg_type == +- NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) { +- /* Copy the response back */ +- memcpy(&net_device->channel_init_pkt, nvsp_packet, +- sizeof(struct nvsp_message)); +- complete(&net_device->channel_init_wait); +- } else if (nvsp_packet->hdr.msg_type == +- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { +- /* Get the send context */ +- nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) +- packet->trans_id; +- +- /* Notify the layer above us */ +- nvsc_packet->completion.send.send_completion( +- nvsc_packet->completion.send.send_completion_ctx); +- +- atomic_dec(&net_device->num_outstanding_sends); +- } else { +- netdev_err(ndev, "Unknown send completion packet type- " +- "%d received!!\n", nvsp_packet->hdr.msg_type); +- } +- +-} +- +-int netvsc_send(struct hv_device *device, +- struct hv_netvsc_packet *packet) +-{ +- struct netvsc_device *net_device; +- int ret = 0; +- struct nvsp_message sendMessage; +- struct net_device *ndev; +- +- net_device = get_outbound_net_device(device); +- if (!net_device) +- return -ENODEV; +- ndev = net_device->ndev; +- +- sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; +- if (packet->is_data_pkt) { +- /* 0 is RMC_DATA; */ +- sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0; +- } else { +- /* 1 is RMC_CONTROL; */ +- sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1; +- } +- +- /* Not using send buffer section */ +- sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index = +- 0xFFFFFFFF; +- sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0; +- +- if (packet->page_buf_cnt) { +- ret = vmbus_sendpacket_pagebuffer(device->channel, +- packet->page_buf, +- packet->page_buf_cnt, +- &sendMessage, +- sizeof(struct nvsp_message), +- (unsigned long)packet); +- } else { +- ret = vmbus_sendpacket(device->channel, &sendMessage, +- sizeof(struct nvsp_message), +- (unsigned long)packet, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- +- } +- +- if (ret != 0) +- netdev_err(ndev, "Unable to send packet %p ret %d\n", +- packet, ret); +- else +- atomic_inc(&net_device->num_outstanding_sends); +- +- return ret; +-} +- +-static void netvsc_send_recv_completion(struct hv_device *device, +- u64 transaction_id) +-{ +- struct nvsp_message recvcompMessage; +- int retries = 0; +- int ret; +- struct net_device *ndev; +- struct netvsc_device *net_device = hv_get_drvdata(device); +- +- ndev = net_device->ndev; +- +- recvcompMessage.hdr.msg_type = +- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; +- +- /* FIXME: Pass in the status */ +- recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = +- NVSP_STAT_SUCCESS; +- +-retry_send_cmplt: +- /* Send the completion */ +- ret = vmbus_sendpacket(device->channel, &recvcompMessage, +- sizeof(struct nvsp_message), transaction_id, +- VM_PKT_COMP, 0); +- if (ret == 0) { +- /* success */ +- /* no-op */ +- } else if (ret == -EAGAIN) { +- /* no more room...wait a bit and attempt to retry 3 times */ +- retries++; +- netdev_err(ndev, "unable to send receive completion pkt" +- " (tid %llx)...retrying %d\n", transaction_id, retries); +- +- if (retries < 4) { +- udelay(100); +- goto retry_send_cmplt; +- } else { +- netdev_err(ndev, "unable to send receive " +- "completion pkt (tid %llx)...give up retrying\n", +- transaction_id); +- } +- } else { +- netdev_err(ndev, "unable to send receive " +- "completion pkt - %llx\n", transaction_id); +- } +-} +- +-/* Send a receive completion packet to RNDIS device (ie NetVsp) */ +-static void netvsc_receive_completion(void *context) +-{ +- struct hv_netvsc_packet *packet = context; +- struct hv_device *device = (struct hv_device *)packet->device; +- struct netvsc_device *net_device; +- u64 transaction_id = 0; +- bool fsend_receive_comp = false; +- unsigned long flags; +- struct net_device *ndev; +- +- /* +- * Even though it seems logical to do a GetOutboundNetDevice() here to +- * send out receive completion, we are using GetInboundNetDevice() +- * since we may have disable outbound traffic already. +- */ +- net_device = get_inbound_net_device(device); +- if (!net_device) +- return; +- ndev = net_device->ndev; +- +- /* Overloading use of the lock. */ +- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); +- +- packet->xfer_page_pkt->count--; +- +- /* +- * Last one in the line that represent 1 xfer page packet. +- * Return the xfer page packet itself to the freelist +- */ +- if (packet->xfer_page_pkt->count == 0) { +- fsend_receive_comp = true; +- transaction_id = packet->completion.recv.recv_completion_tid; +- list_add_tail(&packet->xfer_page_pkt->list_ent, +- &net_device->recv_pkt_list); +- +- } +- +- /* Put the packet back */ +- list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); +- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); +- +- /* Send a receive completion for the xfer page packet */ +- if (fsend_receive_comp) +- netvsc_send_recv_completion(device, transaction_id); +- +-} +- +-static void netvsc_receive(struct hv_device *device, +- struct vmpacket_descriptor *packet) +-{ +- struct netvsc_device *net_device; +- struct vmtransfer_page_packet_header *vmxferpage_packet; +- struct nvsp_message *nvsp_packet; +- struct hv_netvsc_packet *netvsc_packet = NULL; +- unsigned long start; +- unsigned long end, end_virtual; +- /* struct netvsc_driver *netvscDriver; */ +- struct xferpage_packet *xferpage_packet = NULL; +- int i, j; +- int count = 0, bytes_remain = 0; +- unsigned long flags; +- struct net_device *ndev; +- +- LIST_HEAD(listHead); +- +- net_device = get_inbound_net_device(device); +- if (!net_device) +- return; +- ndev = net_device->ndev; +- +- /* +- * All inbound packets other than send completion should be xfer page +- * packet +- */ +- if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) { +- netdev_err(ndev, "Unknown packet type received - %d\n", +- packet->type); +- return; +- } +- +- nvsp_packet = (struct nvsp_message *)((unsigned long)packet + +- (packet->offset8 << 3)); +- +- /* Make sure this is a valid nvsp packet */ +- if (nvsp_packet->hdr.msg_type != +- NVSP_MSG1_TYPE_SEND_RNDIS_PKT) { +- netdev_err(ndev, "Unknown nvsp packet type received-" +- " %d\n", nvsp_packet->hdr.msg_type); +- return; +- } +- +- vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet; +- +- if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) { +- netdev_err(ndev, "Invalid xfer page set id - " +- "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID, +- vmxferpage_packet->xfer_pageset_id); +- return; +- } +- +- /* +- * Grab free packets (range count + 1) to represent this xfer +- * page packet. +1 to represent the xfer page packet itself. +- * We grab it here so that we know exactly how many we can +- * fulfil +- */ +- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); +- while (!list_empty(&net_device->recv_pkt_list)) { +- list_move_tail(net_device->recv_pkt_list.next, &listHead); +- if (++count == vmxferpage_packet->range_cnt + 1) +- break; +- } +- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); +- +- /* +- * We need at least 2 netvsc pkts (1 to represent the xfer +- * page and at least 1 for the range) i.e. we can handled +- * some of the xfer page packet ranges... +- */ +- if (count < 2) { +- netdev_err(ndev, "Got only %d netvsc pkt...needed " +- "%d pkts. Dropping this xfer page packet completely!\n", +- count, vmxferpage_packet->range_cnt + 1); +- +- /* Return it to the freelist */ +- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); +- for (i = count; i != 0; i--) { +- list_move_tail(listHead.next, +- &net_device->recv_pkt_list); +- } +- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, +- flags); +- +- netvsc_send_recv_completion(device, +- vmxferpage_packet->d.trans_id); +- +- return; +- } +- +- /* Remove the 1st packet to represent the xfer page packet itself */ +- xferpage_packet = (struct xferpage_packet *)listHead.next; +- list_del(&xferpage_packet->list_ent); +- +- /* This is how much we can satisfy */ +- xferpage_packet->count = count - 1; +- +- if (xferpage_packet->count != vmxferpage_packet->range_cnt) { +- netdev_err(ndev, "Needed %d netvsc pkts to satisfy " +- "this xfer page...got %d\n", +- vmxferpage_packet->range_cnt, xferpage_packet->count); +- } +- +- /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ +- for (i = 0; i < (count - 1); i++) { +- netvsc_packet = (struct hv_netvsc_packet *)listHead.next; +- list_del(&netvsc_packet->list_ent); +- +- /* Initialize the netvsc packet */ +- netvsc_packet->xfer_page_pkt = xferpage_packet; +- netvsc_packet->completion.recv.recv_completion = +- netvsc_receive_completion; +- netvsc_packet->completion.recv.recv_completion_ctx = +- netvsc_packet; +- netvsc_packet->device = device; +- /* Save this so that we can send it back */ +- netvsc_packet->completion.recv.recv_completion_tid = +- vmxferpage_packet->d.trans_id; +- +- netvsc_packet->total_data_buflen = +- vmxferpage_packet->ranges[i].byte_count; +- netvsc_packet->page_buf_cnt = 1; +- +- netvsc_packet->page_buf[0].len = +- vmxferpage_packet->ranges[i].byte_count; +- +- start = virt_to_phys((void *)((unsigned long)net_device-> +- recv_buf + vmxferpage_packet->ranges[i].byte_offset)); +- +- netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT; +- end_virtual = (unsigned long)net_device->recv_buf +- + vmxferpage_packet->ranges[i].byte_offset +- + vmxferpage_packet->ranges[i].byte_count - 1; +- end = virt_to_phys((void *)end_virtual); +- +- /* Calculate the page relative offset */ +- netvsc_packet->page_buf[0].offset = +- vmxferpage_packet->ranges[i].byte_offset & +- (PAGE_SIZE - 1); +- if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { +- /* Handle frame across multiple pages: */ +- netvsc_packet->page_buf[0].len = +- (netvsc_packet->page_buf[0].pfn << +- PAGE_SHIFT) +- + PAGE_SIZE - start; +- bytes_remain = netvsc_packet->total_data_buflen - +- netvsc_packet->page_buf[0].len; +- for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { +- netvsc_packet->page_buf[j].offset = 0; +- if (bytes_remain <= PAGE_SIZE) { +- netvsc_packet->page_buf[j].len = +- bytes_remain; +- bytes_remain = 0; +- } else { +- netvsc_packet->page_buf[j].len = +- PAGE_SIZE; +- bytes_remain -= PAGE_SIZE; +- } +- netvsc_packet->page_buf[j].pfn = +- virt_to_phys((void *)(end_virtual - +- bytes_remain)) >> PAGE_SHIFT; +- netvsc_packet->page_buf_cnt++; +- if (bytes_remain == 0) +- break; +- } +- } +- +- /* Pass it to the upper layer */ +- rndis_filter_receive(device, netvsc_packet); +- +- netvsc_receive_completion(netvsc_packet-> +- completion.recv.recv_completion_ctx); +- } +- +-} +- +-static void netvsc_channel_cb(void *context) +-{ +- int ret; +- struct hv_device *device = context; +- struct netvsc_device *net_device; +- u32 bytes_recvd; +- u64 request_id; +- unsigned char *packet; +- struct vmpacket_descriptor *desc; +- unsigned char *buffer; +- int bufferlen = NETVSC_PACKET_SIZE; +- struct net_device *ndev; +- +- packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char), +- GFP_ATOMIC); +- if (!packet) +- return; +- buffer = packet; +- +- net_device = get_inbound_net_device(device); +- if (!net_device) +- goto out; +- ndev = net_device->ndev; +- +- do { +- ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, +- &bytes_recvd, &request_id); +- if (ret == 0) { +- if (bytes_recvd > 0) { +- desc = (struct vmpacket_descriptor *)buffer; +- switch (desc->type) { +- case VM_PKT_COMP: +- netvsc_send_completion(device, desc); +- break; +- +- case VM_PKT_DATA_USING_XFER_PAGES: +- netvsc_receive(device, desc); +- break; +- +- default: +- netdev_err(ndev, +- "unhandled packet type %d, " +- "tid %llx len %d\n", +- desc->type, request_id, +- bytes_recvd); +- break; +- } +- +- /* reset */ +- if (bufferlen > NETVSC_PACKET_SIZE) { +- kfree(buffer); +- buffer = packet; +- bufferlen = NETVSC_PACKET_SIZE; +- } +- } else { +- /* reset */ +- if (bufferlen > NETVSC_PACKET_SIZE) { +- kfree(buffer); +- buffer = packet; +- bufferlen = NETVSC_PACKET_SIZE; +- } +- +- break; +- } +- } else if (ret == -ENOBUFS) { +- /* Handle large packet */ +- buffer = kmalloc(bytes_recvd, GFP_ATOMIC); +- if (buffer == NULL) { +- /* Try again next time around */ +- netdev_err(ndev, +- "unable to allocate buffer of size " +- "(%d)!!\n", bytes_recvd); +- break; +- } +- +- bufferlen = bytes_recvd; +- } +- } while (1); +- +-out: +- kfree(buffer); +- return; +-} +- +-/* +- * netvsc_device_add - Callback when the device belonging to this +- * driver is added +- */ +-int netvsc_device_add(struct hv_device *device, void *additional_info) +-{ +- int ret = 0; +- int i; +- int ring_size = +- ((struct netvsc_device_info *)additional_info)->ring_size; +- struct netvsc_device *net_device; +- struct hv_netvsc_packet *packet, *pos; +- struct net_device *ndev; +- +- net_device = alloc_net_device(device); +- if (!net_device) { +- ret = -ENOMEM; +- goto cleanup; +- } +- +- /* +- * Coming into this function, struct net_device * is +- * registered as the driver private data. +- * In alloc_net_device(), we register struct netvsc_device * +- * as the driver private data and stash away struct net_device * +- * in struct netvsc_device *. +- */ +- ndev = net_device->ndev; +- +- /* Initialize the NetVSC channel extension */ +- net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; +- spin_lock_init(&net_device->recv_pkt_list_lock); +- +- INIT_LIST_HEAD(&net_device->recv_pkt_list); +- +- for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { +- packet = kzalloc(sizeof(struct hv_netvsc_packet) + +- (NETVSC_RECEIVE_SG_COUNT * +- sizeof(struct hv_page_buffer)), GFP_KERNEL); +- if (!packet) +- break; +- +- list_add_tail(&packet->list_ent, +- &net_device->recv_pkt_list); +- } +- init_completion(&net_device->channel_init_wait); +- +- /* Open the channel */ +- ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, +- ring_size * PAGE_SIZE, NULL, 0, +- netvsc_channel_cb, device); +- +- if (ret != 0) { +- netdev_err(ndev, "unable to open channel: %d\n", ret); +- goto cleanup; +- } +- +- /* Channel is opened */ +- pr_info("hv_netvsc channel opened successfully\n"); +- +- /* Connect with the NetVsp */ +- ret = netvsc_connect_vsp(device); +- if (ret != 0) { +- netdev_err(ndev, +- "unable to connect to NetVSP - %d\n", ret); +- goto close; +- } +- +- return ret; +- +-close: +- /* Now, we can close the channel safely */ +- vmbus_close(device->channel); +- +-cleanup: +- +- if (net_device) { +- list_for_each_entry_safe(packet, pos, +- &net_device->recv_pkt_list, +- list_ent) { +- list_del(&packet->list_ent); +- kfree(packet); +- } +- +- kfree(net_device); +- } +- +- return ret; +-} +diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c +deleted file mode 100644 +index 93b0e91..0000000 +--- a/drivers/staging/hv/netvsc_drv.c ++++ /dev/null +@@ -1,456 +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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "hyperv_net.h" +- +-struct net_device_context { +- /* point back to our device context */ +- struct hv_device *device_ctx; +- atomic_t avail; +- struct delayed_work dwork; +-}; +- +- +-#define PACKET_PAGES_LOWATER 8 +-/* Need this many pages to handle worst case fragmented packet */ +-#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2) +- +-static int ring_size = 128; +-module_param(ring_size, int, S_IRUGO); +-MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); +- +-/* no-op so the netdev core doesn't return -EINVAL when modifying the the +- * multicast address list in SIOCADDMULTI. hv is setup to get all multicast +- * when it calls RndisFilterOnOpen() */ +-static void netvsc_set_multicast_list(struct net_device *net) +-{ +-} +- +-static int netvsc_open(struct net_device *net) +-{ +- struct net_device_context *net_device_ctx = netdev_priv(net); +- struct hv_device *device_obj = net_device_ctx->device_ctx; +- int ret = 0; +- +- /* Open up the device */ +- ret = rndis_filter_open(device_obj); +- if (ret != 0) { +- netdev_err(net, "unable to open device (ret %d).\n", ret); +- return ret; +- } +- +- netif_start_queue(net); +- +- return ret; +-} +- +-static int netvsc_close(struct net_device *net) +-{ +- struct net_device_context *net_device_ctx = netdev_priv(net); +- struct hv_device *device_obj = net_device_ctx->device_ctx; +- int ret; +- +- netif_stop_queue(net); +- +- ret = rndis_filter_close(device_obj); +- if (ret != 0) +- netdev_err(net, "unable to close device (ret %d).\n", ret); +- +- return ret; +-} +- +-static void netvsc_xmit_completion(void *context) +-{ +- struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; +- struct sk_buff *skb = (struct sk_buff *) +- (unsigned long)packet->completion.send.send_completion_tid; +- +- kfree(packet); +- +- if (skb) { +- struct net_device *net = skb->dev; +- struct net_device_context *net_device_ctx = netdev_priv(net); +- unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2; +- +- dev_kfree_skb_any(skb); +- +- atomic_add(num_pages, &net_device_ctx->avail); +- if (atomic_read(&net_device_ctx->avail) >= +- PACKET_PAGES_HIWATER) +- netif_wake_queue(net); +- } +-} +- +-static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) +-{ +- struct net_device_context *net_device_ctx = netdev_priv(net); +- struct hv_netvsc_packet *packet; +- int ret; +- unsigned int i, num_pages; +- +- /* Add 1 for skb->data and additional one for RNDIS */ +- num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; +- if (num_pages > atomic_read(&net_device_ctx->avail)) +- return NETDEV_TX_BUSY; +- +- /* Allocate a netvsc packet based on # of frags. */ +- packet = kzalloc(sizeof(struct hv_netvsc_packet) + +- (num_pages * sizeof(struct hv_page_buffer)) + +- sizeof(struct rndis_filter_packet), GFP_ATOMIC); +- if (!packet) { +- /* out of memory, drop packet */ +- netdev_err(net, "unable to allocate hv_netvsc_packet\n"); +- +- dev_kfree_skb(skb); +- net->stats.tx_dropped++; +- return NETDEV_TX_BUSY; +- } +- +- packet->extension = (void *)(unsigned long)packet + +- sizeof(struct hv_netvsc_packet) + +- (num_pages * sizeof(struct hv_page_buffer)); +- +- /* Setup the rndis header */ +- packet->page_buf_cnt = num_pages; +- +- /* Initialize it from the skb */ +- packet->total_data_buflen = skb->len; +- +- /* Start filling in the page buffers starting after RNDIS buffer. */ +- packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; +- packet->page_buf[1].offset +- = (unsigned long)skb->data & (PAGE_SIZE - 1); +- packet->page_buf[1].len = skb_headlen(skb); +- +- /* Additional fragments are after SKB data */ +- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +- const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; +- +- packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f)); +- packet->page_buf[i+2].offset = f->page_offset; +- packet->page_buf[i+2].len = skb_frag_size(f); +- } +- +- /* Set the completion routine */ +- packet->completion.send.send_completion = netvsc_xmit_completion; +- packet->completion.send.send_completion_ctx = packet; +- packet->completion.send.send_completion_tid = (unsigned long)skb; +- +- ret = rndis_filter_send(net_device_ctx->device_ctx, +- packet); +- if (ret == 0) { +- net->stats.tx_bytes += skb->len; +- net->stats.tx_packets++; +- +- atomic_sub(num_pages, &net_device_ctx->avail); +- if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER) +- netif_stop_queue(net); +- } else { +- /* we are shutting down or bus overloaded, just drop packet */ +- net->stats.tx_dropped++; +- kfree(packet); +- dev_kfree_skb_any(skb); +- } +- +- return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; +-} +- +-/* +- * netvsc_linkstatus_callback - Link up/down notification +- */ +-void netvsc_linkstatus_callback(struct hv_device *device_obj, +- unsigned int status) +-{ +- struct net_device *net; +- struct net_device_context *ndev_ctx; +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(device_obj); +- net = net_device->ndev; +- +- if (!net) { +- netdev_err(net, "got link status but net device " +- "not initialized yet\n"); +- return; +- } +- +- if (status == 1) { +- netif_carrier_on(net); +- netif_wake_queue(net); +- ndev_ctx = netdev_priv(net); +- schedule_delayed_work(&ndev_ctx->dwork, 0); +- schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); +- } else { +- netif_carrier_off(net); +- netif_stop_queue(net); +- } +-} +- +-/* +- * netvsc_recv_callback - Callback when we receive a packet from the +- * "wire" on the specified device. +- */ +-int netvsc_recv_callback(struct hv_device *device_obj, +- struct hv_netvsc_packet *packet) +-{ +- struct net_device *net = dev_get_drvdata(&device_obj->device); +- struct sk_buff *skb; +- void *data; +- int i; +- unsigned long flags; +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(device_obj); +- net = net_device->ndev; +- +- if (!net) { +- netdev_err(net, "got receive callback but net device" +- " not initialized yet\n"); +- return 0; +- } +- +- /* Allocate a skb - TODO direct I/O to pages? */ +- skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); +- if (unlikely(!skb)) { +- ++net->stats.rx_dropped; +- return 0; +- } +- +- /* for kmap_atomic */ +- local_irq_save(flags); +- +- /* +- * Copy to skb. This copy is needed here since the memory pointed by +- * hv_netvsc_packet cannot be deallocated +- */ +- for (i = 0; i < packet->page_buf_cnt; i++) { +- data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn), +- KM_IRQ1); +- data = (void *)(unsigned long)data + +- packet->page_buf[i].offset; +- +- memcpy(skb_put(skb, packet->page_buf[i].len), data, +- packet->page_buf[i].len); +- +- kunmap_atomic((void *)((unsigned long)data - +- packet->page_buf[i].offset), KM_IRQ1); +- } +- +- local_irq_restore(flags); +- +- skb->protocol = eth_type_trans(skb, net); +- skb->ip_summed = CHECKSUM_NONE; +- +- net->stats.rx_packets++; +- net->stats.rx_bytes += skb->len; +- +- /* +- * Pass the skb back up. Network stack will deallocate the skb when it +- * is done. +- * TODO - use NAPI? +- */ +- netif_rx(skb); +- +- return 0; +-} +- +-static void netvsc_get_drvinfo(struct net_device *net, +- struct ethtool_drvinfo *info) +-{ +- strcpy(info->driver, "hv_netvsc"); +- strcpy(info->version, HV_DRV_VERSION); +- strcpy(info->fw_version, "N/A"); +-} +- +-static const struct ethtool_ops ethtool_ops = { +- .get_drvinfo = netvsc_get_drvinfo, +- .get_link = ethtool_op_get_link, +-}; +- +-static const struct net_device_ops device_ops = { +- .ndo_open = netvsc_open, +- .ndo_stop = netvsc_close, +- .ndo_start_xmit = netvsc_start_xmit, +- .ndo_set_rx_mode = netvsc_set_multicast_list, +- .ndo_change_mtu = eth_change_mtu, +- .ndo_validate_addr = eth_validate_addr, +- .ndo_set_mac_address = eth_mac_addr, +-}; +- +-/* +- * Send GARP packet to network peers after migrations. +- * After Quick Migration, the network is not immediately operational in the +- * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add +- * another netif_notify_peers() into a delayed work, otherwise GARP packet +- * will not be sent after quick migration, and cause network disconnection. +- */ +-static void netvsc_send_garp(struct work_struct *w) +-{ +- struct net_device_context *ndev_ctx; +- struct net_device *net; +- struct netvsc_device *net_device; +- +- ndev_ctx = container_of(w, struct net_device_context, dwork.work); +- net_device = hv_get_drvdata(ndev_ctx->device_ctx); +- net = net_device->ndev; +- netif_notify_peers(net); +-} +- +- +-static int netvsc_probe(struct hv_device *dev, +- const struct hv_vmbus_device_id *dev_id) +-{ +- struct net_device *net = NULL; +- struct net_device_context *net_device_ctx; +- struct netvsc_device_info device_info; +- int ret; +- +- net = alloc_etherdev(sizeof(struct net_device_context)); +- if (!net) +- return -ENOMEM; +- +- /* Set initial state */ +- netif_carrier_off(net); +- +- net_device_ctx = netdev_priv(net); +- net_device_ctx->device_ctx = dev; +- atomic_set(&net_device_ctx->avail, ring_size); +- hv_set_drvdata(dev, net); +- INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); +- +- net->netdev_ops = &device_ops; +- +- /* TODO: Add GSO and Checksum offload */ +- net->hw_features = NETIF_F_SG; +- net->features = NETIF_F_SG; +- +- SET_ETHTOOL_OPS(net, ðtool_ops); +- SET_NETDEV_DEV(net, &dev->device); +- +- ret = register_netdev(net); +- if (ret != 0) { +- pr_err("Unable to register netdev.\n"); +- free_netdev(net); +- goto out; +- } +- +- /* Notify the netvsc driver of the new device */ +- device_info.ring_size = ring_size; +- ret = rndis_filter_device_add(dev, &device_info); +- if (ret != 0) { +- netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); +- unregister_netdev(net); +- free_netdev(net); +- hv_set_drvdata(dev, NULL); +- return ret; +- } +- memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); +- +- netif_carrier_on(net); +- +-out: +- return ret; +-} +- +-static int netvsc_remove(struct hv_device *dev) +-{ +- struct net_device *net; +- struct net_device_context *ndev_ctx; +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(dev); +- net = net_device->ndev; +- +- if (net == NULL) { +- dev_err(&dev->device, "No net device to remove\n"); +- return 0; +- } +- +- ndev_ctx = netdev_priv(net); +- cancel_delayed_work_sync(&ndev_ctx->dwork); +- +- /* Stop outbound asap */ +- netif_stop_queue(net); +- +- unregister_netdev(net); +- +- /* +- * Call to the vsc driver to let it know that the device is being +- * removed +- */ +- rndis_filter_device_remove(dev); +- +- free_netdev(net); +- return 0; +-} +- +-static const struct hv_vmbus_device_id id_table[] = { +- /* Network guid */ +- { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, +- 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) }, +- { }, +-}; +- +-MODULE_DEVICE_TABLE(vmbus, id_table); +- +-/* The one and only one */ +-static struct hv_driver netvsc_drv = { +- .name = "netvsc", +- .id_table = id_table, +- .probe = netvsc_probe, +- .remove = netvsc_remove, +-}; +- +-static void __exit netvsc_drv_exit(void) +-{ +- vmbus_driver_unregister(&netvsc_drv); +-} +- +-static int __init netvsc_drv_init(void) +-{ +- return vmbus_driver_register(&netvsc_drv); +-} +- +-MODULE_LICENSE("GPL"); +-MODULE_VERSION(HV_DRV_VERSION); +-MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); +- +-module_init(netvsc_drv_init); +-module_exit(netvsc_drv_exit); +diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c +deleted file mode 100644 +index bafccb3..0000000 +--- a/drivers/staging/hv/rndis_filter.c ++++ /dev/null +@@ -1,855 +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 +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "hyperv_net.h" +- +- +-enum rndis_device_state { +- RNDIS_DEV_UNINITIALIZED = 0, +- RNDIS_DEV_INITIALIZING, +- RNDIS_DEV_INITIALIZED, +- RNDIS_DEV_DATAINITIALIZED, +-}; +- +-struct rndis_device { +- struct netvsc_device *net_dev; +- +- enum rndis_device_state state; +- bool link_state; +- atomic_t new_req_id; +- +- spinlock_t request_lock; +- struct list_head req_list; +- +- unsigned char hw_mac_adr[ETH_ALEN]; +-}; +- +-struct rndis_request { +- struct list_head list_ent; +- struct completion wait_event; +- +- /* +- * FIXME: We assumed a fixed size response here. If we do ever need to +- * handle a bigger response, we can either define a max response +- * message or add a response buffer variable above this field +- */ +- struct rndis_message response_msg; +- +- /* Simplify allocation by having a netvsc packet inline */ +- struct hv_netvsc_packet pkt; +- struct hv_page_buffer buf; +- /* FIXME: We assumed a fixed size request here. */ +- struct rndis_message request_msg; +-}; +- +-static void rndis_filter_send_completion(void *ctx); +- +-static void rndis_filter_send_request_completion(void *ctx); +- +- +- +-static struct rndis_device *get_rndis_device(void) +-{ +- struct rndis_device *device; +- +- device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); +- if (!device) +- return NULL; +- +- spin_lock_init(&device->request_lock); +- +- INIT_LIST_HEAD(&device->req_list); +- +- device->state = RNDIS_DEV_UNINITIALIZED; +- +- return device; +-} +- +-static struct rndis_request *get_rndis_request(struct rndis_device *dev, +- u32 msg_type, +- u32 msg_len) +-{ +- struct rndis_request *request; +- struct rndis_message *rndis_msg; +- struct rndis_set_request *set; +- unsigned long flags; +- +- request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); +- if (!request) +- return NULL; +- +- init_completion(&request->wait_event); +- +- rndis_msg = &request->request_msg; +- rndis_msg->ndis_msg_type = msg_type; +- rndis_msg->msg_len = msg_len; +- +- /* +- * Set the request id. This field is always after the rndis header for +- * request/response packet types so we just used the SetRequest as a +- * template +- */ +- set = &rndis_msg->msg.set_req; +- set->req_id = atomic_inc_return(&dev->new_req_id); +- +- /* Add to the request list */ +- spin_lock_irqsave(&dev->request_lock, flags); +- list_add_tail(&request->list_ent, &dev->req_list); +- spin_unlock_irqrestore(&dev->request_lock, flags); +- +- return request; +-} +- +-static void put_rndis_request(struct rndis_device *dev, +- struct rndis_request *req) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&dev->request_lock, flags); +- list_del(&req->list_ent); +- spin_unlock_irqrestore(&dev->request_lock, flags); +- +- kfree(req); +-} +- +-static void dump_rndis_message(struct hv_device *hv_dev, +- struct rndis_message *rndis_msg) +-{ +- struct net_device *netdev; +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(hv_dev); +- netdev = net_device->ndev; +- +- switch (rndis_msg->ndis_msg_type) { +- case REMOTE_NDIS_PACKET_MSG: +- netdev_dbg(netdev, "REMOTE_NDIS_PACKET_MSG (len %u, " +- "data offset %u data len %u, # oob %u, " +- "oob offset %u, oob len %u, pkt offset %u, " +- "pkt len %u\n", +- rndis_msg->msg_len, +- rndis_msg->msg.pkt.data_offset, +- rndis_msg->msg.pkt.data_len, +- rndis_msg->msg.pkt.num_oob_data_elements, +- rndis_msg->msg.pkt.oob_data_offset, +- rndis_msg->msg.pkt.oob_data_len, +- rndis_msg->msg.pkt.per_pkt_info_offset, +- rndis_msg->msg.pkt.per_pkt_info_len); +- break; +- +- case REMOTE_NDIS_INITIALIZE_CMPLT: +- netdev_dbg(netdev, "REMOTE_NDIS_INITIALIZE_CMPLT " +- "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " +- "device flags %d, max xfer size 0x%x, max pkts %u, " +- "pkt aligned %u)\n", +- rndis_msg->msg_len, +- rndis_msg->msg.init_complete.req_id, +- rndis_msg->msg.init_complete.status, +- rndis_msg->msg.init_complete.major_ver, +- rndis_msg->msg.init_complete.minor_ver, +- rndis_msg->msg.init_complete.dev_flags, +- rndis_msg->msg.init_complete.max_xfer_size, +- rndis_msg->msg.init_complete. +- max_pkt_per_msg, +- rndis_msg->msg.init_complete. +- pkt_alignment_factor); +- break; +- +- case REMOTE_NDIS_QUERY_CMPLT: +- netdev_dbg(netdev, "REMOTE_NDIS_QUERY_CMPLT " +- "(len %u, id 0x%x, status 0x%x, buf len %u, " +- "buf offset %u)\n", +- rndis_msg->msg_len, +- rndis_msg->msg.query_complete.req_id, +- rndis_msg->msg.query_complete.status, +- rndis_msg->msg.query_complete. +- info_buflen, +- rndis_msg->msg.query_complete. +- info_buf_offset); +- break; +- +- case REMOTE_NDIS_SET_CMPLT: +- netdev_dbg(netdev, +- "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)\n", +- rndis_msg->msg_len, +- rndis_msg->msg.set_complete.req_id, +- rndis_msg->msg.set_complete.status); +- break; +- +- case REMOTE_NDIS_INDICATE_STATUS_MSG: +- netdev_dbg(netdev, "REMOTE_NDIS_INDICATE_STATUS_MSG " +- "(len %u, status 0x%x, buf len %u, buf offset %u)\n", +- rndis_msg->msg_len, +- rndis_msg->msg.indicate_status.status, +- rndis_msg->msg.indicate_status.status_buflen, +- rndis_msg->msg.indicate_status.status_buf_offset); +- break; +- +- default: +- netdev_dbg(netdev, "0x%x (len %u)\n", +- rndis_msg->ndis_msg_type, +- rndis_msg->msg_len); +- break; +- } +-} +- +-static int rndis_filter_send_request(struct rndis_device *dev, +- struct rndis_request *req) +-{ +- int ret; +- struct hv_netvsc_packet *packet; +- +- /* Setup the packet to send it */ +- packet = &req->pkt; +- +- packet->is_data_pkt = false; +- packet->total_data_buflen = req->request_msg.msg_len; +- packet->page_buf_cnt = 1; +- +- packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> +- PAGE_SHIFT; +- packet->page_buf[0].len = req->request_msg.msg_len; +- packet->page_buf[0].offset = +- (unsigned long)&req->request_msg & (PAGE_SIZE - 1); +- +- packet->completion.send.send_completion_ctx = req;/* packet; */ +- packet->completion.send.send_completion = +- rndis_filter_send_request_completion; +- packet->completion.send.send_completion_tid = (unsigned long)dev; +- +- ret = netvsc_send(dev->net_dev->dev, packet); +- return ret; +-} +- +-static void rndis_filter_receive_response(struct rndis_device *dev, +- struct rndis_message *resp) +-{ +- struct rndis_request *request = NULL; +- bool found = false; +- unsigned long flags; +- struct net_device *ndev; +- +- ndev = dev->net_dev->ndev; +- +- spin_lock_irqsave(&dev->request_lock, flags); +- list_for_each_entry(request, &dev->req_list, list_ent) { +- /* +- * All request/response message contains RequestId as the 1st +- * field +- */ +- if (request->request_msg.msg.init_req.req_id +- == resp->msg.init_complete.req_id) { +- found = true; +- break; +- } +- } +- spin_unlock_irqrestore(&dev->request_lock, flags); +- +- if (found) { +- if (resp->msg_len <= sizeof(struct rndis_message)) { +- memcpy(&request->response_msg, resp, +- resp->msg_len); +- } else { +- netdev_err(ndev, +- "rndis response buffer overflow " +- "detected (size %u max %zu)\n", +- resp->msg_len, +- sizeof(struct rndis_filter_packet)); +- +- if (resp->ndis_msg_type == +- REMOTE_NDIS_RESET_CMPLT) { +- /* does not have a request id field */ +- request->response_msg.msg.reset_complete. +- status = STATUS_BUFFER_OVERFLOW; +- } else { +- request->response_msg.msg. +- init_complete.status = +- STATUS_BUFFER_OVERFLOW; +- } +- } +- +- complete(&request->wait_event); +- } else { +- netdev_err(ndev, +- "no rndis request found for this response " +- "(id 0x%x res type 0x%x)\n", +- resp->msg.init_complete.req_id, +- resp->ndis_msg_type); +- } +-} +- +-static void rndis_filter_receive_indicate_status(struct rndis_device *dev, +- struct rndis_message *resp) +-{ +- struct rndis_indicate_status *indicate = +- &resp->msg.indicate_status; +- +- if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { +- netvsc_linkstatus_callback( +- dev->net_dev->dev, 1); +- } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { +- netvsc_linkstatus_callback( +- dev->net_dev->dev, 0); +- } else { +- /* +- * TODO: +- */ +- } +-} +- +-static void rndis_filter_receive_data(struct rndis_device *dev, +- struct rndis_message *msg, +- struct hv_netvsc_packet *pkt) +-{ +- struct rndis_packet *rndis_pkt; +- u32 data_offset; +- int i; +- +- rndis_pkt = &msg->msg.pkt; +- +- /* +- * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this +- * netvsc packet (ie TotalDataBufferLength != MessageLength) +- */ +- +- /* Remove the rndis header and pass it back up the stack */ +- data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; +- +- pkt->total_data_buflen -= data_offset; +- pkt->page_buf[0].offset += data_offset; +- pkt->page_buf[0].len -= data_offset; +- +- /* Drop the 0th page, if rndis data go beyond page boundary */ +- if (pkt->page_buf[0].offset >= PAGE_SIZE) { +- pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE; +- pkt->page_buf[1].len -= pkt->page_buf[1].offset; +- pkt->page_buf_cnt--; +- for (i = 0; i < pkt->page_buf_cnt; i++) +- pkt->page_buf[i] = pkt->page_buf[i+1]; +- } +- +- pkt->is_data_pkt = true; +- +- netvsc_recv_callback(dev->net_dev->dev, pkt); +-} +- +-int rndis_filter_receive(struct hv_device *dev, +- struct hv_netvsc_packet *pkt) +-{ +- struct netvsc_device *net_dev = hv_get_drvdata(dev); +- struct rndis_device *rndis_dev; +- struct rndis_message rndis_msg; +- struct rndis_message *rndis_hdr; +- struct net_device *ndev; +- +- if (!net_dev) +- return -EINVAL; +- +- ndev = net_dev->ndev; +- +- /* Make sure the rndis device state is initialized */ +- if (!net_dev->extension) { +- netdev_err(ndev, "got rndis message but no rndis device - " +- "dropping this message!\n"); +- return -ENODEV; +- } +- +- rndis_dev = (struct rndis_device *)net_dev->extension; +- if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { +- netdev_err(ndev, "got rndis message but rndis device " +- "uninitialized...dropping this message!\n"); +- return -ENODEV; +- } +- +- rndis_hdr = (struct rndis_message *)kmap_atomic( +- pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0); +- +- rndis_hdr = (void *)((unsigned long)rndis_hdr + +- pkt->page_buf[0].offset); +- +- /* Make sure we got a valid rndis message */ +- if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && +- (rndis_hdr->msg_len > sizeof(struct rndis_message))) { +- netdev_err(ndev, "incoming rndis message buffer overflow " +- "detected (got %u, max %zu)..marking it an error!\n", +- rndis_hdr->msg_len, +- sizeof(struct rndis_message)); +- } +- +- memcpy(&rndis_msg, rndis_hdr, +- (rndis_hdr->msg_len > sizeof(struct rndis_message)) ? +- sizeof(struct rndis_message) : +- rndis_hdr->msg_len); +- +- kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); +- +- dump_rndis_message(dev, &rndis_msg); +- +- switch (rndis_msg.ndis_msg_type) { +- case REMOTE_NDIS_PACKET_MSG: +- /* data msg */ +- rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt); +- break; +- +- case REMOTE_NDIS_INITIALIZE_CMPLT: +- case REMOTE_NDIS_QUERY_CMPLT: +- case REMOTE_NDIS_SET_CMPLT: +- /* completion msgs */ +- rndis_filter_receive_response(rndis_dev, &rndis_msg); +- break; +- +- case REMOTE_NDIS_INDICATE_STATUS_MSG: +- /* notification msgs */ +- rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); +- break; +- default: +- netdev_err(ndev, +- "unhandled rndis message (type %u len %u)\n", +- rndis_msg.ndis_msg_type, +- rndis_msg.msg_len); +- break; +- } +- +- return 0; +-} +- +-static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, +- void *result, u32 *result_size) +-{ +- struct rndis_request *request; +- u32 inresult_size = *result_size; +- struct rndis_query_request *query; +- struct rndis_query_complete *query_complete; +- int ret = 0; +- int t; +- +- if (!result) +- return -EINVAL; +- +- *result_size = 0; +- request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG, +- RNDIS_MESSAGE_SIZE(struct rndis_query_request)); +- if (!request) { +- ret = -ENOMEM; +- goto cleanup; +- } +- +- /* Setup the rndis query */ +- query = &request->request_msg.msg.query_req; +- query->oid = oid; +- query->info_buf_offset = sizeof(struct rndis_query_request); +- query->info_buflen = 0; +- query->dev_vc_handle = 0; +- +- ret = rndis_filter_send_request(dev, request); +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- /* Copy the response back */ +- query_complete = &request->response_msg.msg.query_complete; +- +- if (query_complete->info_buflen > inresult_size) { +- ret = -1; +- goto cleanup; +- } +- +- memcpy(result, +- (void *)((unsigned long)query_complete + +- query_complete->info_buf_offset), +- query_complete->info_buflen); +- +- *result_size = query_complete->info_buflen; +- +-cleanup: +- if (request) +- put_rndis_request(dev, request); +- +- return ret; +-} +- +-static int rndis_filter_query_device_mac(struct rndis_device *dev) +-{ +- u32 size = ETH_ALEN; +- +- return rndis_filter_query_device(dev, +- RNDIS_OID_802_3_PERMANENT_ADDRESS, +- dev->hw_mac_adr, &size); +-} +- +-static int rndis_filter_query_device_link_status(struct rndis_device *dev) +-{ +- u32 size = sizeof(u32); +- u32 link_status; +- int ret; +- +- ret = rndis_filter_query_device(dev, +- RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, +- &link_status, &size); +- dev->link_state = (link_status != 0) ? true : false; +- +- return ret; +-} +- +-static int rndis_filter_set_packet_filter(struct rndis_device *dev, +- u32 new_filter) +-{ +- struct rndis_request *request; +- struct rndis_set_request *set; +- struct rndis_set_complete *set_complete; +- u32 status; +- int ret, t; +- struct net_device *ndev; +- +- ndev = dev->net_dev->ndev; +- +- request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, +- RNDIS_MESSAGE_SIZE(struct rndis_set_request) + +- sizeof(u32)); +- if (!request) { +- ret = -ENOMEM; +- goto cleanup; +- } +- +- /* Setup the rndis set */ +- set = &request->request_msg.msg.set_req; +- set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; +- set->info_buflen = sizeof(u32); +- set->info_buf_offset = sizeof(struct rndis_set_request); +- +- memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), +- &new_filter, sizeof(u32)); +- +- ret = rndis_filter_send_request(dev, request); +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- +- if (t == 0) { +- netdev_err(ndev, +- "timeout before we got a set response...\n"); +- /* +- * We can't deallocate the request since we may still receive a +- * send completion for it. +- */ +- goto exit; +- } else { +- set_complete = &request->response_msg.msg.set_complete; +- status = set_complete->status; +- } +- +-cleanup: +- if (request) +- put_rndis_request(dev, request); +-exit: +- return ret; +-} +- +- +-static int rndis_filter_init_device(struct rndis_device *dev) +-{ +- struct rndis_request *request; +- struct rndis_initialize_request *init; +- struct rndis_initialize_complete *init_complete; +- u32 status; +- int ret, t; +- +- request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, +- RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); +- if (!request) { +- ret = -ENOMEM; +- goto cleanup; +- } +- +- /* Setup the rndis set */ +- init = &request->request_msg.msg.init_req; +- init->major_ver = RNDIS_MAJOR_VERSION; +- init->minor_ver = RNDIS_MINOR_VERSION; +- /* FIXME: Use 1536 - rounded ethernet frame size */ +- init->max_xfer_size = 2048; +- +- dev->state = RNDIS_DEV_INITIALIZING; +- +- ret = rndis_filter_send_request(dev, request); +- if (ret != 0) { +- dev->state = RNDIS_DEV_UNINITIALIZED; +- goto cleanup; +- } +- +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- init_complete = &request->response_msg.msg.init_complete; +- status = init_complete->status; +- if (status == RNDIS_STATUS_SUCCESS) { +- dev->state = RNDIS_DEV_INITIALIZED; +- ret = 0; +- } else { +- dev->state = RNDIS_DEV_UNINITIALIZED; +- ret = -EINVAL; +- } +- +-cleanup: +- if (request) +- put_rndis_request(dev, request); +- +- return ret; +-} +- +-static void rndis_filter_halt_device(struct rndis_device *dev) +-{ +- struct rndis_request *request; +- struct rndis_halt_request *halt; +- +- /* Attempt to do a rndis device halt */ +- request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG, +- RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); +- if (!request) +- goto cleanup; +- +- /* Setup the rndis set */ +- halt = &request->request_msg.msg.halt_req; +- halt->req_id = atomic_inc_return(&dev->new_req_id); +- +- /* Ignore return since this msg is optional. */ +- rndis_filter_send_request(dev, request); +- +- dev->state = RNDIS_DEV_UNINITIALIZED; +- +-cleanup: +- if (request) +- put_rndis_request(dev, request); +- return; +-} +- +-static int rndis_filter_open_device(struct rndis_device *dev) +-{ +- int ret; +- +- if (dev->state != RNDIS_DEV_INITIALIZED) +- return 0; +- +- ret = rndis_filter_set_packet_filter(dev, +- NDIS_PACKET_TYPE_BROADCAST | +- NDIS_PACKET_TYPE_ALL_MULTICAST | +- NDIS_PACKET_TYPE_DIRECTED); +- if (ret == 0) +- dev->state = RNDIS_DEV_DATAINITIALIZED; +- +- return ret; +-} +- +-static int rndis_filter_close_device(struct rndis_device *dev) +-{ +- int ret; +- +- if (dev->state != RNDIS_DEV_DATAINITIALIZED) +- return 0; +- +- ret = rndis_filter_set_packet_filter(dev, 0); +- if (ret == 0) +- dev->state = RNDIS_DEV_INITIALIZED; +- +- return ret; +-} +- +-int rndis_filter_device_add(struct hv_device *dev, +- void *additional_info) +-{ +- int ret; +- struct netvsc_device *net_device; +- struct rndis_device *rndis_device; +- struct netvsc_device_info *device_info = additional_info; +- +- rndis_device = get_rndis_device(); +- if (!rndis_device) +- return -ENODEV; +- +- /* +- * Let the inner driver handle this first to create the netvsc channel +- * NOTE! Once the channel is created, we may get a receive callback +- * (RndisFilterOnReceive()) before this call is completed +- */ +- ret = netvsc_device_add(dev, additional_info); +- if (ret != 0) { +- kfree(rndis_device); +- return ret; +- } +- +- +- /* Initialize the rndis device */ +- net_device = hv_get_drvdata(dev); +- +- net_device->extension = rndis_device; +- rndis_device->net_dev = net_device; +- +- /* Send the rndis initialization message */ +- ret = rndis_filter_init_device(rndis_device); +- if (ret != 0) { +- /* +- * TODO: If rndis init failed, we will need to shut down the +- * channel +- */ +- } +- +- /* Get the mac address */ +- ret = rndis_filter_query_device_mac(rndis_device); +- if (ret != 0) { +- /* +- * TODO: shutdown rndis device and the channel +- */ +- } +- +- memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); +- +- rndis_filter_query_device_link_status(rndis_device); +- +- device_info->link_state = rndis_device->link_state; +- +- dev_info(&dev->device, "Device MAC %pM link state %s\n", +- rndis_device->hw_mac_adr, +- device_info->link_state ? "down" : "up"); +- +- return ret; +-} +- +-void rndis_filter_device_remove(struct hv_device *dev) +-{ +- struct netvsc_device *net_dev = hv_get_drvdata(dev); +- struct rndis_device *rndis_dev = net_dev->extension; +- +- /* Halt and release the rndis device */ +- rndis_filter_halt_device(rndis_dev); +- +- kfree(rndis_dev); +- net_dev->extension = NULL; +- +- netvsc_device_remove(dev); +-} +- +- +-int rndis_filter_open(struct hv_device *dev) +-{ +- struct netvsc_device *net_device = hv_get_drvdata(dev); +- +- if (!net_device) +- return -EINVAL; +- +- return rndis_filter_open_device(net_device->extension); +-} +- +-int rndis_filter_close(struct hv_device *dev) +-{ +- struct netvsc_device *netDevice = hv_get_drvdata(dev); +- +- if (!netDevice) +- return -EINVAL; +- +- return rndis_filter_close_device(netDevice->extension); +-} +- +-int rndis_filter_send(struct hv_device *dev, +- struct hv_netvsc_packet *pkt) +-{ +- int ret; +- struct rndis_filter_packet *filterPacket; +- struct rndis_message *rndisMessage; +- struct rndis_packet *rndisPacket; +- u32 rndisMessageSize; +- +- /* Add the rndis header */ +- filterPacket = (struct rndis_filter_packet *)pkt->extension; +- +- memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); +- +- rndisMessage = &filterPacket->msg; +- rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); +- +- rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; +- rndisMessage->msg_len = pkt->total_data_buflen + +- rndisMessageSize; +- +- rndisPacket = &rndisMessage->msg.pkt; +- rndisPacket->data_offset = sizeof(struct rndis_packet); +- rndisPacket->data_len = pkt->total_data_buflen; +- +- pkt->is_data_pkt = true; +- pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; +- pkt->page_buf[0].offset = +- (unsigned long)rndisMessage & (PAGE_SIZE-1); +- pkt->page_buf[0].len = rndisMessageSize; +- +- /* Save the packet send completion and context */ +- filterPacket->completion = pkt->completion.send.send_completion; +- filterPacket->completion_ctx = +- pkt->completion.send.send_completion_ctx; +- +- /* Use ours */ +- pkt->completion.send.send_completion = rndis_filter_send_completion; +- pkt->completion.send.send_completion_ctx = filterPacket; +- +- ret = netvsc_send(dev, pkt); +- if (ret != 0) { +- /* +- * Reset the completion to originals to allow retries from +- * above +- */ +- pkt->completion.send.send_completion = +- filterPacket->completion; +- pkt->completion.send.send_completion_ctx = +- filterPacket->completion_ctx; +- } +- +- return ret; +-} +- +-static void rndis_filter_send_completion(void *ctx) +-{ +- struct rndis_filter_packet *filterPacket = ctx; +- +- /* Pass it back to the original handler */ +- filterPacket->completion(filterPacket->completion_ctx); +-} +- +- +-static void rndis_filter_send_request_completion(void *ctx) +-{ +- /* Noop */ +-} +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0019-net-hyperv-Fix-long-lines-in-netvsc.c.patch b/debian/patches/features/x86/hyperv/0019-net-hyperv-Fix-long-lines-in-netvsc.c.patch new file mode 100644 index 000000000..28fbe82cb --- /dev/null +++ b/debian/patches/features/x86/hyperv/0019-net-hyperv-Fix-long-lines-in-netvsc.c.patch @@ -0,0 +1,35 @@ +From: Haiyang Zhang +Date: Wed, 30 Nov 2011 07:19:07 -0800 +Subject: [PATCH 19/77] net/hyperv: Fix long lines in netvsc.c + +commit c18132005e711c07523d8c6602e5b2266ab9a0f2 upstream. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/netvsc.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index 28e69a6..4a807e4 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -230,9 +230,11 @@ static int netvsc_init_recv_buf(struct hv_device *device) + net_device->recv_section_cnt = init_packet->msg. + v1_msg.send_recv_buf_complete.num_sections; + +- net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections, +- net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section), +- GFP_KERNEL); ++ net_device->recv_section = kmemdup( ++ init_packet->msg.v1_msg.send_recv_buf_complete.sections, ++ net_device->recv_section_cnt * ++ sizeof(struct nvsp_1_receive_buffer_section), ++ GFP_KERNEL); + if (net_device->recv_section == NULL) { + ret = -EINVAL; + goto cleanup; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0020-net-hyperv-Add-support-for-promiscuous-mode-setting.patch b/debian/patches/features/x86/hyperv/0020-net-hyperv-Add-support-for-promiscuous-mode-setting.patch new file mode 100644 index 000000000..f9a1d0e51 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0020-net-hyperv-Add-support-for-promiscuous-mode-setting.patch @@ -0,0 +1,163 @@ +From: Haiyang Zhang +Date: Wed, 30 Nov 2011 07:19:08 -0800 +Subject: [PATCH 20/77] net/hyperv: Add support for promiscuous mode setting + +commit d426b2e3d91f8ec3203f8852e7ad0153b5dfdf71 upstream. + +Add code to accept promiscuous mode setting, and pass it to +RNDIS filter. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/hyperv_net.h | 24 +++++++++++++++++++ + drivers/net/hyperv/netvsc_drv.c | 46 ++++++++++++++++++++++++++++++++++--- + drivers/net/hyperv/rndis_filter.c | 23 +------------------ + 3 files changed, 68 insertions(+), 25 deletions(-) + +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +index ac1ec84..49b131f 100644 +--- a/drivers/net/hyperv/hyperv_net.h ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -87,6 +87,27 @@ struct netvsc_device_info { + int ring_size; + }; + ++enum rndis_device_state { ++ RNDIS_DEV_UNINITIALIZED = 0, ++ RNDIS_DEV_INITIALIZING, ++ RNDIS_DEV_INITIALIZED, ++ RNDIS_DEV_DATAINITIALIZED, ++}; ++ ++struct rndis_device { ++ struct netvsc_device *net_dev; ++ ++ enum rndis_device_state state; ++ bool link_state; ++ atomic_t new_req_id; ++ ++ spinlock_t request_lock; ++ struct list_head req_list; ++ ++ unsigned char hw_mac_adr[ETH_ALEN]; ++}; ++ ++ + /* Interface */ + int netvsc_device_add(struct hv_device *device, void *additional_info); + int netvsc_device_remove(struct hv_device *device); +@@ -109,6 +130,9 @@ int rndis_filter_receive(struct hv_device *dev, + int rndis_filter_send(struct hv_device *dev, + struct hv_netvsc_packet *pkt); + ++int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); ++ ++ + #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) + + #define NVSP_PROTOCOL_VERSION_1 2 +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 93b0e91..b69c3a4 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -56,11 +56,51 @@ static int ring_size = 128; + module_param(ring_size, int, S_IRUGO); + MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); + +-/* no-op so the netdev core doesn't return -EINVAL when modifying the the +- * multicast address list in SIOCADDMULTI. hv is setup to get all multicast +- * when it calls RndisFilterOnOpen() */ ++struct set_multicast_work { ++ struct work_struct work; ++ struct net_device *net; ++}; ++ ++static void do_set_multicast(struct work_struct *w) ++{ ++ struct set_multicast_work *swk = ++ container_of(w, struct set_multicast_work, work); ++ struct net_device *net = swk->net; ++ ++ struct net_device_context *ndevctx = netdev_priv(net); ++ struct netvsc_device *nvdev; ++ struct rndis_device *rdev; ++ ++ nvdev = hv_get_drvdata(ndevctx->device_ctx); ++ if (nvdev == NULL) ++ return; ++ ++ rdev = nvdev->extension; ++ if (rdev == NULL) ++ return; ++ ++ if (net->flags & IFF_PROMISC) ++ rndis_filter_set_packet_filter(rdev, ++ NDIS_PACKET_TYPE_PROMISCUOUS); ++ else ++ rndis_filter_set_packet_filter(rdev, ++ NDIS_PACKET_TYPE_BROADCAST | ++ NDIS_PACKET_TYPE_ALL_MULTICAST | ++ NDIS_PACKET_TYPE_DIRECTED); ++ ++ kfree(w); ++} ++ + static void netvsc_set_multicast_list(struct net_device *net) + { ++ struct set_multicast_work *swk = ++ kmalloc(sizeof(struct set_multicast_work), GFP_ATOMIC); ++ if (swk == NULL) ++ return; ++ ++ swk->net = net; ++ INIT_WORK(&swk->work, do_set_multicast); ++ schedule_work(&swk->work); + } + + static int netvsc_open(struct net_device *net) +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index bafccb3..418e7aa 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -30,26 +30,6 @@ + #include "hyperv_net.h" + + +-enum rndis_device_state { +- RNDIS_DEV_UNINITIALIZED = 0, +- RNDIS_DEV_INITIALIZING, +- RNDIS_DEV_INITIALIZED, +- RNDIS_DEV_DATAINITIALIZED, +-}; +- +-struct rndis_device { +- struct netvsc_device *net_dev; +- +- enum rndis_device_state state; +- bool link_state; +- atomic_t new_req_id; +- +- spinlock_t request_lock; +- struct list_head req_list; +- +- unsigned char hw_mac_adr[ETH_ALEN]; +-}; +- + struct rndis_request { + struct list_head list_ent; + struct completion wait_event; +@@ -522,8 +502,7 @@ static int rndis_filter_query_device_link_status(struct rndis_device *dev) + return ret; + } + +-static int rndis_filter_set_packet_filter(struct rndis_device *dev, +- u32 new_filter) ++int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) + { + struct rndis_request *request; + struct rndis_set_request *set; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0021-Staging-hv-storvsc-Disable-clustering.patch b/debian/patches/features/x86/hyperv/0021-Staging-hv-storvsc-Disable-clustering.patch new file mode 100644 index 000000000..ba25a14cb --- /dev/null +++ b/debian/patches/features/x86/hyperv/0021-Staging-hv-storvsc-Disable-clustering.patch @@ -0,0 +1,65 @@ +From: "K. Y. Srinivasan" +Date: Thu, 1 Dec 2011 04:59:16 -0800 +Subject: [PATCH 21/77] Staging: hv: storvsc: Disable clustering + +commit 039db52de9c5682ee10a3cd69f2d76db40b1dee0 upstream. + +Disable clustering, since the host side on Hyper-V requires that +each I/O element not exceed the page size. As part of this +cleanup, get rid of the function to merge bvecs, as the primary +reason for this function was to avoid having an element exceed +the page size. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 18 +----------------- + 1 file changed, 1 insertion(+), 17 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 0245143..9153f98 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -798,13 +798,6 @@ static int storvsc_device_alloc(struct scsi_device *sdevice) + return 0; + } + +-static int storvsc_merge_bvec(struct request_queue *q, +- struct bvec_merge_data *bmd, struct bio_vec *bvec) +-{ +- /* checking done by caller. */ +- return bvec->bv_len; +-} +- + static int storvsc_device_configure(struct scsi_device *sdevice) + { + scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, +@@ -812,8 +805,6 @@ static int storvsc_device_configure(struct scsi_device *sdevice) + + blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); + +- blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec); +- + blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); + + return 0; +@@ -1375,14 +1366,7 @@ static struct scsi_host_template scsi_driver = { + /* no use setting to 0 since ll_blk_rw reset it to 1 */ + /* currently 32 */ + .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, +- /* +- * ENABLE_CLUSTERING allows mutiple physically contig bio_vecs to merge +- * into 1 sg element. If set, we must limit the max_segment_size to +- * PAGE_SIZE, otherwise we may get 1 sg element that represents +- * multiple +- */ +- /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */ +- .use_clustering = ENABLE_CLUSTERING, ++ .use_clustering = DISABLE_CLUSTERING, + /* Make sure we dont get a sg segment crosses a page boundary */ + .dma_boundary = PAGE_SIZE-1, + }; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0022-Staging-hv-storvsc-Cleanup-storvsc_device_alloc.patch b/debian/patches/features/x86/hyperv/0022-Staging-hv-storvsc-Cleanup-storvsc_device_alloc.patch new file mode 100644 index 000000000..cd0be2bde --- /dev/null +++ b/debian/patches/features/x86/hyperv/0022-Staging-hv-storvsc-Cleanup-storvsc_device_alloc.patch @@ -0,0 +1,36 @@ +From: "K. Y. Srinivasan" +Date: Thu, 1 Dec 2011 04:59:17 -0800 +Subject: [PATCH 22/77] Staging: hv: storvsc: Cleanup storvsc_device_alloc() + +commit 2781866484624fabbe373b95cfd20d44d2c9a69e upstream. + +The code in storvsc_device_alloc() is not needed as this would be +done by default. Get rid of it. We still keep the function as we use +this hook to allocate per-LUN memory pools in a later patch. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 9153f98..14ecb69 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -790,11 +790,6 @@ static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) + + static int storvsc_device_alloc(struct scsi_device *sdevice) + { +- /* +- * This enables luns to be located sparsely. Otherwise, we may not +- * discovered them. +- */ +- sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN; + return 0; + } + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0023-Staging-hv-storvsc-Fix-a-bug-in-storvsc_command_comp.patch b/debian/patches/features/x86/hyperv/0023-Staging-hv-storvsc-Fix-a-bug-in-storvsc_command_comp.patch new file mode 100644 index 000000000..cbebe2c94 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0023-Staging-hv-storvsc-Fix-a-bug-in-storvsc_command_comp.patch @@ -0,0 +1,40 @@ +From: "K. Y. Srinivasan" +Date: Thu, 1 Dec 2011 04:59:18 -0800 +Subject: [PATCH 23/77] Staging: hv: storvsc: Fix a bug in + storvsc_command_completion() + +commit a768a76d55c2798bbd86b81f50ef740312d69935 upstream. + +Fix a bug in storvsc_command_completion() that leaks memory when scatter/gather +lists are used on the "write" side. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 14ecb69..8dafe52 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1123,13 +1123,12 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + + vm_srb = &request->vstor_packet.vm_srb; + if (cmd_request->bounce_sgl_count) { +- if (vm_srb->data_in == READ_TYPE) { ++ if (vm_srb->data_in == READ_TYPE) + copy_from_bounce_buffer(scsi_sglist(scmnd), + cmd_request->bounce_sgl, + scsi_sg_count(scmnd)); +- destroy_bounce_buffer(cmd_request->bounce_sgl, ++ destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); +- } + } + + /* +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0024-Staging-hv-storvsc-Fix-a-bug-in-copy_from_bounce_buf.patch b/debian/patches/features/x86/hyperv/0024-Staging-hv-storvsc-Fix-a-bug-in-copy_from_bounce_buf.patch new file mode 100644 index 000000000..b493a0426 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0024-Staging-hv-storvsc-Fix-a-bug-in-copy_from_bounce_buf.patch @@ -0,0 +1,68 @@ +From: "K. Y. Srinivasan" +Date: Thu, 1 Dec 2011 04:59:19 -0800 +Subject: [PATCH 24/77] Staging: hv: storvsc: Fix a bug in + copy_from_bounce_buffer() + +commit 10c43dd4c4403d35fc6477d547aa3a38b466b310 upstream. + +Fix a bug in copy_from_bounce_buffer(). + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 24 ++++++++++++++++++++++-- + 1 file changed, 22 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 8dafe52..c22de06 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -880,7 +880,8 @@ cleanup: + /* Assume the original sgl has enough room */ + static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + struct scatterlist *bounce_sgl, +- unsigned int orig_sgl_count) ++ unsigned int orig_sgl_count, ++ unsigned int bounce_sgl_count) + { + int i; + int j = 0; +@@ -921,6 +922,24 @@ static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + kunmap_atomic((void *)bounce_addr, KM_IRQ0); + j++; + ++ /* ++ * It is possible that the number of elements ++ * in the bounce buffer may not be equal to ++ * the number of elements in the original ++ * scatter list. Handle this correctly. ++ */ ++ ++ if (j == bounce_sgl_count) { ++ /* ++ * We are done; cleanup and return. ++ */ ++ kunmap_atomic((void *)(dest_addr - ++ orig_sgl[i].offset), ++ KM_IRQ0); ++ local_irq_restore(flags); ++ return total_copied; ++ } ++ + /* if we need to use another bounce buffer */ + if (destlen || i != orig_sgl_count - 1) + bounce_addr = +@@ -1126,7 +1145,8 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + if (vm_srb->data_in == READ_TYPE) + copy_from_bounce_buffer(scsi_sglist(scmnd), + cmd_request->bounce_sgl, +- scsi_sg_count(scmnd)); ++ scsi_sg_count(scmnd), ++ cmd_request->bounce_sgl_count); + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + } +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0025-Staging-hv-storvsc-Implement-per-device-memory-pools.patch b/debian/patches/features/x86/hyperv/0025-Staging-hv-storvsc-Implement-per-device-memory-pools.patch new file mode 100644 index 000000000..08023840c --- /dev/null +++ b/debian/patches/features/x86/hyperv/0025-Staging-hv-storvsc-Implement-per-device-memory-pools.patch @@ -0,0 +1,264 @@ +From: "K. Y. Srinivasan" +Date: Thu, 1 Dec 2011 04:59:20 -0800 +Subject: [PATCH 25/77] Staging: hv: storvsc: Implement per device memory + pools + +commit ce3e301c442f7beffd049908a007697753766cf7 upstream. + +The current code implemented a per-HBA memory pool mechanism. For IDE disks +managed by this driver, there is a one to one correspondance between the +block device and the associated virtual HBA and since currently only IDE devices +can be the boot device, this addressed the deadlock issues that were raised during +the review process. This patch implements a per-lun memory pool mechanism. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 106 ++++++++++++++++++++++---------------- + 1 file changed, 62 insertions(+), 44 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index c22de06..18f8771 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -285,10 +285,13 @@ struct storvsc_device { + struct hv_storvsc_request reset_request; + }; + +-struct hv_host_device { +- struct hv_device *dev; ++struct stor_mem_pools { + struct kmem_cache *request_pool; + mempool_t *request_mempool; ++}; ++ ++struct hv_host_device { ++ struct hv_device *dev; + unsigned int port; + unsigned char path; + unsigned char target; +@@ -790,7 +793,48 @@ static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) + + static int storvsc_device_alloc(struct scsi_device *sdevice) + { ++ struct stor_mem_pools *memp; ++ int number = STORVSC_MIN_BUF_NR; ++ ++ memp = kzalloc(sizeof(struct stor_mem_pools), GFP_KERNEL); ++ if (!memp) ++ return -ENOMEM; ++ ++ memp->request_pool = ++ kmem_cache_create(dev_name(&sdevice->sdev_dev), ++ sizeof(struct storvsc_cmd_request), 0, ++ SLAB_HWCACHE_ALIGN, NULL); ++ ++ if (!memp->request_pool) ++ goto err0; ++ ++ memp->request_mempool = mempool_create(number, mempool_alloc_slab, ++ mempool_free_slab, ++ memp->request_pool); ++ ++ if (!memp->request_mempool) ++ goto err1; ++ ++ sdevice->hostdata = memp; ++ + return 0; ++ ++err1: ++ kmem_cache_destroy(memp->request_pool); ++ ++err0: ++ kfree(memp); ++ return -ENOMEM; ++} ++ ++static void storvsc_device_destroy(struct scsi_device *sdevice) ++{ ++ struct stor_mem_pools *memp = sdevice->hostdata; ++ ++ mempool_destroy(memp->request_mempool); ++ kmem_cache_destroy(memp->request_pool); ++ kfree(memp); ++ sdevice->hostdata = NULL; + } + + static int storvsc_device_configure(struct scsi_device *sdevice) +@@ -1031,19 +1075,13 @@ static int storvsc_remove(struct hv_device *dev) + { + struct storvsc_device *stor_device = hv_get_drvdata(dev); + struct Scsi_Host *host = stor_device->host; +- struct hv_host_device *host_dev = shost_priv(host); + + scsi_remove_host(host); + + scsi_host_put(host); + + storvsc_dev_remove(dev); +- if (host_dev->request_pool) { +- mempool_destroy(host_dev->request_mempool); +- kmem_cache_destroy(host_dev->request_pool); +- host_dev->request_pool = NULL; +- host_dev->request_mempool = NULL; +- } ++ + return 0; + } + +@@ -1139,6 +1177,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + struct scsi_sense_hdr sense_hdr; + struct vmscsi_request *vm_srb; + struct storvsc_scan_work *wrk; ++ struct stor_mem_pools *memp = scmnd->device->hostdata; + + vm_srb = &request->vstor_packet.vm_srb; + if (cmd_request->bounce_sgl_count) { +@@ -1201,7 +1240,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + + scsi_done_fn(scmnd); + +- mempool_free(cmd_request, host_dev->request_mempool); ++ mempool_free(cmd_request, memp->request_mempool); + } + + static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) +@@ -1236,6 +1275,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + struct scatterlist *sgl; + unsigned int sg_count = 0; + struct vmscsi_request *vm_srb; ++ struct stor_mem_pools *memp = scmnd->device->hostdata; + + if (storvsc_check_scsi_cmd(scmnd) == false) { + scmnd->scsi_done(scmnd); +@@ -1253,7 +1293,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + + request_size = sizeof(struct storvsc_cmd_request); + +- cmd_request = mempool_alloc(host_dev->request_mempool, ++ cmd_request = mempool_alloc(memp->request_mempool, + GFP_ATOMIC); + if (!cmd_request) + return SCSI_MLQUEUE_DEVICE_BUSY; +@@ -1312,7 +1352,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + if (!cmd_request->bounce_sgl) { + scmnd->host_scribble = NULL; + mempool_free(cmd_request, +- host_dev->request_mempool); ++ memp->request_mempool); + + return SCSI_MLQUEUE_HOST_BUSY; + } +@@ -1354,7 +1394,7 @@ retry_request: + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + +- mempool_free(cmd_request, host_dev->request_mempool); ++ mempool_free(cmd_request, memp->request_mempool); + + scmnd->host_scribble = NULL; + +@@ -1372,6 +1412,7 @@ static struct scsi_host_template scsi_driver = { + .queuecommand = storvsc_queuecommand, + .eh_host_reset_handler = storvsc_host_reset_handler, + .slave_alloc = storvsc_device_alloc, ++ .slave_destroy = storvsc_device_destroy, + .slave_configure = storvsc_device_configure, + .cmd_per_lun = 1, + /* 64 max_queue * 1 target */ +@@ -1413,7 +1454,6 @@ static int storvsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) + { + int ret; +- int number = STORVSC_MIN_BUF_NR; + struct Scsi_Host *host; + struct hv_host_device *host_dev; + bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); +@@ -1432,29 +1472,11 @@ static int storvsc_probe(struct hv_device *device, + host_dev->port = host->host_no; + host_dev->dev = device; + +- host_dev->request_pool = +- kmem_cache_create(dev_name(&device->device), +- sizeof(struct storvsc_cmd_request), 0, +- SLAB_HWCACHE_ALIGN, NULL); +- +- if (!host_dev->request_pool) { +- scsi_host_put(host); +- return -ENOMEM; +- } +- +- host_dev->request_mempool = mempool_create(number, mempool_alloc_slab, +- mempool_free_slab, +- host_dev->request_pool); +- +- if (!host_dev->request_mempool) { +- ret = -ENOMEM; +- goto err_out0; +- } + + stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); + if (!stor_device) { + ret = -ENOMEM; +- goto err_out1; ++ goto err_out0; + } + + stor_device->destroy = false; +@@ -1466,7 +1488,7 @@ static int storvsc_probe(struct hv_device *device, + stor_device->port_number = host->host_no; + ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); + if (ret) +- goto err_out2; ++ goto err_out1; + + if (dev_is_ide) + storvsc_get_ide_info(device, &target, &path); +@@ -1486,7 +1508,7 @@ static int storvsc_probe(struct hv_device *device, + /* Register the HBA and start the scsi bus scan */ + ret = scsi_add_host(host, &device->device); + if (ret != 0) +- goto err_out3; ++ goto err_out2; + + if (!dev_is_ide) { + scsi_scan_host(host); +@@ -1495,28 +1517,24 @@ static int storvsc_probe(struct hv_device *device, + ret = scsi_add_device(host, 0, target, 0); + if (ret) { + scsi_remove_host(host); +- goto err_out3; ++ goto err_out2; + } + return 0; + +-err_out3: ++err_out2: + /* + * Once we have connected with the host, we would need to + * to invoke storvsc_dev_remove() to rollback this state and + * this call also frees up the stor_device; hence the jump around +- * err_out2 label. ++ * err_out1 label. + */ + storvsc_dev_remove(device); +- goto err_out1; +- +-err_out2: +- kfree(stor_device); ++ goto err_out0; + + err_out1: +- mempool_destroy(host_dev->request_mempool); ++ kfree(stor_device); + + err_out0: +- kmem_cache_destroy(host_dev->request_pool); + scsi_host_put(host); + return ret; + } +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0026-Staging-hv-update-TODO-file.patch b/debian/patches/features/x86/hyperv/0026-Staging-hv-update-TODO-file.patch new file mode 100644 index 000000000..73eaf3489 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0026-Staging-hv-update-TODO-file.patch @@ -0,0 +1,29 @@ +From: Greg Kroah-Hartman +Date: Thu, 1 Dec 2011 10:38:42 -0800 +Subject: [PATCH 26/77] Staging: hv: update TODO file + +commit 0c9d19646249e0124f1d82fb939e5ba76cb1af3c upstream. + +Hank is no longer at Microsoft, so remove his email address as it +bounces. + +Cc: KY Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/TODO | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO +index fd080cb..dea7d92 100644 +--- a/drivers/staging/hv/TODO ++++ b/drivers/staging/hv/TODO +@@ -2,5 +2,4 @@ TODO: + - audit the scsi driver + + Please send patches for this code to Greg Kroah-Hartman , +-Hank Janssen , Haiyang Zhang , +-K. Y. Srinivasan ++Haiyang Zhang , and K. Y. Srinivasan +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0027-HID-hv_mouse-Properly-add-the-hid-device.patch b/debian/patches/features/x86/hyperv/0027-HID-hv_mouse-Properly-add-the-hid-device.patch new file mode 100644 index 000000000..7e1035f8f --- /dev/null +++ b/debian/patches/features/x86/hyperv/0027-HID-hv_mouse-Properly-add-the-hid-device.patch @@ -0,0 +1,34 @@ +From: "K. Y. Srinivasan" +Date: Wed, 30 Nov 2011 08:52:23 -0800 +Subject: [PATCH 27/77] HID: hv_mouse: Properly add the hid device + +commit 74c4fb058083b47571a4f76dcfce95085f2d8098 upstream. + +We need to properly add the hid device to correctly initialize the sysfs state. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Reported-by: Fuzhou Chen +Signed-off-by: Jiri Kosina +--- + drivers/hid/hid-hyperv.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c +index d503cbb..0c33ae9 100644 +--- a/drivers/hid/hid-hyperv.c ++++ b/drivers/hid/hid-hyperv.c +@@ -506,6 +506,10 @@ static int mousevsc_probe(struct hv_device *device, + + sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + ++ ret = hid_add_device(hid_dev); ++ if (ret) ++ goto probe_err1; ++ + ret = hid_parse_report(hid_dev, input_dev->report_desc, + input_dev->report_desc_size); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0028-Staging-hv-storvsc-Fix-a-bug-in-create_bounce_buffer.patch b/debian/patches/features/x86/hyperv/0028-Staging-hv-storvsc-Fix-a-bug-in-create_bounce_buffer.patch new file mode 100644 index 000000000..39d3c5233 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0028-Staging-hv-storvsc-Fix-a-bug-in-create_bounce_buffer.patch @@ -0,0 +1,65 @@ +From: "K. Y. Srinivasan" +Date: Wed, 7 Dec 2011 07:15:52 -0800 +Subject: [PATCH 28/77] Staging: hv: storvsc: Fix a bug in + create_bounce_buffer() + +commit 6e8087a4fae41ae1518babbf1ef46f105d702bfd upstream. + +Set the length field of the scatter gather elements correctly when we create +the bounce buffer. When we use the bounce buffer for a "write" operation, +the act of copying to the bounce buffer, correctly deals with this issue. +However, on the "read" side, the current code was not correctly setting +the buffer length. Fix this bug. Note that when we copy from the bounce +buffer (for the read case), the amount we copy is controlled by the original +scatter gather list given to the driver. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Reported-by: Dadok Milan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 18f8771..eb853f7 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -893,12 +893,14 @@ static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) + + static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, + unsigned int sg_count, +- unsigned int len) ++ unsigned int len, ++ int write) + { + int i; + int num_pages; + struct scatterlist *bounce_sgl; + struct page *page_buf; ++ unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); + + num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; + +@@ -910,7 +912,7 @@ static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, + page_buf = alloc_page(GFP_ATOMIC); + if (!page_buf) + goto cleanup; +- sg_set_page(&bounce_sgl[i], page_buf, 0, 0); ++ sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); + } + + return bounce_sgl; +@@ -1348,7 +1350,8 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { + cmd_request->bounce_sgl = + create_bounce_buffer(sgl, scsi_sg_count(scmnd), +- scsi_bufflen(scmnd)); ++ scsi_bufflen(scmnd), ++ vm_srb->data_in); + if (!cmd_request->bounce_sgl) { + scmnd->host_scribble = NULL; + mempool_free(cmd_request, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0029-net-hyperv-Fix-the-stop-wake-queue-mechanism.patch b/debian/patches/features/x86/hyperv/0029-net-hyperv-Fix-the-stop-wake-queue-mechanism.patch new file mode 100644 index 000000000..f070061d9 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0029-net-hyperv-Fix-the-stop-wake-queue-mechanism.patch @@ -0,0 +1,131 @@ +From: Haiyang Zhang +Date: Fri, 2 Dec 2011 11:56:25 -0800 +Subject: [PATCH 29/77] net/hyperv: Fix the stop/wake queue mechanism + +commit 1d06825b0ede541f63b5577435abd2fc649a9b5e upstream. + +The ring buffer is only used to pass meta data for outbound packets. The +actual payload is accessed by DMA from the host. So the stop/wake queue +mechanism based on counting and comparing number of pages sent v.s. number +of pages in the ring buffer is wrong. Also, there is a race condition in +the stop/wake queue calls, which can stop xmit queue forever. + +The new stop/wake queue mechanism is based on the actual bytes used by +outbound packets in the ring buffer. The check for number of outstanding +sends after stop queue prevents the race condition that can cause wake +queue happening earlier than stop queue. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Reported-by: Long Li +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/netvsc.c | 14 +++++++++++--- + drivers/net/hyperv/netvsc_drv.c | 24 +----------------------- + 2 files changed, 12 insertions(+), 26 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index 4a807e4..b6ac152 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -435,6 +435,9 @@ static void netvsc_send_completion(struct hv_device *device, + nvsc_packet->completion.send.send_completion_ctx); + + atomic_dec(&net_device->num_outstanding_sends); ++ ++ if (netif_queue_stopped(ndev)) ++ netif_wake_queue(ndev); + } else { + netdev_err(ndev, "Unknown send completion packet type- " + "%d received!!\n", nvsp_packet->hdr.msg_type); +@@ -485,11 +488,16 @@ int netvsc_send(struct hv_device *device, + + } + +- if (ret != 0) ++ if (ret == 0) { ++ atomic_inc(&net_device->num_outstanding_sends); ++ } else if (ret == -EAGAIN) { ++ netif_stop_queue(ndev); ++ if (atomic_read(&net_device->num_outstanding_sends) < 1) ++ netif_wake_queue(ndev); ++ } else { + netdev_err(ndev, "Unable to send packet %p ret %d\n", + packet, ret); +- else +- atomic_inc(&net_device->num_outstanding_sends); ++ } + + return ret; + } +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index b69c3a4..7da85eb 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -43,15 +43,10 @@ + struct net_device_context { + /* point back to our device context */ + struct hv_device *device_ctx; +- atomic_t avail; + struct delayed_work dwork; + }; + + +-#define PACKET_PAGES_LOWATER 8 +-/* Need this many pages to handle worst case fragmented packet */ +-#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2) +- + static int ring_size = 128; + module_param(ring_size, int, S_IRUGO); + MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); +@@ -144,18 +139,8 @@ static void netvsc_xmit_completion(void *context) + + kfree(packet); + +- if (skb) { +- struct net_device *net = skb->dev; +- struct net_device_context *net_device_ctx = netdev_priv(net); +- unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2; +- ++ if (skb) + dev_kfree_skb_any(skb); +- +- atomic_add(num_pages, &net_device_ctx->avail); +- if (atomic_read(&net_device_ctx->avail) >= +- PACKET_PAGES_HIWATER) +- netif_wake_queue(net); +- } + } + + static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) +@@ -167,8 +152,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + + /* Add 1 for skb->data and additional one for RNDIS */ + num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; +- if (num_pages > atomic_read(&net_device_ctx->avail)) +- return NETDEV_TX_BUSY; + + /* Allocate a netvsc packet based on # of frags. */ + packet = kzalloc(sizeof(struct hv_netvsc_packet) + +@@ -218,10 +201,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + if (ret == 0) { + net->stats.tx_bytes += skb->len; + net->stats.tx_packets++; +- +- atomic_sub(num_pages, &net_device_ctx->avail); +- if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER) +- netif_stop_queue(net); + } else { + /* we are shutting down or bus overloaded, just drop packet */ + net->stats.tx_dropped++; +@@ -391,7 +370,6 @@ static int netvsc_probe(struct hv_device *dev, + + net_device_ctx = netdev_priv(net); + net_device_ctx->device_ctx = dev; +- atomic_set(&net_device_ctx->avail, ring_size); + hv_set_drvdata(dev, net); + INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0030-Drivers-hv-Fix-a-memory-leak.patch b/debian/patches/features/x86/hyperv/0030-Drivers-hv-Fix-a-memory-leak.patch new file mode 100644 index 000000000..46f3651e6 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0030-Drivers-hv-Fix-a-memory-leak.patch @@ -0,0 +1,31 @@ +From: "K. Y. Srinivasan" +Date: Mon, 12 Dec 2011 09:29:16 -0800 +Subject: [PATCH 30/77] Drivers: hv: Fix a memory leak + +commit 8b8ee6753154dd6cfe397e0d29fe7f90a8adb50b upstream. + +There was a memory leak in a failure path in vmbus_process_offer(). +Fix it. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/channel_mgmt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c +index 12b85ff..b91af50 100644 +--- a/drivers/hv/channel_mgmt.c ++++ b/drivers/hv/channel_mgmt.c +@@ -287,6 +287,7 @@ static void vmbus_process_offer(struct work_struct *work) + spin_lock_irqsave(&vmbus_connection.channel_lock, flags); + list_del(&newchannel->listentry); + spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); ++ kfree(newchannel->device_obj); + + free_channel(newchannel); + } else { +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0031-Drivers-hv-Make-the-vmbus-driver-unloadable.patch b/debian/patches/features/x86/hyperv/0031-Drivers-hv-Make-the-vmbus-driver-unloadable.patch new file mode 100644 index 000000000..6a6b3794e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0031-Drivers-hv-Make-the-vmbus-driver-unloadable.patch @@ -0,0 +1,94 @@ +From: "K. Y. Srinivasan" +Date: Mon, 12 Dec 2011 09:29:17 -0800 +Subject: [PATCH 31/77] Drivers: hv: Make the vmbus driver unloadable + +commit 93e5bd06a95343c701361fa009cdc5a653d6ec8e upstream. + +It turns out that the vmbus driver can be made unloadable. Make it +unloadable. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/channel_mgmt.c | 11 +++++++++++ + drivers/hv/hv.c | 3 +++ + drivers/hv/hyperv_vmbus.h | 1 + + drivers/hv/vmbus_drv.c | 11 +++++++++++ + 4 files changed, 26 insertions(+) + +diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c +index b91af50..36484db 100644 +--- a/drivers/hv/channel_mgmt.c ++++ b/drivers/hv/channel_mgmt.c +@@ -223,6 +223,17 @@ static void vmbus_process_rescind_offer(struct work_struct *work) + vmbus_device_unregister(channel->device_obj); + } + ++void vmbus_free_channels(void) ++{ ++ struct vmbus_channel *channel; ++ ++ list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { ++ vmbus_device_unregister(channel->device_obj); ++ kfree(channel->device_obj); ++ free_channel(channel); ++ } ++} ++ + /* + * vmbus_process_offer - Process the offer by creating a channel/device + * associated with this offer +diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c +index 0fb100e..f8a77d0 100644 +--- a/drivers/hv/hv.c ++++ b/drivers/hv/hv.c +@@ -237,6 +237,9 @@ void hv_cleanup(void) + { + union hv_x64_msr_hypercall_contents hypercall_msr; + ++ /* Reset our OS id */ ++ wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); ++ + kfree(hv_context.signal_event_buffer); + hv_context.signal_event_buffer = NULL; + hv_context.signal_event_param = NULL; +diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h +index 0aee112..6d7d286 100644 +--- a/drivers/hv/hyperv_vmbus.h ++++ b/drivers/hv/hyperv_vmbus.h +@@ -611,6 +611,7 @@ void vmbus_device_unregister(struct hv_device *device_obj); + + struct vmbus_channel *relid2channel(u32 relid); + ++void vmbus_free_channels(void); + + /* Connection interface */ + +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index 05168eb..a220e57 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -792,8 +792,19 @@ cleanup: + return ret; + } + ++static void __exit vmbus_exit(void) ++{ ++ ++ free_irq(irq, hv_acpi_dev); ++ vmbus_free_channels(); ++ bus_unregister(&hv_bus); ++ hv_cleanup(); ++ acpi_bus_unregister_driver(&vmbus_acpi_driver); ++} ++ + + MODULE_LICENSE("GPL"); + MODULE_VERSION(HV_DRV_VERSION); + + subsys_initcall(hv_acpi_init); ++module_exit(vmbus_exit); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0032-Drivers-hv-Get-rid-of-an-unnecessary-check-in-hv.c.patch b/debian/patches/features/x86/hyperv/0032-Drivers-hv-Get-rid-of-an-unnecessary-check-in-hv.c.patch new file mode 100644 index 000000000..72f112d30 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0032-Drivers-hv-Get-rid-of-an-unnecessary-check-in-hv.c.patch @@ -0,0 +1,35 @@ +From: "K. Y. Srinivasan" +Date: Mon, 12 Dec 2011 09:29:18 -0800 +Subject: [PATCH 32/77] Drivers: hv: Get rid of an unnecessary check in hv.c + +commit 175cad266774d09c57a62c65baa6b4e1edafdf3a upstream. + +In preparation for eventually supporting kexec in Linux VMs on Hyper-V, +get rid of an unnecessary check in hv_init(). + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c +index f8a77d0..12aa97f 100644 +--- a/drivers/hv/hv.c ++++ b/drivers/hv/hv.c +@@ -164,11 +164,6 @@ int hv_init(void) + + 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; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0033-net-hyperv-Remove-unnecessary-kmap_atomic-in-netvsc-.patch b/debian/patches/features/x86/hyperv/0033-net-hyperv-Remove-unnecessary-kmap_atomic-in-netvsc-.patch new file mode 100644 index 000000000..122bc166e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0033-net-hyperv-Remove-unnecessary-kmap_atomic-in-netvsc-.patch @@ -0,0 +1,221 @@ +From: Haiyang Zhang +Date: Thu, 15 Dec 2011 13:45:15 -0800 +Subject: [PATCH 33/77] net/hyperv: Remove unnecessary kmap_atomic in netvsc + driver + +commit 453263421f88b4a7e508c2e7b639c97e99c5b118 upstream. + +__get_free_pages() doesn't return HI memory, so the memory is always mapped. +kmap_atomic() is not necessary here. This patch removes the kmap_atomic() +calls and related code for locking and page manipulation. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/hyperv_net.h | 6 ++--- + drivers/net/hyperv/netvsc.c | 53 +++---------------------------------- + drivers/net/hyperv/netvsc_drv.c | 22 ++------------- + drivers/net/hyperv/rndis_filter.c | 21 ++------------- + 4 files changed, 10 insertions(+), 92 deletions(-) + +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +index 49b131f..ff1b520 100644 +--- a/drivers/net/hyperv/hyperv_net.h ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -39,9 +39,6 @@ struct xferpage_packet { + u32 count; + }; + +-/* The number of pages which are enough to cover jumbo frame buffer. */ +-#define NETVSC_PACKET_MAXPAGE 4 +- + /* + * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame + * within the RNDIS +@@ -77,8 +74,9 @@ struct hv_netvsc_packet { + + u32 total_data_buflen; + /* Points to the send/receive buffer where the ethernet frame is */ ++ void *data; + u32 page_buf_cnt; +- struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE]; ++ struct hv_page_buffer page_buf[0]; + }; + + struct netvsc_device_info { +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index b6ac152..bab627f 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -603,12 +603,10 @@ static void netvsc_receive(struct hv_device *device, + struct vmtransfer_page_packet_header *vmxferpage_packet; + struct nvsp_message *nvsp_packet; + struct hv_netvsc_packet *netvsc_packet = NULL; +- unsigned long start; +- unsigned long end, end_virtual; + /* struct netvsc_driver *netvscDriver; */ + struct xferpage_packet *xferpage_packet = NULL; +- int i, j; +- int count = 0, bytes_remain = 0; ++ int i; ++ int count = 0; + unsigned long flags; + struct net_device *ndev; + +@@ -717,53 +715,10 @@ static void netvsc_receive(struct hv_device *device, + netvsc_packet->completion.recv.recv_completion_tid = + vmxferpage_packet->d.trans_id; + ++ netvsc_packet->data = (void *)((unsigned long)net_device-> ++ recv_buf + vmxferpage_packet->ranges[i].byte_offset); + netvsc_packet->total_data_buflen = + vmxferpage_packet->ranges[i].byte_count; +- netvsc_packet->page_buf_cnt = 1; +- +- netvsc_packet->page_buf[0].len = +- vmxferpage_packet->ranges[i].byte_count; +- +- start = virt_to_phys((void *)((unsigned long)net_device-> +- recv_buf + vmxferpage_packet->ranges[i].byte_offset)); +- +- netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT; +- end_virtual = (unsigned long)net_device->recv_buf +- + vmxferpage_packet->ranges[i].byte_offset +- + vmxferpage_packet->ranges[i].byte_count - 1; +- end = virt_to_phys((void *)end_virtual); +- +- /* Calculate the page relative offset */ +- netvsc_packet->page_buf[0].offset = +- vmxferpage_packet->ranges[i].byte_offset & +- (PAGE_SIZE - 1); +- if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { +- /* Handle frame across multiple pages: */ +- netvsc_packet->page_buf[0].len = +- (netvsc_packet->page_buf[0].pfn << +- PAGE_SHIFT) +- + PAGE_SIZE - start; +- bytes_remain = netvsc_packet->total_data_buflen - +- netvsc_packet->page_buf[0].len; +- for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { +- netvsc_packet->page_buf[j].offset = 0; +- if (bytes_remain <= PAGE_SIZE) { +- netvsc_packet->page_buf[j].len = +- bytes_remain; +- bytes_remain = 0; +- } else { +- netvsc_packet->page_buf[j].len = +- PAGE_SIZE; +- bytes_remain -= PAGE_SIZE; +- } +- netvsc_packet->page_buf[j].pfn = +- virt_to_phys((void *)(end_virtual - +- bytes_remain)) >> PAGE_SHIFT; +- netvsc_packet->page_buf_cnt++; +- if (bytes_remain == 0) +- break; +- } +- } + + /* Pass it to the upper layer */ + rndis_filter_receive(device, netvsc_packet); +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 7da85eb..b7cbd12 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -251,9 +251,6 @@ int netvsc_recv_callback(struct hv_device *device_obj, + { + struct net_device *net = dev_get_drvdata(&device_obj->device); + struct sk_buff *skb; +- void *data; +- int i; +- unsigned long flags; + struct netvsc_device *net_device; + + net_device = hv_get_drvdata(device_obj); +@@ -272,27 +269,12 @@ int netvsc_recv_callback(struct hv_device *device_obj, + return 0; + } + +- /* for kmap_atomic */ +- local_irq_save(flags); +- + /* + * Copy to skb. This copy is needed here since the memory pointed by + * hv_netvsc_packet cannot be deallocated + */ +- for (i = 0; i < packet->page_buf_cnt; i++) { +- data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn), +- KM_IRQ1); +- data = (void *)(unsigned long)data + +- packet->page_buf[i].offset; +- +- memcpy(skb_put(skb, packet->page_buf[i].len), data, +- packet->page_buf[i].len); +- +- kunmap_atomic((void *)((unsigned long)data - +- packet->page_buf[i].offset), KM_IRQ1); +- } +- +- local_irq_restore(flags); ++ memcpy(skb_put(skb, packet->total_data_buflen), packet->data, ++ packet->total_data_buflen); + + skb->protocol = eth_type_trans(skb, net); + skb->ip_summed = CHECKSUM_NONE; +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index 418e7aa..da181f9 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -309,7 +309,6 @@ static void rndis_filter_receive_data(struct rndis_device *dev, + { + struct rndis_packet *rndis_pkt; + u32 data_offset; +- int i; + + rndis_pkt = &msg->msg.pkt; + +@@ -322,17 +321,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, + data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; + + pkt->total_data_buflen -= data_offset; +- pkt->page_buf[0].offset += data_offset; +- pkt->page_buf[0].len -= data_offset; +- +- /* Drop the 0th page, if rndis data go beyond page boundary */ +- if (pkt->page_buf[0].offset >= PAGE_SIZE) { +- pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE; +- pkt->page_buf[1].len -= pkt->page_buf[1].offset; +- pkt->page_buf_cnt--; +- for (i = 0; i < pkt->page_buf_cnt; i++) +- pkt->page_buf[i] = pkt->page_buf[i+1]; +- } ++ pkt->data = (void *)((unsigned long)pkt->data + data_offset); + + pkt->is_data_pkt = true; + +@@ -367,11 +356,7 @@ int rndis_filter_receive(struct hv_device *dev, + return -ENODEV; + } + +- rndis_hdr = (struct rndis_message *)kmap_atomic( +- pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0); +- +- rndis_hdr = (void *)((unsigned long)rndis_hdr + +- pkt->page_buf[0].offset); ++ rndis_hdr = pkt->data; + + /* Make sure we got a valid rndis message */ + if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && +@@ -387,8 +372,6 @@ int rndis_filter_receive(struct hv_device *dev, + sizeof(struct rndis_message) : + rndis_hdr->msg_len); + +- kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); +- + dump_rndis_message(dev, &rndis_msg); + + switch (rndis_msg.ndis_msg_type) { +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0034-net-hyperv-Add-NETVSP-protocol-version-negotiation.patch b/debian/patches/features/x86/hyperv/0034-net-hyperv-Add-NETVSP-protocol-version-negotiation.patch new file mode 100644 index 000000000..66276c604 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0034-net-hyperv-Add-NETVSP-protocol-version-negotiation.patch @@ -0,0 +1,284 @@ +From: Haiyang Zhang +Date: Thu, 15 Dec 2011 13:45:16 -0800 +Subject: [PATCH 34/77] net/hyperv: Add NETVSP protocol version negotiation + +commit f157e78de5923dfb209355f3005ce1b5d64f7998 upstream. + +Automatically negotiate the highest protocol version mutually recognized by +both host and guest. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/hyperv_net.h | 101 ++++++++++++++++++++++++++++++++++++--- + drivers/net/hyperv/netvsc.c | 82 ++++++++++++++++++++----------- + 2 files changed, 149 insertions(+), 34 deletions(-) + +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +index ff1b520..2877670 100644 +--- a/drivers/net/hyperv/hyperv_net.h ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -134,8 +134,7 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); + #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) + + #define NVSP_PROTOCOL_VERSION_1 2 +-#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 +-#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 ++#define NVSP_PROTOCOL_VERSION_2 0x30002 + + enum { + NVSP_MSG_TYPE_NONE = 0, +@@ -160,11 +159,36 @@ enum { + NVSP_MSG1_TYPE_SEND_RNDIS_PKT, + NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, + +- /* +- * This should be set to the number of messages for the version with +- * the maximum number of messages. +- */ +- NVSP_NUM_MSG_PER_VERSION = 9, ++ /* Version 2 messages */ ++ NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF, ++ NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF_COMP, ++ NVSP_MSG2_TYPE_REVOKE_CHIMNEY_DELEGATED_BUF, ++ ++ NVSP_MSG2_TYPE_RESUME_CHIMNEY_RX_INDICATION, ++ ++ NVSP_MSG2_TYPE_TERMINATE_CHIMNEY, ++ NVSP_MSG2_TYPE_TERMINATE_CHIMNEY_COMP, ++ ++ NVSP_MSG2_TYPE_INDICATE_CHIMNEY_EVENT, ++ ++ NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT, ++ NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT_COMP, ++ ++ NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ, ++ NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ_COMP, ++ ++ NVSP_MSG2_TYPE_ALLOC_RXBUF, ++ NVSP_MSG2_TYPE_ALLOC_RXBUF_COMP, ++ ++ NVSP_MSG2_TYPE_FREE_RXBUF, ++ ++ NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT, ++ NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT_COMP, ++ ++ NVSP_MSG2_TYPE_SEND_NDIS_CONFIG, ++ ++ NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE, ++ NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE_COMP, + }; + + enum { +@@ -175,6 +199,7 @@ enum { + NVSP_STAT_PROTOCOL_TOO_OLD, + NVSP_STAT_INVALID_RNDIS_PKT, + NVSP_STAT_BUSY, ++ NVSP_STAT_PROTOCOL_UNSUPPORTED, + NVSP_STAT_MAX, + }; + +@@ -359,9 +384,69 @@ union nvsp_1_message_uber { + send_rndis_pkt_complete; + } __packed; + ++ ++/* ++ * Network VSP protocol version 2 messages: ++ */ ++struct nvsp_2_vsc_capability { ++ union { ++ u64 data; ++ struct { ++ u64 vmq:1; ++ u64 chimney:1; ++ u64 sriov:1; ++ u64 ieee8021q:1; ++ u64 correlation_id:1; ++ }; ++ }; ++} __packed; ++ ++struct nvsp_2_send_ndis_config { ++ u32 mtu; ++ u32 reserved; ++ struct nvsp_2_vsc_capability capability; ++} __packed; ++ ++/* Allocate receive buffer */ ++struct nvsp_2_alloc_rxbuf { ++ /* Allocation ID to match the allocation request and response */ ++ u32 alloc_id; ++ ++ /* Length of the VM shared memory receive buffer that needs to ++ * be allocated ++ */ ++ u32 len; ++} __packed; ++ ++/* Allocate receive buffer complete */ ++struct nvsp_2_alloc_rxbuf_comp { ++ /* The NDIS_STATUS code for buffer allocation */ ++ u32 status; ++ ++ u32 alloc_id; ++ ++ /* GPADL handle for the allocated receive buffer */ ++ u32 gpadl_handle; ++ ++ /* Receive buffer ID */ ++ u64 recv_buf_id; ++} __packed; ++ ++struct nvsp_2_free_rxbuf { ++ u64 recv_buf_id; ++} __packed; ++ ++union nvsp_2_message_uber { ++ struct nvsp_2_send_ndis_config send_ndis_config; ++ struct nvsp_2_alloc_rxbuf alloc_rxbuf; ++ struct nvsp_2_alloc_rxbuf_comp alloc_rxbuf_comp; ++ struct nvsp_2_free_rxbuf free_rxbuf; ++} __packed; ++ + union nvsp_all_messages { + union nvsp_message_init_uber init_msg; + union nvsp_1_message_uber v1_msg; ++ union nvsp_2_message_uber v2_msg; + } __packed; + + /* ALL Messages */ +@@ -391,6 +476,8 @@ struct nvsp_message { + struct netvsc_device { + struct hv_device *dev; + ++ u32 nvsp_version; ++ + atomic_t num_outstanding_sends; + bool destroy; + /* +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index bab627f..46828b4 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include "hyperv_net.h" + +@@ -260,27 +261,18 @@ exit: + } + + +-static int netvsc_connect_vsp(struct hv_device *device) ++/* Negotiate NVSP protocol version */ ++static int negotiate_nvsp_ver(struct hv_device *device, ++ struct netvsc_device *net_device, ++ struct nvsp_message *init_packet, ++ u32 nvsp_ver) + { + int ret, t; +- struct netvsc_device *net_device; +- struct nvsp_message *init_packet; +- int ndis_version; +- struct net_device *ndev; +- +- net_device = get_outbound_net_device(device); +- if (!net_device) +- return -ENODEV; +- ndev = net_device->ndev; +- +- init_packet = &net_device->channel_init_pkt; + + memset(init_packet, 0, sizeof(struct nvsp_message)); + init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; +- init_packet->msg.init_msg.init.min_protocol_ver = +- NVSP_MIN_PROTOCOL_VERSION; +- init_packet->msg.init_msg.init.max_protocol_ver = +- NVSP_MAX_PROTOCOL_VERSION; ++ init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver; ++ init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver; + + /* Send the init request */ + ret = vmbus_sendpacket(device->channel, init_packet, +@@ -290,26 +282,62 @@ static int netvsc_connect_vsp(struct hv_device *device) + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) +- goto cleanup; ++ return ret; + + t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); + +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } ++ if (t == 0) ++ return -ETIMEDOUT; + + if (init_packet->msg.init_msg.init_complete.status != +- NVSP_STAT_SUCCESS) { +- ret = -EINVAL; +- goto cleanup; +- } ++ NVSP_STAT_SUCCESS) ++ return -EINVAL; + +- if (init_packet->msg.init_msg.init_complete. +- negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { ++ if (nvsp_ver != NVSP_PROTOCOL_VERSION_2) ++ return 0; ++ ++ /* NVSPv2 only: Send NDIS config */ ++ memset(init_packet, 0, sizeof(struct nvsp_message)); ++ init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; ++ init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN; ++ ++ ret = vmbus_sendpacket(device->channel, init_packet, ++ sizeof(struct nvsp_message), ++ (unsigned long)init_packet, ++ VM_PKT_DATA_INBAND, 0); ++ ++ return ret; ++} ++ ++static int netvsc_connect_vsp(struct hv_device *device) ++{ ++ int ret; ++ struct netvsc_device *net_device; ++ struct nvsp_message *init_packet; ++ int ndis_version; ++ struct net_device *ndev; ++ ++ net_device = get_outbound_net_device(device); ++ if (!net_device) ++ return -ENODEV; ++ ndev = net_device->ndev; ++ ++ init_packet = &net_device->channel_init_pkt; ++ ++ /* Negotiate the latest NVSP protocol supported */ ++ if (negotiate_nvsp_ver(device, net_device, init_packet, ++ NVSP_PROTOCOL_VERSION_2) == 0) { ++ net_device->nvsp_version = NVSP_PROTOCOL_VERSION_2; ++ } else if (negotiate_nvsp_ver(device, net_device, init_packet, ++ NVSP_PROTOCOL_VERSION_1) == 0) { ++ net_device->nvsp_version = NVSP_PROTOCOL_VERSION_1; ++ } else { + ret = -EPROTO; + goto cleanup; + } ++ ++ pr_debug("Negotiated NVSP version:%x\n", net_device->nvsp_version); ++ + /* Send the ndis version */ + memset(init_packet, 0, sizeof(struct nvsp_message)); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0035-net-hyperv-Add-support-for-jumbo-frame-up-to-64KB.patch b/debian/patches/features/x86/hyperv/0035-net-hyperv-Add-support-for-jumbo-frame-up-to-64KB.patch new file mode 100644 index 000000000..672c29e4a --- /dev/null +++ b/debian/patches/features/x86/hyperv/0035-net-hyperv-Add-support-for-jumbo-frame-up-to-64KB.patch @@ -0,0 +1,213 @@ +From: Haiyang Zhang +Date: Thu, 15 Dec 2011 13:45:17 -0800 +Subject: [PATCH 35/77] net/hyperv: Add support for jumbo frame up to 64KB + +commit 4d447c9a6ebc0142d320f075c5bac6d202a79fd4 upstream. + +Allow the user set the MTU up to 65536 for Linux guests running on +Hyper-V 2008 R2 or later. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/hyperv_net.h | 8 ++--- + drivers/net/hyperv/netvsc.c | 6 ++-- + drivers/net/hyperv/netvsc_drv.c | 70 ++++++++++++++++++++++++++++++++++----- + include/linux/hyperv.h | 2 +- + 4 files changed, 68 insertions(+), 18 deletions(-) + +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +index 2877670..dec5836 100644 +--- a/drivers/net/hyperv/hyperv_net.h ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -456,12 +456,9 @@ struct nvsp_message { + } __packed; + + ++#define NETVSC_MTU 65536 + +- +-/* #define NVSC_MIN_PROTOCOL_VERSION 1 */ +-/* #define NVSC_MAX_PROTOCOL_VERSION 1 */ +- +-#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ ++#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*2) /* 2MB */ + + #define NETVSC_RECEIVE_BUFFER_ID 0xcafe + +@@ -479,6 +476,7 @@ struct netvsc_device { + u32 nvsp_version; + + atomic_t num_outstanding_sends; ++ bool start_remove; + bool destroy; + /* + * List of free preallocated hv_netvsc_packet to represent receive +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index 46828b4..8965b45 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -42,7 +42,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) + if (!net_device) + return NULL; + +- ++ net_device->start_remove = false; + net_device->destroy = false; + net_device->dev = device; + net_device->ndev = ndev; +@@ -299,7 +299,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, + /* NVSPv2 only: Send NDIS config */ + memset(init_packet, 0, sizeof(struct nvsp_message)); + init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; +- init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN; ++ init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu; + + ret = vmbus_sendpacket(device->channel, init_packet, + sizeof(struct nvsp_message), +@@ -464,7 +464,7 @@ static void netvsc_send_completion(struct hv_device *device, + + atomic_dec(&net_device->num_outstanding_sends); + +- if (netif_queue_stopped(ndev)) ++ if (netif_queue_stopped(ndev) && !net_device->start_remove) + netif_wake_queue(ndev); + } else { + netdev_err(ndev, "Unknown send completion packet type- " +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index b7cbd12..462d05f 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -148,10 +148,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + struct net_device_context *net_device_ctx = netdev_priv(net); + struct hv_netvsc_packet *packet; + int ret; +- unsigned int i, num_pages; ++ unsigned int i, num_pages, npg_data; + +- /* Add 1 for skb->data and additional one for RNDIS */ +- num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; ++ /* Add multipage for skb->data and additional one for RNDIS */ ++ npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) ++ >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; ++ num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; + + /* Allocate a netvsc packet based on # of frags. */ + packet = kzalloc(sizeof(struct hv_netvsc_packet) + +@@ -174,21 +176,36 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + packet->page_buf_cnt = num_pages; + + /* Initialize it from the skb */ +- packet->total_data_buflen = skb->len; ++ packet->total_data_buflen = skb->len; + + /* Start filling in the page buffers starting after RNDIS buffer. */ + packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; + packet->page_buf[1].offset + = (unsigned long)skb->data & (PAGE_SIZE - 1); +- packet->page_buf[1].len = skb_headlen(skb); ++ if (npg_data == 1) ++ packet->page_buf[1].len = skb_headlen(skb); ++ else ++ packet->page_buf[1].len = PAGE_SIZE ++ - packet->page_buf[1].offset; ++ ++ for (i = 2; i <= npg_data; i++) { ++ packet->page_buf[i].pfn = virt_to_phys(skb->data ++ + PAGE_SIZE * (i-1)) >> PAGE_SHIFT; ++ packet->page_buf[i].offset = 0; ++ packet->page_buf[i].len = PAGE_SIZE; ++ } ++ if (npg_data > 1) ++ packet->page_buf[npg_data].len = (((unsigned long)skb->data ++ + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1; + + /* Additional fragments are after SKB data */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; + +- packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f)); +- packet->page_buf[i+2].offset = f->page_offset; +- packet->page_buf[i+2].len = skb_frag_size(f); ++ packet->page_buf[i+npg_data+1].pfn = ++ page_to_pfn(skb_frag_page(f)); ++ packet->page_buf[i+npg_data+1].offset = f->page_offset; ++ packet->page_buf[i+npg_data+1].len = skb_frag_size(f); + } + + /* Set the completion routine */ +@@ -300,6 +317,39 @@ static void netvsc_get_drvinfo(struct net_device *net, + strcpy(info->fw_version, "N/A"); + } + ++static int netvsc_change_mtu(struct net_device *ndev, int mtu) ++{ ++ struct net_device_context *ndevctx = netdev_priv(ndev); ++ struct hv_device *hdev = ndevctx->device_ctx; ++ struct netvsc_device *nvdev = hv_get_drvdata(hdev); ++ struct netvsc_device_info device_info; ++ int limit = ETH_DATA_LEN; ++ ++ if (nvdev == NULL || nvdev->destroy) ++ return -ENODEV; ++ ++ if (nvdev->nvsp_version == NVSP_PROTOCOL_VERSION_2) ++ limit = NETVSC_MTU; ++ ++ if (mtu < 68 || mtu > limit) ++ return -EINVAL; ++ ++ nvdev->start_remove = true; ++ cancel_delayed_work_sync(&ndevctx->dwork); ++ netif_stop_queue(ndev); ++ rndis_filter_device_remove(hdev); ++ ++ ndev->mtu = mtu; ++ ++ ndevctx->device_ctx = hdev; ++ hv_set_drvdata(hdev, ndev); ++ device_info.ring_size = ring_size; ++ rndis_filter_device_add(hdev, &device_info); ++ netif_wake_queue(ndev); ++ ++ return 0; ++} ++ + static const struct ethtool_ops ethtool_ops = { + .get_drvinfo = netvsc_get_drvinfo, + .get_link = ethtool_op_get_link, +@@ -310,7 +360,7 @@ static const struct net_device_ops device_ops = { + .ndo_stop = netvsc_close, + .ndo_start_xmit = netvsc_start_xmit, + .ndo_set_rx_mode = netvsc_set_multicast_list, +- .ndo_change_mtu = eth_change_mtu, ++ .ndo_change_mtu = netvsc_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + }; +@@ -403,6 +453,8 @@ static int netvsc_remove(struct hv_device *dev) + return 0; + } + ++ net_device->start_remove = true; ++ + ndev_ctx = netdev_priv(net); + cancel_delayed_work_sync(&ndev_ctx->dwork); + +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index 12ec328..62b908e 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -35,7 +35,7 @@ + #include + + +-#define MAX_PAGE_BUFFER_COUNT 16 ++#define MAX_PAGE_BUFFER_COUNT 18 + #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ + + #pragma pack(push, 1) +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0036-net-hyperv-fix-possible-memory-leak-in-do_set_multic.patch b/debian/patches/features/x86/hyperv/0036-net-hyperv-fix-possible-memory-leak-in-do_set_multic.patch new file mode 100644 index 000000000..c1f4eed1d --- /dev/null +++ b/debian/patches/features/x86/hyperv/0036-net-hyperv-fix-possible-memory-leak-in-do_set_multic.patch @@ -0,0 +1,47 @@ +From: Wei Yongjun +Date: Tue, 24 Jan 2012 10:21:28 +0000 +Subject: [PATCH 36/77] net/hyperv: fix possible memory leak in + do_set_multicast() + +commit c11bf1c8baff170fa478adc04964da519d160e62 upstream. + +do_set_multicast() may not free the memory malloc in +netvsc_set_multicast_list(). + +Signed-off-by: Wei Yongjun +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 462d05f..1a1ca6c 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -68,11 +68,11 @@ static void do_set_multicast(struct work_struct *w) + + nvdev = hv_get_drvdata(ndevctx->device_ctx); + if (nvdev == NULL) +- return; ++ goto out; + + rdev = nvdev->extension; + if (rdev == NULL) +- return; ++ goto out; + + if (net->flags & IFF_PROMISC) + rndis_filter_set_packet_filter(rdev, +@@ -83,6 +83,7 @@ static void do_set_multicast(struct work_struct *w) + NDIS_PACKET_TYPE_ALL_MULTICAST | + NDIS_PACKET_TYPE_DIRECTED); + ++out: + kfree(w); + } + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0037-drivers-hv-Get-rid-of-some-unnecessary-code.patch b/debian/patches/features/x86/hyperv/0037-drivers-hv-Get-rid-of-some-unnecessary-code.patch new file mode 100644 index 000000000..d19692007 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0037-drivers-hv-Get-rid-of-some-unnecessary-code.patch @@ -0,0 +1,127 @@ +From: "K. Y. Srinivasan" +Date: Sun, 8 Jan 2012 10:12:18 -0800 +Subject: [PATCH 37/77] drivers: hv: Get rid of some unnecessary code + +commit 2897a563a55442379e5e59ec68c229a7f27fb7c6 upstream. + +The current code unnecessarily limits the number of offers we handle. +Get rid of this limitation. As part of this cleanup, also get rid of an +unused define - MAX_MSG_TYPES. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/channel_mgmt.c | 87 --------------------------------------------- + 1 file changed, 87 deletions(-) + +diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c +index 36484db..9ffbfc5 100644 +--- a/drivers/hv/channel_mgmt.c ++++ b/drivers/hv/channel_mgmt.c +@@ -37,81 +37,6 @@ struct vmbus_channel_message_table_entry { + 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 +- } +- }, +- +-}; +- + + /** + * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message +@@ -321,20 +246,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) + 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; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0038-net-hyperv-rx_bytes-should-account-the-ether-header-.patch b/debian/patches/features/x86/hyperv/0038-net-hyperv-rx_bytes-should-account-the-ether-header-.patch new file mode 100644 index 000000000..e487c2fcb --- /dev/null +++ b/debian/patches/features/x86/hyperv/0038-net-hyperv-rx_bytes-should-account-the-ether-header-.patch @@ -0,0 +1,32 @@ +From: Wei Yongjun +Date: Sun, 29 Jan 2012 22:14:02 +0000 +Subject: [PATCH 38/77] net/hyperv: rx_bytes should account the ether header + size + +commit 48c3883999cb06246911e29356d194f96f1c75ef upstream. + +skb->len after call eth_type_trans() does not include the ether +header size, but rx_bytes should account it. + +Signed-off-by: Wei Yongjun +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 1a1ca6c..9dccc7a 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -298,7 +298,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, + skb->ip_summed = CHECKSUM_NONE; + + net->stats.rx_packets++; +- net->stats.rx_bytes += skb->len; ++ net->stats.rx_bytes += packet->total_data_buflen; + + /* + * Pass the skb back up. Network stack will deallocate the skb when it +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0039-HID-hyperv-Properly-disconnect-the-input-device.patch b/debian/patches/features/x86/hyperv/0039-HID-hyperv-Properly-disconnect-the-input-device.patch new file mode 100644 index 000000000..7cd9bb2b9 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0039-HID-hyperv-Properly-disconnect-the-input-device.patch @@ -0,0 +1,32 @@ +From: "K. Y. Srinivasan" +Date: Wed, 18 Jan 2012 08:57:14 -0800 +Subject: [PATCH 39/77] HID: hyperv: Properly disconnect the input device + +commit c1c454b8691cc95aa83f19273ed7845914c70e83 upstream. + +When we unload the mouse driver, properly disconnect the input device. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Reported-by: Fuzhou Chen +Cc: stable +Signed-off-by: Jiri Kosina +--- + drivers/hid/hid-hyperv.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c +index 0c33ae9..4066324 100644 +--- a/drivers/hid/hid-hyperv.c ++++ b/drivers/hid/hid-hyperv.c +@@ -548,6 +548,7 @@ static int mousevsc_remove(struct hv_device *dev) + struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + + vmbus_close(dev->channel); ++ hid_hw_stop(input_dev->hid_device); + hid_destroy_device(input_dev->hid_device); + mousevsc_free_device(input_dev); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0040-net-hyperv-fix-the-issue-that-large-packets-be-dropp.patch b/debian/patches/features/x86/hyperv/0040-net-hyperv-fix-the-issue-that-large-packets-be-dropp.patch new file mode 100644 index 000000000..63372e7f5 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0040-net-hyperv-fix-the-issue-that-large-packets-be-dropp.patch @@ -0,0 +1,68 @@ +From: Wei Yongjun +Date: Wed, 1 Feb 2012 20:17:23 +0000 +Subject: [PATCH 40/77] net/hyperv: fix the issue that large packets be + dropped under bridge + +commit 4b8a8bc9249f144803d840f2f7608ee9bbf1ea51 upstream. + +The packets with size larger than 1452 will be dropped by bridge +which with two hyperv netdevice ports. This cause by hyperv netvsc +driver always copy the trailer padding to the data packet, and then +the skb received from netdevice may include wrong skb->len (20 bytes +larger than the real size normally). The captured packet may like +this: + + Ethernet II, Src: Microsof_00:00:07 (00:15:5d:00:00:07), + Dst: HewlettP_00:00:4e (00:1f:29:00:00:4e) + Destination: HewlettP_e6:00:4e (00:1f:29:00:00:4e) + Source: Microsof_f6:6d:07 (00:15:5d:f6:6d:07) + Type: IP (0x0800) + Trailer: 1415161718191A1B1C1D1E1F20212223 + Frame check sequence: 0x24252627 [incorrect, should be 0x7c2e5a5e] + +The following command help to reproduction it, and the ping ICMP +packets will be dropped by bridge. + $ ping ip -s 1453 + +This patch fixed it by removing the trailer padding from the data +packet. + +Signed-off-by: Wei Yongjun +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/rndis_filter.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index da181f9..dc2e384 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -321,6 +321,25 @@ static void rndis_filter_receive_data(struct rndis_device *dev, + data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; + + pkt->total_data_buflen -= data_offset; ++ ++ /* ++ * Make sure we got a valid RNDIS message, now total_data_buflen ++ * should be the data packet size plus the trailer padding size ++ */ ++ if (pkt->total_data_buflen < rndis_pkt->data_len) { ++ netdev_err(dev->net_dev->ndev, "rndis message buffer " ++ "overflow detected (got %u, min %u)" ++ "...dropping this message!\n", ++ pkt->total_data_buflen, rndis_pkt->data_len); ++ return; ++ } ++ ++ /* ++ * Remove the rndis trailer padding from rndis packet message ++ * rndis_pkt->data_len tell us the real data length, we only copy ++ * the data packet to the stack, without the rndis trailer padding ++ */ ++ pkt->total_data_buflen = rndis_pkt->data_len; + pkt->data = (void *)((unsigned long)pkt->data + data_offset); + + pkt->is_data_pkt = true; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0041-net-hyperv-Use-netif_tx_disable-instead-of-netif_sto.patch b/debian/patches/features/x86/hyperv/0041-net-hyperv-Use-netif_tx_disable-instead-of-netif_sto.patch new file mode 100644 index 000000000..77e1503d9 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0041-net-hyperv-Use-netif_tx_disable-instead-of-netif_sto.patch @@ -0,0 +1,60 @@ +From: Haiyang Zhang +Date: Thu, 2 Feb 2012 07:17:59 +0000 +Subject: [PATCH 41/77] net/hyperv: Use netif_tx_disable() instead of + netif_stop_queue() when necessary + +commit 0a282538cc1977655004cdb2eb25dd2b63f20637 upstream. + +For code path not on the xmit, use netif_tx_disable() instead of +netif_stop_queue() to ensure other CPUs are not doing xmit. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 9dccc7a..69193fc 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -123,7 +123,7 @@ static int netvsc_close(struct net_device *net) + struct hv_device *device_obj = net_device_ctx->device_ctx; + int ret; + +- netif_stop_queue(net); ++ netif_tx_disable(net); + + ret = rndis_filter_close(device_obj); + if (ret != 0) +@@ -256,7 +256,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, + schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); + } else { + netif_carrier_off(net); +- netif_stop_queue(net); ++ netif_tx_disable(net); + } + } + +@@ -337,7 +337,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) + + nvdev->start_remove = true; + cancel_delayed_work_sync(&ndevctx->dwork); +- netif_stop_queue(ndev); ++ netif_tx_disable(ndev); + rndis_filter_device_remove(hdev); + + ndev->mtu = mtu; +@@ -460,7 +460,7 @@ static int netvsc_remove(struct hv_device *dev) + cancel_delayed_work_sync(&ndev_ctx->dwork); + + /* Stop outbound asap */ +- netif_stop_queue(net); ++ netif_tx_disable(net); + + unregister_netdev(net); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0042-net-hyperv-Fix-the-page-buffer-when-an-RNDIS-message.patch b/debian/patches/features/x86/hyperv/0042-net-hyperv-Fix-the-page-buffer-when-an-RNDIS-message.patch new file mode 100644 index 000000000..92da39052 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0042-net-hyperv-Fix-the-page-buffer-when-an-RNDIS-message.patch @@ -0,0 +1,87 @@ +From: Haiyang Zhang +Date: Thu, 2 Feb 2012 07:18:00 +0000 +Subject: [PATCH 42/77] net/hyperv: Fix the page buffer when an RNDIS message + goes beyond page boundary + +commit c31c151b1c4a29da4dc92212aa8648fb4f8557b9 upstream. + +There is a possible data corruption if an RNDIS message goes beyond page +boundary in the sending code path. This patch fixes the problem. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 8 ++++---- + drivers/net/hyperv/rndis_filter.c | 13 +++++++++++++ + include/linux/hyperv.h | 2 +- + 3 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 69193fc..466c58a 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -151,10 +151,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + int ret; + unsigned int i, num_pages, npg_data; + +- /* Add multipage for skb->data and additional one for RNDIS */ ++ /* Add multipages for skb->data and additional 2 for RNDIS */ + npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) + >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; +- num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; ++ num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; + + /* Allocate a netvsc packet based on # of frags. */ + packet = kzalloc(sizeof(struct hv_netvsc_packet) + +@@ -173,8 +173,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + sizeof(struct hv_netvsc_packet) + + (num_pages * sizeof(struct hv_page_buffer)); + +- /* Setup the rndis header */ +- packet->page_buf_cnt = num_pages; ++ /* If the rndis msg goes beyond 1 page, we will add 1 later */ ++ packet->page_buf_cnt = num_pages - 1; + + /* Initialize it from the skb */ + packet->total_data_buflen = skb->len; +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index dc2e384..133b7fb 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -797,6 +797,19 @@ int rndis_filter_send(struct hv_device *dev, + (unsigned long)rndisMessage & (PAGE_SIZE-1); + pkt->page_buf[0].len = rndisMessageSize; + ++ /* Add one page_buf if the rndis msg goes beyond page boundary */ ++ if (pkt->page_buf[0].offset + rndisMessageSize > PAGE_SIZE) { ++ int i; ++ for (i = pkt->page_buf_cnt; i > 1; i--) ++ pkt->page_buf[i] = pkt->page_buf[i-1]; ++ pkt->page_buf_cnt++; ++ pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; ++ pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) ++ rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT; ++ pkt->page_buf[1].offset = 0; ++ pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len; ++ } ++ + /* Save the packet send completion and context */ + filterPacket->completion = pkt->completion.send.send_completion; + filterPacket->completion_ctx = +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index 62b908e..0ae065a 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -35,7 +35,7 @@ + #include + + +-#define MAX_PAGE_BUFFER_COUNT 18 ++#define MAX_PAGE_BUFFER_COUNT 19 + #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ + + #pragma pack(push, 1) +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0043-drivers-hv-kvp-Add-cleanup-connector-defines.patch b/debian/patches/features/x86/hyperv/0043-drivers-hv-kvp-Add-cleanup-connector-defines.patch new file mode 100644 index 000000000..f5d257745 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0043-drivers-hv-kvp-Add-cleanup-connector-defines.patch @@ -0,0 +1,68 @@ +From: "K. Y. Srinivasan" +Date: Fri, 27 Jan 2012 15:55:57 -0800 +Subject: [PATCH 43/77] drivers: hv: kvp: Add/cleanup connector defines + +commit 4f03a2c934894f30a64d397df8c7c4de129c5b30 upstream. + +The current KVP code carries some private connector related defines. +Update connector.h to have all the KVP defines. As part of this patch +get rid of some unused defines. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.h | 3 --- + include/linux/connector.h | 1 + + tools/hv/hv_kvp_daemon.c | 4 ---- + 3 files changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h +index 9b765d7..c2c5bba 100644 +--- a/drivers/hv/hv_kvp.h ++++ b/drivers/hv/hv_kvp.h +@@ -107,9 +107,6 @@ + * 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 */ +diff --git a/include/linux/connector.h b/include/linux/connector.h +index 3c9c54f..7638407 100644 +--- a/include/linux/connector.h ++++ b/include/linux/connector.h +@@ -43,6 +43,7 @@ + #define CN_IDX_DRBD 0x8 + #define CN_VAL_DRBD 0x1 + #define CN_KVP_IDX 0x9 /* HyperV KVP */ ++#define CN_KVP_VAL 0x1 /* queries from the kernel */ + + #define CN_NETLINK_USERS 10 /* Highest index + 1 */ + +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index 11224ed..2b6a2d9 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -40,15 +40,11 @@ + #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 +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0044-drivers-hv-kvp-Move-the-contents-of-hv_kvp.h-to-hype.patch b/debian/patches/features/x86/hyperv/0044-drivers-hv-kvp-Move-the-contents-of-hv_kvp.h-to-hype.patch new file mode 100644 index 000000000..b53431506 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0044-drivers-hv-kvp-Move-the-contents-of-hv_kvp.h-to-hype.patch @@ -0,0 +1,420 @@ +From: "K. Y. Srinivasan" +Date: Fri, 27 Jan 2012 15:55:58 -0800 +Subject: [PATCH 44/77] drivers: hv: kvp: Move the contents of hv_kvp.h to + hyperv.h + +commit 2939437ce8f2de07237eb2bcce29b6a699bfe799 upstream. + +In preparation for consolidating all KVP related defines into a single header file +that both the kernel and user level components can use, move the contents of +hv_kvp.h into hyperv.h. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 2 - + drivers/hv/hv_kvp.h | 181 ------------------------------------------------ + drivers/hv/hv_util.c | 3 - + include/linux/hyperv.h | 165 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 165 insertions(+), 186 deletions(-) + delete mode 100644 drivers/hv/hv_kvp.h + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index 0e8343f..4a6971e 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -28,8 +28,6 @@ + #include + #include + +-#include "hv_kvp.h" +- + + + /* +diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h +deleted file mode 100644 +index c2c5bba..0000000 +--- a/drivers/hv/hv_kvp.h ++++ /dev/null +@@ -1,181 +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. +- */ +- +-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 +index 55d58f2..dbb8b8e 100644 +--- a/drivers/hv/hv_util.c ++++ b/drivers/hv/hv_util.c +@@ -28,9 +28,6 @@ + #include + #include + +-#include "hv_kvp.h" +- +- + static void shutdown_onchannelcallback(void *context); + static struct hv_util_service util_shutdown = { + .util_cb = shutdown_onchannelcallback, +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index 0ae065a..c445ead 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -25,6 +25,166 @@ + #ifndef _HYPERV_H + #define _HYPERV_H + ++#include ++ ++/* ++ * An implementation of HyperV key value pair (KVP) functionality for Linux. ++ * ++ * ++ * Copyright (C) 2010, Novell, Inc. ++ * Author : K. Y. Srinivasan ++ * ++ */ ++ ++/* ++ * 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. ++ */ ++ ++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; ++}; ++ + #include + #include + #include +@@ -870,4 +1030,9 @@ struct hyperv_service_callback { + extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, + struct icmsg_negotiate *, u8 *); + ++int hv_kvp_init(struct hv_util_service *); ++void hv_kvp_deinit(void); ++void hv_kvp_onchannelcallback(void *); ++ ++#endif /* __KERNEL__ */ + #endif /* _HYPERV_H */ +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0045-net-hyperv-Convert-camel-cased-variables-in-rndis_fi.patch b/debian/patches/features/x86/hyperv/0045-net-hyperv-Convert-camel-cased-variables-in-rndis_fi.patch new file mode 100644 index 000000000..e8fe57486 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0045-net-hyperv-Convert-camel-cased-variables-in-rndis_fi.patch @@ -0,0 +1,140 @@ +From: Haiyang Zhang +Date: Sun, 5 Feb 2012 12:13:08 +0000 +Subject: [PATCH 45/77] net/hyperv: Convert camel cased variables in + rndis_filter.c to lower cases + +commit 5fccab3b66d53883a97fc65e0c33f3ebf74e8ff9 upstream. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Reviewed-by: Jesper Juhl +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/rndis_filter.c | 60 ++++++++++++++++++------------------- + 1 file changed, 30 insertions(+), 30 deletions(-) + +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index 133b7fb..a60e5e2 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -758,66 +758,66 @@ int rndis_filter_open(struct hv_device *dev) + + int rndis_filter_close(struct hv_device *dev) + { +- struct netvsc_device *netDevice = hv_get_drvdata(dev); ++ struct netvsc_device *nvdev = hv_get_drvdata(dev); + +- if (!netDevice) ++ if (!nvdev) + return -EINVAL; + +- return rndis_filter_close_device(netDevice->extension); ++ return rndis_filter_close_device(nvdev->extension); + } + + int rndis_filter_send(struct hv_device *dev, + struct hv_netvsc_packet *pkt) + { + int ret; +- struct rndis_filter_packet *filterPacket; +- struct rndis_message *rndisMessage; +- struct rndis_packet *rndisPacket; +- u32 rndisMessageSize; ++ struct rndis_filter_packet *filter_pkt; ++ struct rndis_message *rndis_msg; ++ struct rndis_packet *rndis_pkt; ++ u32 rndis_msg_size; + + /* Add the rndis header */ +- filterPacket = (struct rndis_filter_packet *)pkt->extension; ++ filter_pkt = (struct rndis_filter_packet *)pkt->extension; + +- memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); ++ memset(filter_pkt, 0, sizeof(struct rndis_filter_packet)); + +- rndisMessage = &filterPacket->msg; +- rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); ++ rndis_msg = &filter_pkt->msg; ++ rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); + +- rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; +- rndisMessage->msg_len = pkt->total_data_buflen + +- rndisMessageSize; ++ rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; ++ rndis_msg->msg_len = pkt->total_data_buflen + ++ rndis_msg_size; + +- rndisPacket = &rndisMessage->msg.pkt; +- rndisPacket->data_offset = sizeof(struct rndis_packet); +- rndisPacket->data_len = pkt->total_data_buflen; ++ rndis_pkt = &rndis_msg->msg.pkt; ++ rndis_pkt->data_offset = sizeof(struct rndis_packet); ++ rndis_pkt->data_len = pkt->total_data_buflen; + + pkt->is_data_pkt = true; +- pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; ++ pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; + pkt->page_buf[0].offset = +- (unsigned long)rndisMessage & (PAGE_SIZE-1); +- pkt->page_buf[0].len = rndisMessageSize; ++ (unsigned long)rndis_msg & (PAGE_SIZE-1); ++ pkt->page_buf[0].len = rndis_msg_size; + + /* Add one page_buf if the rndis msg goes beyond page boundary */ +- if (pkt->page_buf[0].offset + rndisMessageSize > PAGE_SIZE) { ++ if (pkt->page_buf[0].offset + rndis_msg_size > PAGE_SIZE) { + int i; + for (i = pkt->page_buf_cnt; i > 1; i--) + pkt->page_buf[i] = pkt->page_buf[i-1]; + pkt->page_buf_cnt++; + pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; + pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) +- rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT; ++ rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT; + pkt->page_buf[1].offset = 0; +- pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len; ++ pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len; + } + + /* Save the packet send completion and context */ +- filterPacket->completion = pkt->completion.send.send_completion; +- filterPacket->completion_ctx = ++ filter_pkt->completion = pkt->completion.send.send_completion; ++ filter_pkt->completion_ctx = + pkt->completion.send.send_completion_ctx; + + /* Use ours */ + pkt->completion.send.send_completion = rndis_filter_send_completion; +- pkt->completion.send.send_completion_ctx = filterPacket; ++ pkt->completion.send.send_completion_ctx = filter_pkt; + + ret = netvsc_send(dev, pkt); + if (ret != 0) { +@@ -826,9 +826,9 @@ int rndis_filter_send(struct hv_device *dev, + * above + */ + pkt->completion.send.send_completion = +- filterPacket->completion; ++ filter_pkt->completion; + pkt->completion.send.send_completion_ctx = +- filterPacket->completion_ctx; ++ filter_pkt->completion_ctx; + } + + return ret; +@@ -836,10 +836,10 @@ int rndis_filter_send(struct hv_device *dev, + + static void rndis_filter_send_completion(void *ctx) + { +- struct rndis_filter_packet *filterPacket = ctx; ++ struct rndis_filter_packet *filter_pkt = ctx; + + /* Pass it back to the original handler */ +- filterPacket->completion(filterPacket->completion_ctx); ++ filter_pkt->completion(filter_pkt->completion_ctx); + } + + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0046-net-hyperv-Correct-the-assignment-in-netvsc_recv_cal.patch b/debian/patches/features/x86/hyperv/0046-net-hyperv-Correct-the-assignment-in-netvsc_recv_cal.patch new file mode 100644 index 000000000..53fc24587 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0046-net-hyperv-Correct-the-assignment-in-netvsc_recv_cal.patch @@ -0,0 +1,40 @@ +From: Haiyang Zhang +Date: Sun, 5 Feb 2012 12:13:09 +0000 +Subject: [PATCH 46/77] net/hyperv: Correct the assignment in + netvsc_recv_callback() + +commit 6f4c44460750dd4eb9926a58ab1ad0ceacef8284 upstream. + +The first assignment to variable "net" is wrong, but overridden by the +latter assignments. So the bug isn't manifested. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 466c58a..0ae7a1a 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -267,13 +267,10 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, + int netvsc_recv_callback(struct hv_device *device_obj, + struct hv_netvsc_packet *packet) + { +- struct net_device *net = dev_get_drvdata(&device_obj->device); ++ struct net_device *net; + struct sk_buff *skb; +- struct netvsc_device *net_device; +- +- net_device = hv_get_drvdata(device_obj); +- net = net_device->ndev; + ++ net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev; + if (!net) { + netdev_err(net, "got receive callback but net device" + " not initialized yet\n"); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0047-net-hyperv-Remove-the-unnecessary-memset-in-rndis_fi.patch b/debian/patches/features/x86/hyperv/0047-net-hyperv-Remove-the-unnecessary-memset-in-rndis_fi.patch new file mode 100644 index 000000000..dff777c20 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0047-net-hyperv-Remove-the-unnecessary-memset-in-rndis_fi.patch @@ -0,0 +1,33 @@ +From: Haiyang Zhang +Date: Sun, 5 Feb 2012 12:13:10 +0000 +Subject: [PATCH 47/77] net/hyperv: Remove the unnecessary memset in + rndis_filter_send() + +commit bce60806de882cf15ad3a80a51e9878863a8fced upstream. + +The memory has been allocated by kzalloc, so it's unnecessary to memset +again. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/rndis_filter.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index a60e5e2..136efd8 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -778,8 +778,6 @@ int rndis_filter_send(struct hv_device *dev, + /* Add the rndis header */ + filter_pkt = (struct rndis_filter_packet *)pkt->extension; + +- memset(filter_pkt, 0, sizeof(struct rndis_filter_packet)); +- + rndis_msg = &filter_pkt->msg; + rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0048-Staging-hv-storvsc-Cleanup-some-comments.patch b/debian/patches/features/x86/hyperv/0048-Staging-hv-storvsc-Cleanup-some-comments.patch new file mode 100644 index 000000000..f87dfe285 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0048-Staging-hv-storvsc-Cleanup-some-comments.patch @@ -0,0 +1,170 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:54 -0800 +Subject: [PATCH 48/77] Staging: hv: storvsc: Cleanup some comments + +commit 09f0355f0612520820ae4b2c342a26e048bef6e7 upstream. + +Use consistent format for comments and get rid of some unnecessary +comments. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 57 +++++++++++++++++--------------------- + 1 file changed, 26 insertions(+), 31 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index eb853f7..1633b03 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -50,8 +50,10 @@ static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; + module_param(storvsc_ringbuffer_size, int, S_IRUGO); + MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + +-/* to alert the user that structure sizes may be mismatched even though the */ +-/* protocol versions match. */ ++/* ++ * To alert the user that structure sizes may be mismatched even though the ++ * protocol versions match. ++ */ + + + #define REVISION_STRING(REVISION_) #REVISION_ +@@ -68,26 +70,36 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + } \ + } while (0) + +-/* Major/minor macros. Minor version is in LSB, meaning that earlier flat */ +-/* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */ ++/* ++ * Major/minor macros. Minor version is in LSB, meaning that earlier flat ++ * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). ++ */ ++ + #define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) + #define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) + #define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff))) + #define VMSTOR_INVALID_PROTOCOL_VERSION (-1) + +-/* Version history: */ +-/* V1 Beta 0.1 */ +-/* V1 RC < 2008/1/31 1.0 */ +-/* V1 RC > 2008/1/31 2.0 */ ++/* ++ * Version history: ++ * V1 Beta: 0.1 ++ * V1 RC < 2008/1/31: 1.0 ++ * V1 RC > 2008/1/31: 2.0 ++ * Win7: 4.2 ++ */ ++ + #define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(4, 2) + + + + +-/* This will get replaced with the max transfer length that is possible on */ +-/* the host adapter. */ +-/* The max transfer length will be published when we offer a vmbus channel. */ ++/* ++ * This will get replaced with the max transfer length that is possible on ++ * the host adapter. ++ * The max transfer length will be published when we offer a vmbus channel. ++ */ ++ + #define MAX_TRANSFER_LENGTH 0x40000 + #define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ + sizeof(struct vstor_packet) + \ +@@ -211,18 +223,19 @@ struct vstor_packet { + }; + } __packed; + +-/* Packet flags */ + /* ++ * Packet Flags: ++ * + * This flag indicates that the server should send back a completion for this + * packet. + */ ++ + #define REQUEST_COMPLETION_FLAG 0x1 + + /* This is the set of flags that the vsc can set in any packets it sends */ + #define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) + + +-/* Defines */ + + #define STORVSC_MAX_IO_REQUESTS 128 + +@@ -674,7 +687,6 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) + + memset(&props, 0, sizeof(struct vmstorage_channel_properties)); + +- /* Open the channel */ + ret = vmbus_open(device->channel, + ring_size, + ring_size, +@@ -1154,9 +1166,6 @@ static int storvsc_host_reset(struct hv_device *device) + } + + +-/* +- * storvsc_host_reset_handler - Reset the scsi HBA +- */ + static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + { + struct hv_host_device *host_dev = shost_priv(scmnd->device->host); +@@ -1166,9 +1175,6 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + } + + +-/* +- * storvsc_command_completion - Command completion processing +- */ + static void storvsc_command_completion(struct hv_storvsc_request *request) + { + struct storvsc_cmd_request *cmd_request = +@@ -1262,9 +1268,6 @@ static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) + return allowed; + } + +-/* +- * storvsc_queuecommand - Initiate command processing +- */ + static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + { + int ret; +@@ -1407,7 +1410,6 @@ retry_request: + return ret; + } + +-/* Scsi driver */ + static struct scsi_host_template scsi_driver = { + .module = THIS_MODULE, + .name = "storvsc_host_t", +@@ -1448,11 +1450,6 @@ static const struct hv_vmbus_device_id id_table[] = { + + MODULE_DEVICE_TABLE(vmbus, id_table); + +- +-/* +- * storvsc_probe - Add a new device for this driver +- */ +- + static int storvsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) + { +@@ -1542,8 +1539,6 @@ err_out0: + return ret; + } + +-/* The one and only one */ +- + static struct hv_driver storvsc_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0049-Staging-hv-storvsc-Cleanup-storvsc_probe.patch b/debian/patches/features/x86/hyperv/0049-Staging-hv-storvsc-Cleanup-storvsc_probe.patch new file mode 100644 index 000000000..fb0a33f6f --- /dev/null +++ b/debian/patches/features/x86/hyperv/0049-Staging-hv-storvsc-Cleanup-storvsc_probe.patch @@ -0,0 +1,80 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:55 -0800 +Subject: [PATCH 49/77] Staging: hv: storvsc: Cleanup storvsc_probe() + +commit 59d22950b274182b006e4071d6690bfbc94d7267 upstream. + +Cleanup storvsc_probe(). As part of this cleanup, get rid of +storvsc_get_ide_info() by inlining the necessary code. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 30 ++++++++---------------------- + 1 file changed, 8 insertions(+), 22 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 1633b03..7561d29 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -791,18 +791,6 @@ static int storvsc_do_io(struct hv_device *device, + return ret; + } + +-static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) +-{ +- *target = +- dev->dev_instance.b[5] << 8 | dev->dev_instance.b[4]; +- +- *path = +- dev->dev_instance.b[3] << 24 | +- dev->dev_instance.b[2] << 16 | +- dev->dev_instance.b[1] << 8 | dev->dev_instance.b[0]; +-} +- +- + static int storvsc_device_alloc(struct scsi_device *sdevice) + { + struct stor_mem_pools *memp; +@@ -1457,7 +1445,6 @@ static int storvsc_probe(struct hv_device *device, + struct Scsi_Host *host; + struct hv_host_device *host_dev; + bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); +- int path = 0; + int target = 0; + struct storvsc_device *stor_device; + +@@ -1490,9 +1477,6 @@ static int storvsc_probe(struct hv_device *device, + if (ret) + goto err_out1; + +- if (dev_is_ide) +- storvsc_get_ide_info(device, &target, &path); +- + host_dev->path = stor_device->path_id; + host_dev->target = stor_device->target_id; + +@@ -1512,12 +1496,14 @@ static int storvsc_probe(struct hv_device *device, + + if (!dev_is_ide) { + scsi_scan_host(host); +- return 0; +- } +- ret = scsi_add_device(host, 0, target, 0); +- if (ret) { +- scsi_remove_host(host); +- goto err_out2; ++ } else { ++ target = (device->dev_instance.b[5] << 8 | ++ device->dev_instance.b[4]); ++ ret = scsi_add_device(host, 0, target, 0); ++ if (ret) { ++ scsi_remove_host(host); ++ goto err_out2; ++ } + } + return 0; + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0050-Staging-hv-storvsc-Cleanup-storvsc_queuecommand.patch b/debian/patches/features/x86/hyperv/0050-Staging-hv-storvsc-Cleanup-storvsc_queuecommand.patch new file mode 100644 index 000000000..3223509b4 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0050-Staging-hv-storvsc-Cleanup-storvsc_queuecommand.patch @@ -0,0 +1,129 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:56 -0800 +Subject: [PATCH 50/77] Staging: hv: storvsc: Cleanup storvsc_queuecommand() + +commit c77b63b639f52b2e92d0bbaa94f33a78939f3e7c upstream. + +Cleanup storvsc_queuecommand(). As part of this cleanup, rename the function to +check if the scsi command can be sent to the host, consolidate error recovery +and get rid of some dead code. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 48 +++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 26 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 7561d29..71e50c3 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1239,13 +1239,16 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + mempool_free(cmd_request, memp->request_mempool); + } + +-static bool storvsc_check_scsi_cmd(struct scsi_cmnd *scmnd) ++static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) + { + bool allowed = true; + u8 scsi_op = scmnd->cmnd[0]; + + switch (scsi_op) { +- /* smartd sends this command, which will offline the device */ ++ /* ++ * smartd sends this command and the host does not handle ++ * this. So, don't send it. ++ */ + case SET_WINDOW: + scmnd->result = ILLEGAL_REQUEST << 16; + allowed = false; +@@ -1270,32 +1273,26 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + struct vmscsi_request *vm_srb; + struct stor_mem_pools *memp = scmnd->device->hostdata; + +- if (storvsc_check_scsi_cmd(scmnd) == false) { ++ if (!storvsc_scsi_cmd_ok(scmnd)) { + scmnd->scsi_done(scmnd); + return 0; + } + +- /* If retrying, no need to prep the cmd */ +- if (scmnd->host_scribble) { +- +- cmd_request = +- (struct storvsc_cmd_request *)scmnd->host_scribble; +- +- goto retry_request; +- } +- + request_size = sizeof(struct storvsc_cmd_request); + + cmd_request = mempool_alloc(memp->request_mempool, + GFP_ATOMIC); ++ ++ /* ++ * We might be invoked in an interrupt context; hence ++ * mempool_alloc() can fail. ++ */ + if (!cmd_request) + return SCSI_MLQUEUE_DEVICE_BUSY; + + memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); + + /* Setup the cmd request */ +- cmd_request->bounce_sgl_count = 0; +- cmd_request->bounce_sgl = NULL; + cmd_request->cmd = scmnd; + + scmnd->host_scribble = (unsigned char *)cmd_request; +@@ -1344,11 +1341,8 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + scsi_bufflen(scmnd), + vm_srb->data_in); + if (!cmd_request->bounce_sgl) { +- scmnd->host_scribble = NULL; +- mempool_free(cmd_request, +- memp->request_mempool); +- +- return SCSI_MLQUEUE_HOST_BUSY; ++ ret = SCSI_MLQUEUE_HOST_BUSY; ++ goto queue_error; + } + + cmd_request->bounce_sgl_count = +@@ -1377,24 +1371,26 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT; + } + +-retry_request: + /* Invokes the vsc to start an IO */ + ret = storvsc_do_io(dev, &cmd_request->request); + + if (ret == -EAGAIN) { + /* no more space */ + +- if (cmd_request->bounce_sgl_count) ++ if (cmd_request->bounce_sgl_count) { + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + +- mempool_free(cmd_request, memp->request_mempool); +- +- scmnd->host_scribble = NULL; +- +- ret = SCSI_MLQUEUE_DEVICE_BUSY; ++ ret = SCSI_MLQUEUE_DEVICE_BUSY; ++ goto queue_error; ++ } + } + ++ return 0; ++ ++queue_error: ++ mempool_free(cmd_request, memp->request_mempool); ++ scmnd->host_scribble = NULL; + return ret; + } + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0051-Staging-hv-storvsc-Introduce-defines-for-srb-status-.patch b/debian/patches/features/x86/hyperv/0051-Staging-hv-storvsc-Introduce-defines-for-srb-status-.patch new file mode 100644 index 000000000..bd43b5435 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0051-Staging-hv-storvsc-Introduce-defines-for-srb-status-.patch @@ -0,0 +1,85 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:57 -0800 +Subject: [PATCH 51/77] Staging: hv: storvsc: Introduce defines for srb status + codes + +commit 160463204f541cea112fd4d68422f297bd98ec51 upstream. + +Introduce defines for srb status codes. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 71e50c3..979f25b 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -257,6 +257,16 @@ enum storvsc_request_type { + UNKNOWN_TYPE, + }; + ++/* ++ * SRB status codes and masks; a subset of the codes used here. ++ */ ++ ++#define SRB_STATUS_AUTOSENSE_VALID 0x80 ++#define SRB_STATUS_INVALID_LUN 0x20 ++#define SRB_STATUS_SUCCESS 0x01 ++#define SRB_STATUS_ERROR 0x04 ++ ++ + + struct hv_storvsc_request { + struct hv_device *device; +@@ -561,7 +571,7 @@ static void storvsc_on_io_completion(struct hv_device *device, + if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || + (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { + vstor_packet->vm_srb.scsi_status = 0; +- vstor_packet->vm_srb.srb_status = 0x1; ++ vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; + } + + +@@ -572,7 +582,7 @@ static void storvsc_on_io_completion(struct hv_device *device, + vstor_packet->vm_srb.sense_info_length; + + if (vstor_packet->vm_srb.scsi_status != 0 || +- vstor_packet->vm_srb.srb_status != 1){ ++ vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS){ + dev_warn(&device->device, + "cmd 0x%x scsi status 0x%x srb status 0x%x\n", + stor_pkt->vm_srb.cdb[0], +@@ -582,7 +592,8 @@ static void storvsc_on_io_completion(struct hv_device *device, + + if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { + /* CHECK_CONDITION */ +- if (vstor_packet->vm_srb.srb_status & 0x80) { ++ if (vstor_packet->vm_srb.srb_status & ++ SRB_STATUS_AUTOSENSE_VALID) { + /* autosense data available */ + dev_warn(&device->device, + "stor pkt %p autosense data valid - len %d\n", +@@ -1191,7 +1202,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + * error recovery strategies would have already been + * deployed on the host side. + */ +- if (vm_srb->srb_status == 0x4) ++ if (vm_srb->srb_status == SRB_STATUS_ERROR) + scmnd->result = DID_TARGET_FAILURE << 16; + else + scmnd->result = vm_srb->scsi_status; +@@ -1199,7 +1210,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + /* + * If the LUN is invalid; remove the device. + */ +- if (vm_srb->srb_status == 0x20) { ++ if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { + struct storvsc_device *stor_dev; + struct hv_device *dev = host_dev->dev; + struct Scsi_Host *host; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0052-Staging-hv-storvsc-Cleanup-storvsc_host_reset_handle.patch b/debian/patches/features/x86/hyperv/0052-Staging-hv-storvsc-Cleanup-storvsc_host_reset_handle.patch new file mode 100644 index 000000000..e7f3cfe17 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0052-Staging-hv-storvsc-Cleanup-storvsc_host_reset_handle.patch @@ -0,0 +1,51 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:58 -0800 +Subject: [PATCH 52/77] Staging: hv: storvsc: Cleanup + storvsc_host_reset_handler() + +commit 4b270c8b23722128adedb5694097aea367284ce1 upstream. + +Cleanup storvsc_host_reset_handler() by getting rid of storvsc_host_reset(). + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 979f25b..8340387 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1122,8 +1122,11 @@ static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, + return 0; + } + +-static int storvsc_host_reset(struct hv_device *device) ++static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + { ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); ++ struct hv_device *device = host_dev->dev; ++ + struct storvsc_device *stor_device; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; +@@ -1165,15 +1168,6 @@ static int storvsc_host_reset(struct hv_device *device) + } + + +-static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) +-{ +- struct hv_host_device *host_dev = shost_priv(scmnd->device->host); +- struct hv_device *dev = host_dev->dev; +- +- return storvsc_host_reset(dev); +-} +- +- + static void storvsc_command_completion(struct hv_storvsc_request *request) + { + struct storvsc_cmd_request *cmd_request = +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0053-Staging-hv-storvsc-Move-and-cleanup-storvsc_remove.patch b/debian/patches/features/x86/hyperv/0053-Staging-hv-storvsc-Move-and-cleanup-storvsc_remove.patch new file mode 100644 index 000000000..1f03117f4 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0053-Staging-hv-storvsc-Move-and-cleanup-storvsc_remove.patch @@ -0,0 +1,65 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:37:59 -0800 +Subject: [PATCH 53/77] Staging: hv: storvsc: Move and cleanup + storvsc_remove() + +commit ddcbf65e484c3473d3e808718ddb6c8166822c4c upstream. + +Relocate the storvsc_remove() function to a different location in the file +and invoke scsi_host_put() only after all the cleanup. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 28 ++++++++++++---------------- + 1 file changed, 12 insertions(+), 16 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 8340387..e0e471c 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -1083,22 +1083,6 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, + return total_copied; + } + +- +-static int storvsc_remove(struct hv_device *dev) +-{ +- struct storvsc_device *stor_device = hv_get_drvdata(dev); +- struct Scsi_Host *host = stor_device->host; +- +- scsi_remove_host(host); +- +- scsi_host_put(host); +- +- storvsc_dev_remove(dev); +- +- return 0; +-} +- +- + static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, + sector_t capacity, int *info) + { +@@ -1526,6 +1510,18 @@ err_out0: + return ret; + } + ++static int storvsc_remove(struct hv_device *dev) ++{ ++ struct storvsc_device *stor_device = hv_get_drvdata(dev); ++ struct Scsi_Host *host = stor_device->host; ++ ++ scsi_remove_host(host); ++ storvsc_dev_remove(dev); ++ scsi_host_put(host); ++ ++ return 0; ++} ++ + static struct hv_driver storvsc_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0054-Staging-hv-storvsc-Add-a-comment-to-explain-life-cyc.patch b/debian/patches/features/x86/hyperv/0054-Staging-hv-storvsc-Add-a-comment-to-explain-life-cyc.patch new file mode 100644 index 000000000..bac58d90b --- /dev/null +++ b/debian/patches/features/x86/hyperv/0054-Staging-hv-storvsc-Add-a-comment-to-explain-life-cyc.patch @@ -0,0 +1,53 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:00 -0800 +Subject: [PATCH 54/77] Staging: hv: storvsc: Add a comment to explain + life-cycle management + +commit a8c18c573bf896f81dbcd68fdb81d81d4200dcad upstream. + +Add a comment to explain life-cycle management and fix format issue. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index e0e471c..204b3ca 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -375,6 +375,21 @@ done: + kfree(wrk); + } + ++/* ++ * We can get incoming messages from the host that are not in response to ++ * messages that we have sent out. An example of this would be messages ++ * received by the guest to notify dynamic addition/removal of LUNs. To ++ * deal with potential race conditions where the driver may be in the ++ * midst of being unloaded when we might receive an unsolicited message ++ * from the host, we have implemented a mechanism to gurantee sequential ++ * consistency: ++ * ++ * 1) Once the device is marked as being destroyed, we will fail all ++ * outgoing messages. ++ * 2) We permit incoming messages when the device is being destroyed, ++ * only to properly account for messages already sent out. ++ */ ++ + static inline struct storvsc_device *get_out_stor_device( + struct hv_device *device) + { +@@ -569,7 +584,7 @@ static void storvsc_on_io_completion(struct hv_device *device, + */ + + if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || +- (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { ++ (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { + vstor_packet->vm_srb.scsi_status = 0; + vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; + } +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0055-Staging-hv-storvsc-Get-rid-of-the-on_io_completion-i.patch b/debian/patches/features/x86/hyperv/0055-Staging-hv-storvsc-Get-rid-of-the-on_io_completion-i.patch new file mode 100644 index 000000000..4b8b4d0b0 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0055-Staging-hv-storvsc-Get-rid-of-the-on_io_completion-i.patch @@ -0,0 +1,706 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:01 -0800 +Subject: [PATCH 55/77] Staging: hv: storvsc: Get rid of the on_io_completion + in hv_storvsc_request + +commit 2707388c7c5c6edf4e646a9013f6b7aa195fb174 upstream. + +Get rid of the on_io_completion field in struct hv_storvsc_request. As part of this +relocate the bounce buffer handling code (to avoid having forward declarations). + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 630 +++++++++++++++++++------------------- + 1 file changed, 313 insertions(+), 317 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 204b3ca..7c9fa19 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -276,7 +276,6 @@ struct hv_storvsc_request { + + unsigned char *sense_buffer; + void *context; +- void (*on_io_completion)(struct hv_storvsc_request *request); + struct hv_multipage_buffer data_buffer; + + struct vstor_packet vstor_packet; +@@ -436,6 +435,227 @@ get_in_err: + + } + ++static void destroy_bounce_buffer(struct scatterlist *sgl, ++ unsigned int sg_count) ++{ ++ int i; ++ struct page *page_buf; ++ ++ for (i = 0; i < sg_count; i++) { ++ page_buf = sg_page((&sgl[i])); ++ if (page_buf != NULL) ++ __free_page(page_buf); ++ } ++ ++ kfree(sgl); ++} ++ ++static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) ++{ ++ int i; ++ ++ /* No need to check */ ++ if (sg_count < 2) ++ return -1; ++ ++ /* We have at least 2 sg entries */ ++ for (i = 0; i < sg_count; i++) { ++ if (i == 0) { ++ /* make sure 1st one does not have hole */ ++ if (sgl[i].offset + sgl[i].length != PAGE_SIZE) ++ return i; ++ } else if (i == sg_count - 1) { ++ /* make sure last one does not have hole */ ++ if (sgl[i].offset != 0) ++ return i; ++ } else { ++ /* make sure no hole in the middle */ ++ if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) ++ return i; ++ } ++ } ++ return -1; ++} ++ ++static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, ++ unsigned int sg_count, ++ unsigned int len, ++ int write) ++{ ++ int i; ++ int num_pages; ++ struct scatterlist *bounce_sgl; ++ struct page *page_buf; ++ unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); ++ ++ num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; ++ ++ bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); ++ if (!bounce_sgl) ++ return NULL; ++ ++ for (i = 0; i < num_pages; i++) { ++ page_buf = alloc_page(GFP_ATOMIC); ++ if (!page_buf) ++ goto cleanup; ++ sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); ++ } ++ ++ return bounce_sgl; ++ ++cleanup: ++ destroy_bounce_buffer(bounce_sgl, num_pages); ++ return NULL; ++} ++ ++/* Assume the original sgl has enough room */ ++static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, ++ struct scatterlist *bounce_sgl, ++ unsigned int orig_sgl_count, ++ unsigned int bounce_sgl_count) ++{ ++ int i; ++ int j = 0; ++ unsigned long src, dest; ++ unsigned int srclen, destlen, copylen; ++ unsigned int total_copied = 0; ++ unsigned long bounce_addr = 0; ++ unsigned long dest_addr = 0; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ for (i = 0; i < orig_sgl_count; i++) { ++ dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), ++ KM_IRQ0) + orig_sgl[i].offset; ++ dest = dest_addr; ++ destlen = orig_sgl[i].length; ++ ++ if (bounce_addr == 0) ++ bounce_addr = ++ (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), ++ KM_IRQ0); ++ ++ while (destlen) { ++ src = bounce_addr + bounce_sgl[j].offset; ++ srclen = bounce_sgl[j].length - bounce_sgl[j].offset; ++ ++ copylen = min(srclen, destlen); ++ memcpy((void *)dest, (void *)src, copylen); ++ ++ total_copied += copylen; ++ bounce_sgl[j].offset += copylen; ++ destlen -= copylen; ++ dest += copylen; ++ ++ if (bounce_sgl[j].offset == bounce_sgl[j].length) { ++ /* full */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ j++; ++ ++ /* ++ * It is possible that the number of elements ++ * in the bounce buffer may not be equal to ++ * the number of elements in the original ++ * scatter list. Handle this correctly. ++ */ ++ ++ if (j == bounce_sgl_count) { ++ /* ++ * We are done; cleanup and return. ++ */ ++ kunmap_atomic((void *)(dest_addr - ++ orig_sgl[i].offset), ++ KM_IRQ0); ++ local_irq_restore(flags); ++ return total_copied; ++ } ++ ++ /* if we need to use another bounce buffer */ ++ if (destlen || i != orig_sgl_count - 1) ++ bounce_addr = ++ (unsigned long)kmap_atomic( ++ sg_page((&bounce_sgl[j])), KM_IRQ0); ++ } else if (destlen == 0 && i == orig_sgl_count - 1) { ++ /* unmap the last bounce that is < PAGE_SIZE */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ } ++ } ++ ++ kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), ++ KM_IRQ0); ++ } ++ ++ local_irq_restore(flags); ++ ++ return total_copied; ++} ++ ++/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ ++static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, ++ struct scatterlist *bounce_sgl, ++ unsigned int orig_sgl_count) ++{ ++ int i; ++ int j = 0; ++ unsigned long src, dest; ++ unsigned int srclen, destlen, copylen; ++ unsigned int total_copied = 0; ++ unsigned long bounce_addr = 0; ++ unsigned long src_addr = 0; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ for (i = 0; i < orig_sgl_count; i++) { ++ src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), ++ KM_IRQ0) + orig_sgl[i].offset; ++ src = src_addr; ++ srclen = orig_sgl[i].length; ++ ++ if (bounce_addr == 0) ++ bounce_addr = ++ (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), ++ KM_IRQ0); ++ ++ while (srclen) { ++ /* assume bounce offset always == 0 */ ++ dest = bounce_addr + bounce_sgl[j].length; ++ destlen = PAGE_SIZE - bounce_sgl[j].length; ++ ++ copylen = min(srclen, destlen); ++ memcpy((void *)dest, (void *)src, copylen); ++ ++ total_copied += copylen; ++ bounce_sgl[j].length += copylen; ++ srclen -= copylen; ++ src += copylen; ++ ++ if (bounce_sgl[j].length == PAGE_SIZE) { ++ /* full..move to next entry */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ j++; ++ ++ /* if we need to use another bounce buffer */ ++ if (srclen || i != orig_sgl_count - 1) ++ bounce_addr = ++ (unsigned long)kmap_atomic( ++ sg_page((&bounce_sgl[j])), KM_IRQ0); ++ ++ } else if (srclen == 0 && i == orig_sgl_count - 1) { ++ /* unmap the last bounce that is < PAGE_SIZE */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ } ++ } ++ ++ kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); ++ } ++ ++ local_irq_restore(flags); ++ ++ return total_copied; ++} ++ + static int storvsc_channel_init(struct hv_device *device) + { + struct storvsc_device *stor_device; +@@ -562,24 +782,101 @@ cleanup: + return ret; + } + +-static void storvsc_on_io_completion(struct hv_device *device, +- struct vstor_packet *vstor_packet, +- struct hv_storvsc_request *request) ++ ++static void storvsc_command_completion(struct hv_storvsc_request *request) + { +- struct storvsc_device *stor_device; +- struct vstor_packet *stor_pkt; ++ struct storvsc_cmd_request *cmd_request = ++ (struct storvsc_cmd_request *)request->context; ++ struct scsi_cmnd *scmnd = cmd_request->cmd; ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); ++ void (*scsi_done_fn)(struct scsi_cmnd *); ++ struct scsi_sense_hdr sense_hdr; ++ struct vmscsi_request *vm_srb; ++ struct storvsc_scan_work *wrk; ++ struct stor_mem_pools *memp = scmnd->device->hostdata; + +- stor_device = hv_get_drvdata(device); +- stor_pkt = &request->vstor_packet; ++ vm_srb = &request->vstor_packet.vm_srb; ++ if (cmd_request->bounce_sgl_count) { ++ if (vm_srb->data_in == READ_TYPE) ++ copy_from_bounce_buffer(scsi_sglist(scmnd), ++ cmd_request->bounce_sgl, ++ scsi_sg_count(scmnd), ++ cmd_request->bounce_sgl_count); ++ destroy_bounce_buffer(cmd_request->bounce_sgl, ++ cmd_request->bounce_sgl_count); ++ } + + /* +- * The current SCSI handling on the host side does +- * not correctly handle: +- * INQUIRY command with page code parameter set to 0x80 +- * MODE_SENSE command with cmd[2] == 0x1c +- * +- * Setup srb and scsi status so this won't be fatal. +- * We do this so we can distinguish truly fatal failues ++ * If there is an error; offline the device since all ++ * error recovery strategies would have already been ++ * deployed on the host side. ++ */ ++ if (vm_srb->srb_status == SRB_STATUS_ERROR) ++ scmnd->result = DID_TARGET_FAILURE << 16; ++ else ++ scmnd->result = vm_srb->scsi_status; ++ ++ /* ++ * If the LUN is invalid; remove the device. ++ */ ++ if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { ++ struct storvsc_device *stor_dev; ++ struct hv_device *dev = host_dev->dev; ++ struct Scsi_Host *host; ++ ++ stor_dev = get_in_stor_device(dev); ++ host = stor_dev->host; ++ ++ wrk = kmalloc(sizeof(struct storvsc_scan_work), ++ GFP_ATOMIC); ++ if (!wrk) { ++ scmnd->result = DID_TARGET_FAILURE << 16; ++ } else { ++ wrk->host = host; ++ wrk->lun = vm_srb->lun; ++ INIT_WORK(&wrk->work, storvsc_remove_lun); ++ schedule_work(&wrk->work); ++ } ++ } ++ ++ if (scmnd->result) { ++ if (scsi_normalize_sense(scmnd->sense_buffer, ++ SCSI_SENSE_BUFFERSIZE, &sense_hdr)) ++ scsi_print_sense_hdr("storvsc", &sense_hdr); ++ } ++ ++ scsi_set_resid(scmnd, ++ request->data_buffer.len - ++ vm_srb->data_transfer_length); ++ ++ scsi_done_fn = scmnd->scsi_done; ++ ++ scmnd->host_scribble = NULL; ++ scmnd->scsi_done = NULL; ++ ++ scsi_done_fn(scmnd); ++ ++ mempool_free(cmd_request, memp->request_mempool); ++} ++ ++static void storvsc_on_io_completion(struct hv_device *device, ++ struct vstor_packet *vstor_packet, ++ struct hv_storvsc_request *request) ++{ ++ struct storvsc_device *stor_device; ++ struct vstor_packet *stor_pkt; ++ ++ stor_device = hv_get_drvdata(device); ++ stor_pkt = &request->vstor_packet; ++ ++ /* ++ * The current SCSI handling on the host side does ++ * not correctly handle: ++ * INQUIRY command with page code parameter set to 0x80 ++ * MODE_SENSE command with cmd[2] == 0x1c ++ * ++ * Setup srb and scsi status so this won't be fatal. ++ * We do this so we can distinguish truly fatal failues + * (srb status == 0x4) and off-line the device in that case. + */ + +@@ -625,7 +922,7 @@ static void storvsc_on_io_completion(struct hv_device *device, + stor_pkt->vm_srb.data_transfer_length = + vstor_packet->vm_srb.data_transfer_length; + +- request->on_io_completion(request); ++ storvsc_command_completion(request); + + if (atomic_dec_and_test(&stor_device->num_outstanding_req) && + stor_device->drain_notify) +@@ -875,229 +1172,6 @@ static int storvsc_device_configure(struct scsi_device *sdevice) + return 0; + } + +-static void destroy_bounce_buffer(struct scatterlist *sgl, +- unsigned int sg_count) +-{ +- int i; +- struct page *page_buf; +- +- for (i = 0; i < sg_count; i++) { +- page_buf = sg_page((&sgl[i])); +- if (page_buf != NULL) +- __free_page(page_buf); +- } +- +- kfree(sgl); +-} +- +-static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) +-{ +- int i; +- +- /* No need to check */ +- if (sg_count < 2) +- return -1; +- +- /* We have at least 2 sg entries */ +- for (i = 0; i < sg_count; i++) { +- if (i == 0) { +- /* make sure 1st one does not have hole */ +- if (sgl[i].offset + sgl[i].length != PAGE_SIZE) +- return i; +- } else if (i == sg_count - 1) { +- /* make sure last one does not have hole */ +- if (sgl[i].offset != 0) +- return i; +- } else { +- /* make sure no hole in the middle */ +- if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) +- return i; +- } +- } +- return -1; +-} +- +-static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, +- unsigned int sg_count, +- unsigned int len, +- int write) +-{ +- int i; +- int num_pages; +- struct scatterlist *bounce_sgl; +- struct page *page_buf; +- unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); +- +- num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; +- +- bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); +- if (!bounce_sgl) +- return NULL; +- +- for (i = 0; i < num_pages; i++) { +- page_buf = alloc_page(GFP_ATOMIC); +- if (!page_buf) +- goto cleanup; +- sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); +- } +- +- return bounce_sgl; +- +-cleanup: +- destroy_bounce_buffer(bounce_sgl, num_pages); +- return NULL; +-} +- +- +-/* Assume the original sgl has enough room */ +-static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, +- struct scatterlist *bounce_sgl, +- unsigned int orig_sgl_count, +- unsigned int bounce_sgl_count) +-{ +- int i; +- int j = 0; +- unsigned long src, dest; +- unsigned int srclen, destlen, copylen; +- unsigned int total_copied = 0; +- unsigned long bounce_addr = 0; +- unsigned long dest_addr = 0; +- unsigned long flags; +- +- local_irq_save(flags); +- +- for (i = 0; i < orig_sgl_count; i++) { +- dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; +- dest = dest_addr; +- destlen = orig_sgl[i].length; +- +- if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); +- +- while (destlen) { +- src = bounce_addr + bounce_sgl[j].offset; +- srclen = bounce_sgl[j].length - bounce_sgl[j].offset; +- +- copylen = min(srclen, destlen); +- memcpy((void *)dest, (void *)src, copylen); +- +- total_copied += copylen; +- bounce_sgl[j].offset += copylen; +- destlen -= copylen; +- dest += copylen; +- +- if (bounce_sgl[j].offset == bounce_sgl[j].length) { +- /* full */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- j++; +- +- /* +- * It is possible that the number of elements +- * in the bounce buffer may not be equal to +- * the number of elements in the original +- * scatter list. Handle this correctly. +- */ +- +- if (j == bounce_sgl_count) { +- /* +- * We are done; cleanup and return. +- */ +- kunmap_atomic((void *)(dest_addr - +- orig_sgl[i].offset), +- KM_IRQ0); +- local_irq_restore(flags); +- return total_copied; +- } +- +- /* if we need to use another bounce buffer */ +- if (destlen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); +- } else if (destlen == 0 && i == orig_sgl_count - 1) { +- /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- } +- } +- +- kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), +- KM_IRQ0); +- } +- +- local_irq_restore(flags); +- +- return total_copied; +-} +- +- +-/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ +-static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, +- struct scatterlist *bounce_sgl, +- unsigned int orig_sgl_count) +-{ +- int i; +- int j = 0; +- unsigned long src, dest; +- unsigned int srclen, destlen, copylen; +- unsigned int total_copied = 0; +- unsigned long bounce_addr = 0; +- unsigned long src_addr = 0; +- unsigned long flags; +- +- local_irq_save(flags); +- +- for (i = 0; i < orig_sgl_count; i++) { +- src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; +- src = src_addr; +- srclen = orig_sgl[i].length; +- +- if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); +- +- while (srclen) { +- /* assume bounce offset always == 0 */ +- dest = bounce_addr + bounce_sgl[j].length; +- destlen = PAGE_SIZE - bounce_sgl[j].length; +- +- copylen = min(srclen, destlen); +- memcpy((void *)dest, (void *)src, copylen); +- +- total_copied += copylen; +- bounce_sgl[j].length += copylen; +- srclen -= copylen; +- src += copylen; +- +- if (bounce_sgl[j].length == PAGE_SIZE) { +- /* full..move to next entry */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- j++; +- +- /* if we need to use another bounce buffer */ +- if (srclen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); +- +- } else if (srclen == 0 && i == orig_sgl_count - 1) { +- /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- } +- } +- +- kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); +- } +- +- local_irq_restore(flags); +- +- return total_copied; +-} +- + static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, + sector_t capacity, int *info) + { +@@ -1166,83 +1240,6 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + return SUCCESS; + } + +- +-static void storvsc_command_completion(struct hv_storvsc_request *request) +-{ +- struct storvsc_cmd_request *cmd_request = +- (struct storvsc_cmd_request *)request->context; +- struct scsi_cmnd *scmnd = cmd_request->cmd; +- struct hv_host_device *host_dev = shost_priv(scmnd->device->host); +- void (*scsi_done_fn)(struct scsi_cmnd *); +- struct scsi_sense_hdr sense_hdr; +- struct vmscsi_request *vm_srb; +- struct storvsc_scan_work *wrk; +- struct stor_mem_pools *memp = scmnd->device->hostdata; +- +- vm_srb = &request->vstor_packet.vm_srb; +- if (cmd_request->bounce_sgl_count) { +- if (vm_srb->data_in == READ_TYPE) +- copy_from_bounce_buffer(scsi_sglist(scmnd), +- cmd_request->bounce_sgl, +- scsi_sg_count(scmnd), +- cmd_request->bounce_sgl_count); +- destroy_bounce_buffer(cmd_request->bounce_sgl, +- cmd_request->bounce_sgl_count); +- } +- +- /* +- * If there is an error; offline the device since all +- * error recovery strategies would have already been +- * deployed on the host side. +- */ +- if (vm_srb->srb_status == SRB_STATUS_ERROR) +- scmnd->result = DID_TARGET_FAILURE << 16; +- else +- scmnd->result = vm_srb->scsi_status; +- +- /* +- * If the LUN is invalid; remove the device. +- */ +- if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { +- struct storvsc_device *stor_dev; +- struct hv_device *dev = host_dev->dev; +- struct Scsi_Host *host; +- +- stor_dev = get_in_stor_device(dev); +- host = stor_dev->host; +- +- wrk = kmalloc(sizeof(struct storvsc_scan_work), +- GFP_ATOMIC); +- if (!wrk) { +- scmnd->result = DID_TARGET_FAILURE << 16; +- } else { +- wrk->host = host; +- wrk->lun = vm_srb->lun; +- INIT_WORK(&wrk->work, storvsc_remove_lun); +- schedule_work(&wrk->work); +- } +- } +- +- if (scmnd->result) { +- if (scsi_normalize_sense(scmnd->sense_buffer, +- SCSI_SENSE_BUFFERSIZE, &sense_hdr)) +- scsi_print_sense_hdr("storvsc", &sense_hdr); +- } +- +- scsi_set_resid(scmnd, +- request->data_buffer.len - +- vm_srb->data_transfer_length); +- +- scsi_done_fn = scmnd->scsi_done; +- +- scmnd->host_scribble = NULL; +- scmnd->scsi_done = NULL; +- +- scsi_done_fn(scmnd); +- +- mempool_free(cmd_request, memp->request_mempool); +-} +- + static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) + { + bool allowed = true; +@@ -1318,7 +1315,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + break; + } + +- request->on_io_completion = storvsc_command_completion; + request->context = cmd_request;/* scmnd; */ + + vm_srb->port_number = host_dev->port; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0056-Staging-hv-storvsc-Rename-the-context-field-in-hv_st.patch b/debian/patches/features/x86/hyperv/0056-Staging-hv-storvsc-Rename-the-context-field-in-hv_st.patch new file mode 100644 index 000000000..d201dbe36 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0056-Staging-hv-storvsc-Rename-the-context-field-in-hv_st.patch @@ -0,0 +1,51 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:02 -0800 +Subject: [PATCH 56/77] Staging: hv: storvsc: Rename the context field in + hv_storvsc_request + +commit 93a1bf4dd018af2a4931b916dd46fdfb963fa4ad upstream. + +Rename the context field in hv_storvsc_request. As part of this change +fix the type of this field. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 7c9fa19..0515707 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -275,7 +275,7 @@ struct hv_storvsc_request { + struct completion wait_event; + + unsigned char *sense_buffer; +- void *context; ++ struct storvsc_cmd_request *cmd; + struct hv_multipage_buffer data_buffer; + + struct vstor_packet vstor_packet; +@@ -785,8 +785,7 @@ cleanup: + + static void storvsc_command_completion(struct hv_storvsc_request *request) + { +- struct storvsc_cmd_request *cmd_request = +- (struct storvsc_cmd_request *)request->context; ++ struct storvsc_cmd_request *cmd_request = request->cmd; + struct scsi_cmnd *scmnd = cmd_request->cmd; + struct hv_host_device *host_dev = shost_priv(scmnd->device->host); + void (*scsi_done_fn)(struct scsi_cmnd *); +@@ -1315,7 +1314,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + break; + } + +- request->context = cmd_request;/* scmnd; */ ++ request->cmd = cmd_request; + + vm_srb->port_number = host_dev->port; + vm_srb->path_id = scmnd->device->channel; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0057-Staging-hv-storvsc-Miscellaneous-cleanup-of-storvsc-.patch b/debian/patches/features/x86/hyperv/0057-Staging-hv-storvsc-Miscellaneous-cleanup-of-storvsc-.patch new file mode 100644 index 000000000..70cad1338 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0057-Staging-hv-storvsc-Miscellaneous-cleanup-of-storvsc-.patch @@ -0,0 +1,195 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:03 -0800 +Subject: [PATCH 57/77] Staging: hv: storvsc: Miscellaneous cleanup of storvsc + driver + +commit c649114a5d99eb10e58f977f82018b1d77191aa7 upstream. + +Miscellaneous cleanup of storvsc driver - get rid of unnecessary defines and +use fixed size types for structures used for communication with the host. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 94 ++++++++++++++++---------------------- + 1 file changed, 39 insertions(+), 55 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 0515707..da71294 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -43,32 +43,18 @@ + #include + + ++/* ++ * We setup a mempool to allocate request structures for this driver ++ * on a per-lun basis. The following define specifies the number of ++ * elements in the pool. ++ */ ++ + #define STORVSC_MIN_BUF_NR 64 +-#define STORVSC_RING_BUFFER_SIZE (20*PAGE_SIZE) +-static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; ++static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); + + module_param(storvsc_ringbuffer_size, int, S_IRUGO); + MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + +-/* +- * To alert the user that structure sizes may be mismatched even though the +- * protocol versions match. +- */ +- +- +-#define REVISION_STRING(REVISION_) #REVISION_ +-#define FILL_VMSTOR_REVISION(RESULT_LVALUE_) \ +- do { \ +- char *revision_string \ +- = REVISION_STRING($Rev : 6 $) + 6; \ +- RESULT_LVALUE_ = 0; \ +- while (*revision_string >= '0' \ +- && *revision_string <= '9') { \ +- RESULT_LVALUE_ *= 10; \ +- RESULT_LVALUE_ += *revision_string - '0'; \ +- revision_string++; \ +- } \ +- } while (0) + + /* + * Major/minor macros. Minor version is in LSB, meaning that earlier flat +@@ -79,7 +65,6 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + #define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) + #define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff))) +-#define VMSTOR_INVALID_PROTOCOL_VERSION (-1) + + /* + * Version history: +@@ -136,26 +121,26 @@ enum vstor_packet_operation { + #define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 + + struct vmscsi_request { +- unsigned short length; +- unsigned char srb_status; +- unsigned char scsi_status; ++ u16 length; ++ u8 srb_status; ++ u8 scsi_status; + +- unsigned char port_number; +- unsigned char path_id; +- unsigned char target_id; +- unsigned char lun; ++ u8 port_number; ++ u8 path_id; ++ u8 target_id; ++ u8 lun; + +- unsigned char cdb_length; +- unsigned char sense_info_length; +- unsigned char data_in; +- unsigned char reserved; ++ u8 cdb_length; ++ u8 sense_info_length; ++ u8 data_in; ++ u8 reserved; + +- unsigned int data_transfer_length; ++ u32 data_transfer_length; + + union { +- unsigned char cdb[CDB16GENERIC_LENGTH]; +- unsigned char sense_data[SENSE_BUFFER_SIZE]; +- unsigned char reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; ++ u8 cdb[CDB16GENERIC_LENGTH]; ++ u8 sense_data[SENSE_BUFFER_SIZE]; ++ u8 reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; + }; + } __attribute((packed)); + +@@ -165,18 +150,21 @@ struct vmscsi_request { + * properties of the channel. + */ + struct vmstorage_channel_properties { +- unsigned short protocol_version; +- unsigned char path_id; +- unsigned char target_id; ++ u16 protocol_version; ++ u8 path_id; ++ u8 target_id; + + /* Note: port number is only really known on the client side */ +- unsigned int port_number; +- unsigned int flags; +- unsigned int max_transfer_bytes; ++ u32 port_number; ++ u32 flags; ++ u32 max_transfer_bytes; + +- /* This id is unique for each channel and will correspond with */ +- /* vendor specific data in the inquirydata */ +- unsigned long long unique_id; ++ /* ++ * This id is unique for each channel and will correspond with ++ * vendor specific data in the inquiry data. ++ */ ++ ++ u64 unique_id; + } __packed; + + /* This structure is sent during the storage protocol negotiations. */ +@@ -189,6 +177,7 @@ struct vmstorage_protocol_version { + * (See FILL_VMSTOR_REVISION macro above). Mismatch does not + * definitely indicate incompatibility--but it does indicate mismatched + * builds. ++ * This is only used on the windows side. Just set it to 0. + */ + unsigned short revision; + } __packed; +@@ -202,10 +191,10 @@ struct vstor_packet { + enum vstor_packet_operation operation; + + /* Flags - see below for values */ +- unsigned int flags; ++ u32 flags; + + /* Status of the request returned from the server side. */ +- unsigned int status; ++ u32 status; + + /* Data payload area */ + union { +@@ -232,11 +221,6 @@ struct vstor_packet { + + #define REQUEST_COMPLETION_FLAG 0x1 + +-/* This is the set of flags that the vsc can set in any packets it sends */ +-#define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG) +- +- +- + #define STORVSC_MAX_IO_REQUESTS 128 + + /* +@@ -252,7 +236,7 @@ struct vstor_packet { + + /* Matches Windows-end */ + enum storvsc_request_type { +- WRITE_TYPE, ++ WRITE_TYPE = 0, + READ_TYPE, + UNKNOWN_TYPE, + }; +@@ -704,7 +688,7 @@ static int storvsc_channel_init(struct hv_device *device) + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; +- FILL_VMSTOR_REVISION(vstor_packet->version.revision); ++ vstor_packet->version.revision = 0; + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0058-Staging-hv-storvsc-Cleanup-the-code-for-generating-p.patch b/debian/patches/features/x86/hyperv/0058-Staging-hv-storvsc-Cleanup-the-code-for-generating-p.patch new file mode 100644 index 000000000..dc23cce0d --- /dev/null +++ b/debian/patches/features/x86/hyperv/0058-Staging-hv-storvsc-Cleanup-the-code-for-generating-p.patch @@ -0,0 +1,84 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:04 -0800 +Subject: [PATCH 58/77] Staging: hv: storvsc: Cleanup the code for generating + protocol version + +commit 85904a5e553f343243e5cf59f150174159e2e5ea upstream. + +Cleanup the code for generating protocol version. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index da71294..629edd1 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -61,10 +61,13 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). + */ + +-#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff) +-#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff) +-#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ +- (((MINOR_) & 0xff))) ++static inline u16 storvsc_get_version(u8 major, u8 minor) ++{ ++ u16 version; ++ ++ version = ((major << 8) | minor); ++ return version; ++} + + /* + * Version history: +@@ -74,9 +77,8 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); + * Win7: 4.2 + */ + +-#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(4, 2) +- +- ++#define VMSTOR_CURRENT_MAJOR 4 ++#define VMSTOR_CURRENT_MINOR 2 + + + /* +@@ -170,7 +172,7 @@ struct vmstorage_channel_properties { + /* This structure is sent during the storage protocol negotiations. */ + struct vmstorage_protocol_version { + /* Major (MSW) and minor (LSW) version numbers. */ +- unsigned short major_minor; ++ u16 major_minor; + + /* + * Revision number is auto-incremented whenever this file is changed +@@ -179,7 +181,7 @@ struct vmstorage_protocol_version { + * builds. + * This is only used on the windows side. Just set it to 0. + */ +- unsigned short revision; ++ u16 revision; + } __packed; + + /* Channel Property Flags */ +@@ -687,7 +689,12 @@ static int storvsc_channel_init(struct hv_device *device) + vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + +- vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; ++ vstor_packet->version.major_minor = ++ storvsc_get_version(VMSTOR_CURRENT_MAJOR, VMSTOR_CURRENT_MINOR); ++ ++ /* ++ * The revision number is only used in Windows; set it to 0. ++ */ + vstor_packet->version.revision = 0; + + ret = vmbus_sendpacket(device->channel, vstor_packet, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0059-Staging-hv-storvsc-Cleanup-some-protocol-related-con.patch b/debian/patches/features/x86/hyperv/0059-Staging-hv-storvsc-Cleanup-some-protocol-related-con.patch new file mode 100644 index 000000000..4d732a54d --- /dev/null +++ b/debian/patches/features/x86/hyperv/0059-Staging-hv-storvsc-Cleanup-some-protocol-related-con.patch @@ -0,0 +1,69 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:05 -0800 +Subject: [PATCH 59/77] Staging: hv: storvsc: Cleanup some protocol related + constants + +commit 6b2f949559b26448cc6d161d62341281db42c909 upstream. + +Cleanup some protocol related constants. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 19 +++++++------------ + 1 file changed, 7 insertions(+), 12 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 629edd1..9f07458 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -114,13 +114,9 @@ enum vstor_packet_operation { + * this remains the same across the write regardless of 32/64 bit + * note: it's patterned off the SCSI_PASS_THROUGH structure + */ +-#define CDB16GENERIC_LENGTH 0x10 +- +-#ifndef SENSE_BUFFER_SIZE +-#define SENSE_BUFFER_SIZE 0x12 +-#endif +- +-#define MAX_DATA_BUF_LEN_WITH_PADDING 0x14 ++#define STORVSC_MAX_CMD_LEN 0x10 ++#define STORVSC_SENSE_BUFFER_SIZE 0x12 ++#define STORVSC_MAX_BUF_LEN_WITH_PADDING 0x14 + + struct vmscsi_request { + u16 length; +@@ -140,9 +136,9 @@ struct vmscsi_request { + u32 data_transfer_length; + + union { +- u8 cdb[CDB16GENERIC_LENGTH]; +- u8 sense_data[SENSE_BUFFER_SIZE]; +- u8 reserved_array[MAX_DATA_BUF_LEN_WITH_PADDING]; ++ u8 cdb[STORVSC_MAX_CMD_LEN]; ++ u8 sense_data[STORVSC_SENSE_BUFFER_SIZE]; ++ u8 reserved_array[STORVSC_MAX_BUF_LEN_WITH_PADDING]; + }; + } __attribute((packed)); + +@@ -234,7 +230,6 @@ struct vstor_packet { + #define STORVSC_MAX_LUNS_PER_TARGET 64 + #define STORVSC_MAX_TARGETS 1 + #define STORVSC_MAX_CHANNELS 1 +-#define STORVSC_MAX_CMD_LEN 16 + + /* Matches Windows-end */ + enum storvsc_request_type { +@@ -1074,7 +1069,7 @@ static int storvsc_do_io(struct hv_device *device, + vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); + + +- vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; ++ vstor_packet->vm_srb.sense_info_length = STORVSC_SENSE_BUFFER_SIZE; + + + vstor_packet->vm_srb.data_transfer_length = +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0060-Staging-hv-storvsc-Get-rid-of-some-unused-defines.patch b/debian/patches/features/x86/hyperv/0060-Staging-hv-storvsc-Get-rid-of-some-unused-defines.patch new file mode 100644 index 000000000..d9926271e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0060-Staging-hv-storvsc-Get-rid-of-some-unused-defines.patch @@ -0,0 +1,40 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:06 -0800 +Subject: [PATCH 60/77] Staging: hv: storvsc: Get rid of some unused defines + +commit 410c52ed66cabd1837a87b37348d92a17f01c199 upstream. + +Get rid of some unused defines. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 9f07458..d34e3ca 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -81,18 +81,6 @@ static inline u16 storvsc_get_version(u8 major, u8 minor) + #define VMSTOR_CURRENT_MINOR 2 + + +-/* +- * This will get replaced with the max transfer length that is possible on +- * the host adapter. +- * The max transfer length will be published when we offer a vmbus channel. +- */ +- +-#define MAX_TRANSFER_LENGTH 0x40000 +-#define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \ +- sizeof(struct vstor_packet) + \ +- sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE))) +- +- + /* Packet structure describing virtual storage requests. */ + enum vstor_packet_operation { + VSTOR_OPERATION_COMPLETE_IO = 1, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0061-Staging-hv-storvsc-Consolidate-the-request-structure.patch b/debian/patches/features/x86/hyperv/0061-Staging-hv-storvsc-Consolidate-the-request-structure.patch new file mode 100644 index 000000000..45cc0de3e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0061-Staging-hv-storvsc-Consolidate-the-request-structure.patch @@ -0,0 +1,241 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:07 -0800 +Subject: [PATCH 61/77] Staging: hv: storvsc: Consolidate the request + structure + +commit 61eaffc91d375e02bc12c2c831478841f5ce4757 upstream. + +Consolidate the request structure by getting rid of struct hv_storvsc_request. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 65 ++++++++++++++++---------------------- + 1 file changed, 27 insertions(+), 38 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index d34e3ca..9ccc1c4 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -236,17 +236,20 @@ enum storvsc_request_type { + #define SRB_STATUS_ERROR 0x04 + + ++struct storvsc_cmd_request { ++ struct list_head entry; ++ struct scsi_cmnd *cmd; ++ ++ unsigned int bounce_sgl_count; ++ struct scatterlist *bounce_sgl; + +-struct hv_storvsc_request { + struct hv_device *device; + + /* Synchronize the request/response if needed */ + struct completion wait_event; + + unsigned char *sense_buffer; +- struct storvsc_cmd_request *cmd; + struct hv_multipage_buffer data_buffer; +- + struct vstor_packet vstor_packet; + }; + +@@ -272,8 +275,8 @@ struct storvsc_device { + unsigned char target_id; + + /* Used for vsc/vsp channel reset process */ +- struct hv_storvsc_request init_request; +- struct hv_storvsc_request reset_request; ++ struct storvsc_cmd_request init_request; ++ struct storvsc_cmd_request reset_request; + }; + + struct stor_mem_pools { +@@ -288,16 +291,6 @@ struct hv_host_device { + unsigned char target; + }; + +-struct storvsc_cmd_request { +- struct list_head entry; +- struct scsi_cmnd *cmd; +- +- unsigned int bounce_sgl_count; +- struct scatterlist *bounce_sgl; +- +- struct hv_storvsc_request request; +-}; +- + struct storvsc_scan_work { + struct work_struct work; + struct Scsi_Host *host; +@@ -628,7 +621,7 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, + static int storvsc_channel_init(struct hv_device *device) + { + struct storvsc_device *stor_device; +- struct hv_storvsc_request *request; ++ struct storvsc_cmd_request *request; + struct vstor_packet *vstor_packet; + int ret, t; + +@@ -643,7 +636,7 @@ static int storvsc_channel_init(struct hv_device *device) + * Now, initiate the vsc/vsp initialization protocol on the open + * channel + */ +- memset(request, 0, sizeof(struct hv_storvsc_request)); ++ memset(request, 0, sizeof(struct storvsc_cmd_request)); + init_completion(&request->wait_event); + vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; +@@ -757,9 +750,8 @@ cleanup: + } + + +-static void storvsc_command_completion(struct hv_storvsc_request *request) ++static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) + { +- struct storvsc_cmd_request *cmd_request = request->cmd; + struct scsi_cmnd *scmnd = cmd_request->cmd; + struct hv_host_device *host_dev = shost_priv(scmnd->device->host); + void (*scsi_done_fn)(struct scsi_cmnd *); +@@ -768,7 +760,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + struct storvsc_scan_work *wrk; + struct stor_mem_pools *memp = scmnd->device->hostdata; + +- vm_srb = &request->vstor_packet.vm_srb; ++ vm_srb = &cmd_request->vstor_packet.vm_srb; + if (cmd_request->bounce_sgl_count) { + if (vm_srb->data_in == READ_TYPE) + copy_from_bounce_buffer(scsi_sglist(scmnd), +@@ -819,7 +811,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + } + + scsi_set_resid(scmnd, +- request->data_buffer.len - ++ cmd_request->data_buffer.len - + vm_srb->data_transfer_length); + + scsi_done_fn = scmnd->scsi_done; +@@ -834,7 +826,7 @@ static void storvsc_command_completion(struct hv_storvsc_request *request) + + static void storvsc_on_io_completion(struct hv_device *device, + struct vstor_packet *vstor_packet, +- struct hv_storvsc_request *request) ++ struct storvsc_cmd_request *request) + { + struct storvsc_device *stor_device; + struct vstor_packet *stor_pkt; +@@ -906,7 +898,7 @@ static void storvsc_on_io_completion(struct hv_device *device, + + static void storvsc_on_receive(struct hv_device *device, + struct vstor_packet *vstor_packet, +- struct hv_storvsc_request *request) ++ struct storvsc_cmd_request *request) + { + struct storvsc_scan_work *work; + struct storvsc_device *stor_device; +@@ -940,7 +932,7 @@ static void storvsc_on_channel_callback(void *context) + u32 bytes_recvd; + u64 request_id; + unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; +- struct hv_storvsc_request *request; ++ struct storvsc_cmd_request *request; + int ret; + + +@@ -954,7 +946,7 @@ static void storvsc_on_channel_callback(void *context) + &bytes_recvd, &request_id); + if (ret == 0 && bytes_recvd > 0) { + +- request = (struct hv_storvsc_request *) ++ request = (struct storvsc_cmd_request *) + (unsigned long)request_id; + + if ((request == &stor_device->init_request) || +@@ -1036,7 +1028,7 @@ static int storvsc_dev_remove(struct hv_device *device) + } + + static int storvsc_do_io(struct hv_device *device, +- struct hv_storvsc_request *request) ++ struct storvsc_cmd_request *request) + { + struct storvsc_device *stor_device; + struct vstor_packet *vstor_packet; +@@ -1174,7 +1166,7 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) + struct hv_device *device = host_dev->dev; + + struct storvsc_device *stor_device; +- struct hv_storvsc_request *request; ++ struct storvsc_cmd_request *request; + struct vstor_packet *vstor_packet; + int ret, t; + +@@ -1238,7 +1230,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + int ret; + struct hv_host_device *host_dev = shost_priv(host); + struct hv_device *dev = host_dev->dev; +- struct hv_storvsc_request *request; + struct storvsc_cmd_request *cmd_request; + unsigned int request_size = 0; + int i; +@@ -1271,8 +1262,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + + scmnd->host_scribble = (unsigned char *)cmd_request; + +- request = &cmd_request->request; +- vm_srb = &request->vstor_packet.vm_srb; ++ vm_srb = &cmd_request->vstor_packet.vm_srb; + + + /* Build the SRB */ +@@ -1288,7 +1278,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + break; + } + +- request->cmd = cmd_request; + + vm_srb->port_number = host_dev->port; + vm_srb->path_id = scmnd->device->channel; +@@ -1299,10 +1288,10 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + + memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); + +- request->sense_buffer = scmnd->sense_buffer; ++ cmd_request->sense_buffer = scmnd->sense_buffer; + + +- request->data_buffer.len = scsi_bufflen(scmnd); ++ cmd_request->data_buffer.len = scsi_bufflen(scmnd); + if (scsi_sg_count(scmnd)) { + sgl = (struct scatterlist *)scsi_sglist(scmnd); + sg_count = scsi_sg_count(scmnd); +@@ -1331,21 +1320,21 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) + sg_count = cmd_request->bounce_sgl_count; + } + +- request->data_buffer.offset = sgl[0].offset; ++ cmd_request->data_buffer.offset = sgl[0].offset; + + for (i = 0; i < sg_count; i++) +- request->data_buffer.pfn_array[i] = ++ cmd_request->data_buffer.pfn_array[i] = + page_to_pfn(sg_page((&sgl[i]))); + + } else if (scsi_sglist(scmnd)) { +- request->data_buffer.offset = ++ cmd_request->data_buffer.offset = + virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1); +- request->data_buffer.pfn_array[0] = ++ cmd_request->data_buffer.pfn_array[0] = + virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT; + } + + /* Invokes the vsc to start an IO */ +- ret = storvsc_do_io(dev, &cmd_request->request); ++ ret = storvsc_do_io(dev, cmd_request); + + if (ret == -EAGAIN) { + /* no more space */ +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0062-Staging-hv-storvsc-Consolidate-all-the-wire-protocol.patch b/debian/patches/features/x86/hyperv/0062-Staging-hv-storvsc-Consolidate-all-the-wire-protocol.patch new file mode 100644 index 000000000..fb76ea21f --- /dev/null +++ b/debian/patches/features/x86/hyperv/0062-Staging-hv-storvsc-Consolidate-all-the-wire-protocol.patch @@ -0,0 +1,136 @@ +From: "K. Y. Srinivasan" +Date: Thu, 12 Jan 2012 12:38:08 -0800 +Subject: [PATCH 62/77] Staging: hv: storvsc: Consolidate all the wire + protocol definitions + +commit af9584b82df77751710db45043cf8d7d7740056d upstream. + +Consolidate all definitions that support communication with the host. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/hv/storvsc_drv.c | 83 +++++++++++++++++++++----------------- + 1 file changed, 47 insertions(+), 36 deletions(-) + +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +index 9ccc1c4..695ffc3 100644 +--- a/drivers/staging/hv/storvsc_drv.c ++++ b/drivers/staging/hv/storvsc_drv.c +@@ -42,33 +42,13 @@ + #include + #include + +- +-/* +- * We setup a mempool to allocate request structures for this driver +- * on a per-lun basis. The following define specifies the number of +- * elements in the pool. +- */ +- +-#define STORVSC_MIN_BUF_NR 64 +-static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); +- +-module_param(storvsc_ringbuffer_size, int, S_IRUGO); +-MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); +- +- + /* +- * Major/minor macros. Minor version is in LSB, meaning that earlier flat +- * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). ++ * All wire protocol details (storage protocol between the guest and the host) ++ * are consolidated here. ++ * ++ * Begin protocol definitions. + */ + +-static inline u16 storvsc_get_version(u8 major, u8 minor) +-{ +- u16 version; +- +- version = ((major << 8) | minor); +- return version; +-} +- + /* + * Version history: + * V1 Beta: 0.1 +@@ -207,18 +187,6 @@ struct vstor_packet { + + #define REQUEST_COMPLETION_FLAG 0x1 + +-#define STORVSC_MAX_IO_REQUESTS 128 +- +-/* +- * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In +- * reality, the path/target is not used (ie always set to 0) so our +- * scsi host adapter essentially has 1 bus with 1 target that contains +- * up to 256 luns. +- */ +-#define STORVSC_MAX_LUNS_PER_TARGET 64 +-#define STORVSC_MAX_TARGETS 1 +-#define STORVSC_MAX_CHANNELS 1 +- + /* Matches Windows-end */ + enum storvsc_request_type { + WRITE_TYPE = 0, +@@ -235,6 +203,36 @@ enum storvsc_request_type { + #define SRB_STATUS_SUCCESS 0x01 + #define SRB_STATUS_ERROR 0x04 + ++/* ++ * This is the end of Protocol specific defines. ++ */ ++ ++ ++/* ++ * We setup a mempool to allocate request structures for this driver ++ * on a per-lun basis. The following define specifies the number of ++ * elements in the pool. ++ */ ++ ++#define STORVSC_MIN_BUF_NR 64 ++static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); ++ ++module_param(storvsc_ringbuffer_size, int, S_IRUGO); ++MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); ++ ++#define STORVSC_MAX_IO_REQUESTS 128 ++ ++/* ++ * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In ++ * reality, the path/target is not used (ie always set to 0) so our ++ * scsi host adapter essentially has 1 bus with 1 target that contains ++ * up to 256 luns. ++ */ ++#define STORVSC_MAX_LUNS_PER_TARGET 64 ++#define STORVSC_MAX_TARGETS 1 ++#define STORVSC_MAX_CHANNELS 1 ++ ++ + + struct storvsc_cmd_request { + struct list_head entry; +@@ -337,6 +335,19 @@ done: + } + + /* ++ * Major/minor macros. Minor version is in LSB, meaning that earlier flat ++ * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). ++ */ ++ ++static inline u16 storvsc_get_version(u8 major, u8 minor) ++{ ++ u16 version; ++ ++ version = ((major << 8) | minor); ++ return version; ++} ++ ++/* + * We can get incoming messages from the host that are not in response to + * messages that we have sent out. An example of this would be messages + * received by the guest to notify dynamic addition/removal of LUNs. To +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0063-drivers-hv-Cleanup-the-kvp-related-state-in-hyperv.h.patch b/debian/patches/features/x86/hyperv/0063-drivers-hv-Cleanup-the-kvp-related-state-in-hyperv.h.patch new file mode 100644 index 000000000..900bf3951 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0063-drivers-hv-Cleanup-the-kvp-related-state-in-hyperv.h.patch @@ -0,0 +1,73 @@ +From: "K. Y. Srinivasan" +Date: Thu, 2 Feb 2012 16:56:48 -0800 +Subject: [PATCH 63/77] drivers: hv: Cleanup the kvp related state in hyperv.h + +commit 59a084a70afa0678bda2a23a7bc7cc59664945c7 upstream. + +Now cleanup the hyperv.h with regards to KVP definitions. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/hyperv.h | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index c445ead..dd0e3ee 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -137,7 +137,6 @@ struct hv_ku_msg { + + + +-#ifdef __KERNEL__ + + /* + * Registry value types. +@@ -163,28 +162,30 @@ enum hv_kvp_exchg_pool { + }; + + struct hv_kvp_hdr { +- u8 operation; +- u8 pool; +-}; ++ __u8 operation; ++ __u8 pool; ++ __u16 pad; ++} __attribute__((packed)); + + 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]; +-}; ++ __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]; ++} __attribute__((packed)); + + struct hv_kvp_msg_enumerate { +- u32 index; ++ __u32 index; + struct hv_kvp_exchg_msg_value data; +-}; ++} __attribute__((packed)); + + struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + struct hv_kvp_msg_enumerate kvp_data; +-}; ++} __attribute__((packed)); + ++#ifdef __KERNEL__ + #include + #include + #include +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0064-tools-hv-Use-hyperv.h-to-get-the-KVP-definitions.patch b/debian/patches/features/x86/hyperv/0064-tools-hv-Use-hyperv.h-to-get-the-KVP-definitions.patch new file mode 100644 index 000000000..59ba436b9 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0064-tools-hv-Use-hyperv.h-to-get-the-KVP-definitions.patch @@ -0,0 +1,78 @@ +From: "K. Y. Srinivasan" +Date: Thu, 2 Feb 2012 16:56:49 -0800 +Subject: [PATCH 64/77] tools: hv: Use hyperv.h to get the KVP definitions + +commit eab6af71f0b83a7f62b9c48be5b2c0a82a86fad3 upstream. + +Now use hyperv.h to get the KVP defines in the KVP user-mode code. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + tools/hv/hv_kvp_daemon.c | 28 +--------------------------- + 1 file changed, 1 insertion(+), 27 deletions(-) + +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index 2b6a2d9..b75523c 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -34,17 +34,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + +-/* +- * +- * 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. +- */ + + /* + * KVP protocol: The user mode component first registers with the +@@ -56,25 +51,8 @@ + * 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, +@@ -89,10 +67,6 @@ enum key_index { + ProcessorArchitecture + }; + +-/* +- * End of shared definitions. +- */ +- + static char kvp_send_buffer[4096]; + static char kvp_recv_buffer[4096]; + static struct sockaddr_nl addr; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0065-drivers-hv-kvp-Cleanup-the-kernel-user-protocol.patch b/debian/patches/features/x86/hyperv/0065-drivers-hv-kvp-Cleanup-the-kernel-user-protocol.patch new file mode 100644 index 000000000..8f0a525fb --- /dev/null +++ b/debian/patches/features/x86/hyperv/0065-drivers-hv-kvp-Cleanup-the-kernel-user-protocol.patch @@ -0,0 +1,258 @@ +From: "K. Y. Srinivasan" +Date: Thu, 2 Feb 2012 16:56:50 -0800 +Subject: [PATCH 65/77] drivers: hv: kvp: Cleanup the kernel/user protocol + +commit 2640335438ca4d7b139e114dae5f0d80e740e106 upstream. + +Now, cleanup the user/kernel KVP protocol by using the same structure +definition that is used for host/guest KVP protocol. This simplifies the code. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 41 +++++++++++++++++++++++++---------------- + include/linux/hyperv.h | 30 +++++------------------------- + tools/hv/hv_kvp_daemon.c | 30 +++++++++++++++--------------- + 3 files changed, 45 insertions(+), 56 deletions(-) + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index 4a6971e..0ef4c1f 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -71,15 +71,20 @@ kvp_register(void) + { + + struct cn_msg *msg; ++ struct hv_kvp_msg *kvp_msg; ++ char *version; + +- msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); ++ msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); + + if (msg) { ++ kvp_msg = (struct hv_kvp_msg *)msg->data; ++ version = kvp_msg->body.kvp_version; + 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; ++ ++ kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; ++ strcpy(version, HV_DRV_VERSION); ++ msg->len = sizeof(struct hv_kvp_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + } +@@ -101,23 +106,24 @@ kvp_work_func(struct work_struct *dummy) + static void + kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) + { +- struct hv_ku_msg *message; ++ struct hv_kvp_msg *message; ++ struct hv_kvp_msg_enumerate *data; + +- message = (struct hv_ku_msg *)msg->data; +- if (msg->seq == KVP_REGISTER) { ++ message = (struct hv_kvp_msg *)msg->data; ++ if (message->kvp_hdr.operation == KVP_OP_REGISTER) { + pr_info("KVP: user-mode registering done.\n"); + kvp_register(); + } + +- if (msg->seq == KVP_USER_SET) { ++ if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { ++ data = &message->body.kvp_enum_data; + /* + * 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)); ++ kvp_respond_to_host(data->data.key, data->data.value, ++ !strlen(data->data.key)); + } + } + +@@ -125,6 +131,7 @@ static void + kvp_send_key(struct work_struct *dummy) + { + struct cn_msg *msg; ++ struct hv_kvp_msg *message; + int index = kvp_transaction.index; + + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); +@@ -132,9 +139,11 @@ kvp_send_key(struct work_struct *dummy) + 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); ++ ++ message = (struct hv_kvp_msg *)msg->data; ++ message->kvp_hdr.operation = KVP_OP_ENUMERATE; ++ message->body.kvp_enum_data.index = index; ++ msg->len = sizeof(struct hv_kvp_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + } +@@ -191,7 +200,7 @@ kvp_respond_to_host(char *key, char *value, int error) + kvp_msg = (struct hv_kvp_msg *) + &recv_buffer[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; +- kvp_data = &kvp_msg->kvp_data; ++ kvp_data = &kvp_msg->body.kvp_enum_data; + key_name = key; + + /* +@@ -266,7 +275,7 @@ void hv_kvp_onchannelcallback(void *context) + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + +- kvp_data = &kvp_msg->kvp_data; ++ kvp_data = &kvp_msg->body.kvp_enum_data; + + /* + * We only support the "get" operation on +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index dd0e3ee..e57a6c6 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -113,30 +113,6 @@ + * (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. +- */ +- +-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 */ +-}; +- +- +- +- + + /* + * Registry value types. +@@ -149,6 +125,7 @@ enum hv_kvp_exchg_op { + KVP_OP_SET, + KVP_OP_DELETE, + KVP_OP_ENUMERATE, ++ KVP_OP_REGISTER, + KVP_OP_COUNT /* Number of operations, must be last. */ + }; + +@@ -182,7 +159,10 @@ struct hv_kvp_msg_enumerate { + + struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; +- struct hv_kvp_msg_enumerate kvp_data; ++ union { ++ struct hv_kvp_msg_enumerate kvp_enum_data; ++ char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ++ } body; + } __attribute__((packed)); + + #ifdef __KERNEL__ +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index b75523c..4ebf703 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -302,7 +302,7 @@ int main(void) + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; +- struct hv_ku_msg *hv_msg; ++ struct hv_kvp_msg *hv_msg; + char *p; + char *key_value; + char *key_name; +@@ -340,9 +340,11 @@ int main(void) + message = (struct cn_msg *)kvp_send_buffer; + message->id.idx = CN_KVP_IDX; + message->id.val = CN_KVP_VAL; +- message->seq = KVP_REGISTER; ++ ++ hv_msg = (struct hv_kvp_msg *)message->data; ++ hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; + message->ack = 0; +- message->len = 0; ++ message->len = sizeof(struct hv_kvp_msg); + + len = netlink_send(fd, message); + if (len < 0) { +@@ -368,14 +370,15 @@ int main(void) + + incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); ++ hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + +- switch (incoming_cn_msg->seq) { +- case KVP_REGISTER: ++ switch (hv_msg->kvp_hdr.operation) { ++ case KVP_OP_REGISTER: + /* + * Driver is registering with us; stash away the version + * information. + */ +- p = (char *)incoming_cn_msg->data; ++ p = (char *)hv_msg->body.kvp_version; + lic_version = malloc(strlen(p) + 1); + if (lic_version) { + strcpy(lic_version, p); +@@ -386,17 +389,15 @@ int main(void) + } + continue; + +- case KVP_KERNEL_GET: +- break; + default: +- continue; ++ break; + } + +- hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; +- key_name = (char *)hv_msg->kvp_key; +- key_value = (char *)hv_msg->kvp_value; ++ hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; ++ key_name = (char *)hv_msg->body.kvp_enum_data.data.key; ++ key_value = (char *)hv_msg->body.kvp_enum_data.data.value; + +- switch (hv_msg->kvp_index) { ++ switch (hv_msg->body.kvp_enum_data.index) { + case FullyQualifiedDomainName: + kvp_get_domain_name(key_value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); +@@ -456,9 +457,8 @@ int main(void) + + 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); ++ incoming_cn_msg->len = sizeof(struct hv_kvp_msg); + + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0066-drivers-hv-Increase-the-number-of-VCPUs-supported-in.patch b/debian/patches/features/x86/hyperv/0066-drivers-hv-Increase-the-number-of-VCPUs-supported-in.patch new file mode 100644 index 000000000..53c070704 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0066-drivers-hv-Increase-the-number-of-VCPUs-supported-in.patch @@ -0,0 +1,65 @@ +From: "K. Y. Srinivasan" +Date: Thu, 2 Feb 2012 16:56:51 -0800 +Subject: [PATCH 66/77] drivers: hv: Increase the number of VCPUs supported in + the guest + +commit 14c1bf8a8920f36f6e0603a2ff920b48eec14387 upstream. + +The current code arbirarily limited the number of CPUs the guest could have. +Change that so that we can support the maximum number of CPUs the guest can +support. While we use NR_CPUS to size the per-cpu state all we are allocating +based on NR_CPUS are the pointers to per-cpu state that will be allocatted in +the context of the initializing CPU. This patch triggers a checkpatch warning +for the usage of NR_CPU and since all we are allocating a couple of pointers +per CPU, it should be ok. + +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Haiyang Zhang +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv.c | 4 ++-- + drivers/hv/hyperv_vmbus.h | 5 ++--- + 2 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c +index 12aa97f..15956bd 100644 +--- a/drivers/hv/hv.c ++++ b/drivers/hv/hv.c +@@ -155,9 +155,9 @@ int hv_init(void) + 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_event_page, 0, sizeof(void *) * NR_CPUS); + memset(hv_context.synic_message_page, 0, +- sizeof(void *) * MAX_NUM_CPUS); ++ sizeof(void *) * NR_CPUS); + + if (!query_hypervisor_presence()) + goto cleanup; +diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h +index 6d7d286..699f0d8 100644 +--- a/drivers/hv/hyperv_vmbus.h ++++ b/drivers/hv/hyperv_vmbus.h +@@ -457,7 +457,6 @@ static const uuid_le VMBUS_SERVICE_ID = { + }, + }; + +-#define MAX_NUM_CPUS 32 + + + struct hv_input_signal_event_buffer { +@@ -483,8 +482,8 @@ struct hv_context { + /* 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]; ++ void *synic_message_page[NR_CPUS]; ++ void *synic_event_page[NR_CPUS]; + }; + + extern struct hv_context hv_context; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0067-Staging-hv-storvsc-Move-the-storage-driver-out-of-th.patch b/debian/patches/features/x86/hyperv/0067-Staging-hv-storvsc-Move-the-storage-driver-out-of-th.patch new file mode 100644 index 000000000..169991052 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0067-Staging-hv-storvsc-Move-the-storage-driver-out-of-th.patch @@ -0,0 +1,3244 @@ +From: "K. Y. Srinivasan" +Date: Thu, 9 Feb 2012 14:14:48 -0800 +Subject: [PATCH 67/77] Staging: hv: storvsc: Move the storage driver out of + the staging area + +commit 89ae7d709357df3a60e81c57613859d91d093ace upstream. + +The storage driver (storvsc_drv.c) handles all block storage devices +assigned to Linux guests hosted on Hyper-V. This driver has been in the +staging tree for a while and this patch moves it out of the staging area. + +James was willing to apply this patch during the 3.3-rc phase and a decision +was taken to defer this to 3.4 since Greg had queued up a bunch of storvsc +patches for 3.4. Now that Greg has applied all of the pending storvsc patches, +I am sending this patch to move this driver out of staging. Based on James' +recommendation, this patch gets rid of the unneeded files in the staging/hv +directory. + +Signed-off-by: K. Y. Srinivasan +Acked-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman +--- + drivers/scsi/Kconfig | 7 + + drivers/scsi/Makefile | 3 + + drivers/scsi/storvsc_drv.c | 1548 ++++++++++++++++++++++++++++++++++++++ + drivers/staging/Kconfig | 2 - + drivers/staging/Makefile | 1 - + drivers/staging/hv/Kconfig | 5 - + drivers/staging/hv/Makefile | 3 - + drivers/staging/hv/TODO | 5 - + drivers/staging/hv/storvsc_drv.c | 1548 -------------------------------------- + 9 files changed, 1558 insertions(+), 1564 deletions(-) + create mode 100644 drivers/scsi/storvsc_drv.c + delete mode 100644 drivers/staging/hv/Kconfig + delete mode 100644 drivers/staging/hv/Makefile + delete mode 100644 drivers/staging/hv/TODO + delete mode 100644 drivers/staging/hv/storvsc_drv.c + +diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +index 06ea3bc..4910269 100644 +--- a/drivers/scsi/Kconfig ++++ b/drivers/scsi/Kconfig +@@ -662,6 +662,13 @@ config VMWARE_PVSCSI + To compile this driver as a module, choose M here: the + module will be called vmw_pvscsi. + ++config HYPERV_STORAGE ++ tristate "Microsoft Hyper-V virtual storage driver" ++ depends on SCSI && HYPERV ++ default HYPERV ++ help ++ Select this option to enable the Hyper-V virtual storage driver. ++ + config LIBFC + tristate "LibFC module" + select SCSI_FC_ATTRS +diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile +index 2b88749..e4c1a69 100644 +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -142,6 +142,7 @@ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/ + obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/ + obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o + obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o ++obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o + + obj-$(CONFIG_ARM) += arm/ + +@@ -170,6 +171,8 @@ scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o + scsi_mod-y += scsi_trace.o + scsi_mod-$(CONFIG_PM) += scsi_pm.o + ++hv_storvsc-y := storvsc_drv.o ++ + scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o + + sd_mod-objs := sd.o +diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c +new file mode 100644 +index 0000000..695ffc3 +--- /dev/null ++++ b/drivers/scsi/storvsc_drv.c +@@ -0,0 +1,1548 @@ ++/* ++ * 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * All wire protocol details (storage protocol between the guest and the host) ++ * are consolidated here. ++ * ++ * Begin protocol definitions. ++ */ ++ ++/* ++ * Version history: ++ * V1 Beta: 0.1 ++ * V1 RC < 2008/1/31: 1.0 ++ * V1 RC > 2008/1/31: 2.0 ++ * Win7: 4.2 ++ */ ++ ++#define VMSTOR_CURRENT_MAJOR 4 ++#define VMSTOR_CURRENT_MINOR 2 ++ ++ ++/* Packet structure describing virtual storage requests. */ ++enum vstor_packet_operation { ++ VSTOR_OPERATION_COMPLETE_IO = 1, ++ VSTOR_OPERATION_REMOVE_DEVICE = 2, ++ VSTOR_OPERATION_EXECUTE_SRB = 3, ++ VSTOR_OPERATION_RESET_LUN = 4, ++ VSTOR_OPERATION_RESET_ADAPTER = 5, ++ VSTOR_OPERATION_RESET_BUS = 6, ++ VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, ++ VSTOR_OPERATION_END_INITIALIZATION = 8, ++ VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, ++ VSTOR_OPERATION_QUERY_PROPERTIES = 10, ++ VSTOR_OPERATION_ENUMERATE_BUS = 11, ++ VSTOR_OPERATION_MAXIMUM = 11 ++}; ++ ++/* ++ * Platform neutral description of a scsi request - ++ * this remains the same across the write regardless of 32/64 bit ++ * note: it's patterned off the SCSI_PASS_THROUGH structure ++ */ ++#define STORVSC_MAX_CMD_LEN 0x10 ++#define STORVSC_SENSE_BUFFER_SIZE 0x12 ++#define STORVSC_MAX_BUF_LEN_WITH_PADDING 0x14 ++ ++struct vmscsi_request { ++ u16 length; ++ u8 srb_status; ++ u8 scsi_status; ++ ++ u8 port_number; ++ u8 path_id; ++ u8 target_id; ++ u8 lun; ++ ++ u8 cdb_length; ++ u8 sense_info_length; ++ u8 data_in; ++ u8 reserved; ++ ++ u32 data_transfer_length; ++ ++ union { ++ u8 cdb[STORVSC_MAX_CMD_LEN]; ++ u8 sense_data[STORVSC_SENSE_BUFFER_SIZE]; ++ u8 reserved_array[STORVSC_MAX_BUF_LEN_WITH_PADDING]; ++ }; ++} __attribute((packed)); ++ ++ ++/* ++ * This structure is sent during the intialization phase to get the different ++ * properties of the channel. ++ */ ++struct vmstorage_channel_properties { ++ u16 protocol_version; ++ u8 path_id; ++ u8 target_id; ++ ++ /* Note: port number is only really known on the client side */ ++ u32 port_number; ++ u32 flags; ++ u32 max_transfer_bytes; ++ ++ /* ++ * This id is unique for each channel and will correspond with ++ * vendor specific data in the inquiry data. ++ */ ++ ++ u64 unique_id; ++} __packed; ++ ++/* This structure is sent during the storage protocol negotiations. */ ++struct vmstorage_protocol_version { ++ /* Major (MSW) and minor (LSW) version numbers. */ ++ u16 major_minor; ++ ++ /* ++ * Revision number is auto-incremented whenever this file is changed ++ * (See FILL_VMSTOR_REVISION macro above). Mismatch does not ++ * definitely indicate incompatibility--but it does indicate mismatched ++ * builds. ++ * This is only used on the windows side. Just set it to 0. ++ */ ++ u16 revision; ++} __packed; ++ ++/* Channel Property Flags */ ++#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 ++#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 ++ ++struct vstor_packet { ++ /* Requested operation type */ ++ enum vstor_packet_operation operation; ++ ++ /* Flags - see below for values */ ++ u32 flags; ++ ++ /* Status of the request returned from the server side. */ ++ u32 status; ++ ++ /* Data payload area */ ++ union { ++ /* ++ * Structure used to forward SCSI commands from the ++ * client to the server. ++ */ ++ struct vmscsi_request vm_srb; ++ ++ /* Structure used to query channel properties. */ ++ struct vmstorage_channel_properties storage_channel_properties; ++ ++ /* Used during version negotiations. */ ++ struct vmstorage_protocol_version version; ++ }; ++} __packed; ++ ++/* ++ * Packet Flags: ++ * ++ * This flag indicates that the server should send back a completion for this ++ * packet. ++ */ ++ ++#define REQUEST_COMPLETION_FLAG 0x1 ++ ++/* Matches Windows-end */ ++enum storvsc_request_type { ++ WRITE_TYPE = 0, ++ READ_TYPE, ++ UNKNOWN_TYPE, ++}; ++ ++/* ++ * SRB status codes and masks; a subset of the codes used here. ++ */ ++ ++#define SRB_STATUS_AUTOSENSE_VALID 0x80 ++#define SRB_STATUS_INVALID_LUN 0x20 ++#define SRB_STATUS_SUCCESS 0x01 ++#define SRB_STATUS_ERROR 0x04 ++ ++/* ++ * This is the end of Protocol specific defines. ++ */ ++ ++ ++/* ++ * We setup a mempool to allocate request structures for this driver ++ * on a per-lun basis. The following define specifies the number of ++ * elements in the pool. ++ */ ++ ++#define STORVSC_MIN_BUF_NR 64 ++static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); ++ ++module_param(storvsc_ringbuffer_size, int, S_IRUGO); ++MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); ++ ++#define STORVSC_MAX_IO_REQUESTS 128 ++ ++/* ++ * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In ++ * reality, the path/target is not used (ie always set to 0) so our ++ * scsi host adapter essentially has 1 bus with 1 target that contains ++ * up to 256 luns. ++ */ ++#define STORVSC_MAX_LUNS_PER_TARGET 64 ++#define STORVSC_MAX_TARGETS 1 ++#define STORVSC_MAX_CHANNELS 1 ++ ++ ++ ++struct storvsc_cmd_request { ++ struct list_head entry; ++ struct scsi_cmnd *cmd; ++ ++ unsigned int bounce_sgl_count; ++ struct scatterlist *bounce_sgl; ++ ++ struct hv_device *device; ++ ++ /* Synchronize the request/response if needed */ ++ struct completion wait_event; ++ ++ unsigned char *sense_buffer; ++ struct hv_multipage_buffer data_buffer; ++ struct vstor_packet vstor_packet; ++}; ++ ++ ++/* A storvsc device is a device object that contains a vmbus channel */ ++struct storvsc_device { ++ struct hv_device *device; ++ ++ bool destroy; ++ bool drain_notify; ++ atomic_t num_outstanding_req; ++ struct Scsi_Host *host; ++ ++ wait_queue_head_t waiting_to_drain; ++ ++ /* ++ * Each unique Port/Path/Target represents 1 channel ie scsi ++ * controller. In reality, the pathid, targetid is always 0 ++ * and the port is set by us ++ */ ++ unsigned int port_number; ++ unsigned char path_id; ++ unsigned char target_id; ++ ++ /* Used for vsc/vsp channel reset process */ ++ struct storvsc_cmd_request init_request; ++ struct storvsc_cmd_request reset_request; ++}; ++ ++struct stor_mem_pools { ++ struct kmem_cache *request_pool; ++ mempool_t *request_mempool; ++}; ++ ++struct hv_host_device { ++ struct hv_device *dev; ++ unsigned int port; ++ unsigned char path; ++ unsigned char target; ++}; ++ ++struct storvsc_scan_work { ++ struct work_struct work; ++ struct Scsi_Host *host; ++ uint lun; ++}; ++ ++static void storvsc_bus_scan(struct work_struct *work) ++{ ++ struct storvsc_scan_work *wrk; ++ int id, order_id; ++ ++ wrk = container_of(work, struct storvsc_scan_work, work); ++ for (id = 0; id < wrk->host->max_id; ++id) { ++ if (wrk->host->reverse_ordering) ++ order_id = wrk->host->max_id - id - 1; ++ else ++ order_id = id; ++ ++ scsi_scan_target(&wrk->host->shost_gendev, 0, ++ order_id, SCAN_WILD_CARD, 1); ++ } ++ kfree(wrk); ++} ++ ++static void storvsc_remove_lun(struct work_struct *work) ++{ ++ struct storvsc_scan_work *wrk; ++ struct scsi_device *sdev; ++ ++ wrk = container_of(work, struct storvsc_scan_work, work); ++ if (!scsi_host_get(wrk->host)) ++ goto done; ++ ++ sdev = scsi_device_lookup(wrk->host, 0, 0, wrk->lun); ++ ++ if (sdev) { ++ scsi_remove_device(sdev); ++ scsi_device_put(sdev); ++ } ++ scsi_host_put(wrk->host); ++ ++done: ++ kfree(wrk); ++} ++ ++/* ++ * Major/minor macros. Minor version is in LSB, meaning that earlier flat ++ * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). ++ */ ++ ++static inline u16 storvsc_get_version(u8 major, u8 minor) ++{ ++ u16 version; ++ ++ version = ((major << 8) | minor); ++ return version; ++} ++ ++/* ++ * We can get incoming messages from the host that are not in response to ++ * messages that we have sent out. An example of this would be messages ++ * received by the guest to notify dynamic addition/removal of LUNs. To ++ * deal with potential race conditions where the driver may be in the ++ * midst of being unloaded when we might receive an unsolicited message ++ * from the host, we have implemented a mechanism to gurantee sequential ++ * consistency: ++ * ++ * 1) Once the device is marked as being destroyed, we will fail all ++ * outgoing messages. ++ * 2) We permit incoming messages when the device is being destroyed, ++ * only to properly account for messages already sent out. ++ */ ++ ++static inline struct storvsc_device *get_out_stor_device( ++ struct hv_device *device) ++{ ++ struct storvsc_device *stor_device; ++ ++ stor_device = hv_get_drvdata(device); ++ ++ if (stor_device && stor_device->destroy) ++ stor_device = NULL; ++ ++ return stor_device; ++} ++ ++ ++static inline void storvsc_wait_to_drain(struct storvsc_device *dev) ++{ ++ dev->drain_notify = true; ++ wait_event(dev->waiting_to_drain, ++ atomic_read(&dev->num_outstanding_req) == 0); ++ dev->drain_notify = false; ++} ++ ++static inline struct storvsc_device *get_in_stor_device( ++ struct hv_device *device) ++{ ++ struct storvsc_device *stor_device; ++ ++ stor_device = hv_get_drvdata(device); ++ ++ if (!stor_device) ++ goto get_in_err; ++ ++ /* ++ * If the device is being destroyed; allow incoming ++ * traffic only to cleanup outstanding requests. ++ */ ++ ++ if (stor_device->destroy && ++ (atomic_read(&stor_device->num_outstanding_req) == 0)) ++ stor_device = NULL; ++ ++get_in_err: ++ return stor_device; ++ ++} ++ ++static void destroy_bounce_buffer(struct scatterlist *sgl, ++ unsigned int sg_count) ++{ ++ int i; ++ struct page *page_buf; ++ ++ for (i = 0; i < sg_count; i++) { ++ page_buf = sg_page((&sgl[i])); ++ if (page_buf != NULL) ++ __free_page(page_buf); ++ } ++ ++ kfree(sgl); ++} ++ ++static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) ++{ ++ int i; ++ ++ /* No need to check */ ++ if (sg_count < 2) ++ return -1; ++ ++ /* We have at least 2 sg entries */ ++ for (i = 0; i < sg_count; i++) { ++ if (i == 0) { ++ /* make sure 1st one does not have hole */ ++ if (sgl[i].offset + sgl[i].length != PAGE_SIZE) ++ return i; ++ } else if (i == sg_count - 1) { ++ /* make sure last one does not have hole */ ++ if (sgl[i].offset != 0) ++ return i; ++ } else { ++ /* make sure no hole in the middle */ ++ if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) ++ return i; ++ } ++ } ++ return -1; ++} ++ ++static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, ++ unsigned int sg_count, ++ unsigned int len, ++ int write) ++{ ++ int i; ++ int num_pages; ++ struct scatterlist *bounce_sgl; ++ struct page *page_buf; ++ unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); ++ ++ num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; ++ ++ bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); ++ if (!bounce_sgl) ++ return NULL; ++ ++ for (i = 0; i < num_pages; i++) { ++ page_buf = alloc_page(GFP_ATOMIC); ++ if (!page_buf) ++ goto cleanup; ++ sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); ++ } ++ ++ return bounce_sgl; ++ ++cleanup: ++ destroy_bounce_buffer(bounce_sgl, num_pages); ++ return NULL; ++} ++ ++/* Assume the original sgl has enough room */ ++static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, ++ struct scatterlist *bounce_sgl, ++ unsigned int orig_sgl_count, ++ unsigned int bounce_sgl_count) ++{ ++ int i; ++ int j = 0; ++ unsigned long src, dest; ++ unsigned int srclen, destlen, copylen; ++ unsigned int total_copied = 0; ++ unsigned long bounce_addr = 0; ++ unsigned long dest_addr = 0; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ for (i = 0; i < orig_sgl_count; i++) { ++ dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), ++ KM_IRQ0) + orig_sgl[i].offset; ++ dest = dest_addr; ++ destlen = orig_sgl[i].length; ++ ++ if (bounce_addr == 0) ++ bounce_addr = ++ (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), ++ KM_IRQ0); ++ ++ while (destlen) { ++ src = bounce_addr + bounce_sgl[j].offset; ++ srclen = bounce_sgl[j].length - bounce_sgl[j].offset; ++ ++ copylen = min(srclen, destlen); ++ memcpy((void *)dest, (void *)src, copylen); ++ ++ total_copied += copylen; ++ bounce_sgl[j].offset += copylen; ++ destlen -= copylen; ++ dest += copylen; ++ ++ if (bounce_sgl[j].offset == bounce_sgl[j].length) { ++ /* full */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ j++; ++ ++ /* ++ * It is possible that the number of elements ++ * in the bounce buffer may not be equal to ++ * the number of elements in the original ++ * scatter list. Handle this correctly. ++ */ ++ ++ if (j == bounce_sgl_count) { ++ /* ++ * We are done; cleanup and return. ++ */ ++ kunmap_atomic((void *)(dest_addr - ++ orig_sgl[i].offset), ++ KM_IRQ0); ++ local_irq_restore(flags); ++ return total_copied; ++ } ++ ++ /* if we need to use another bounce buffer */ ++ if (destlen || i != orig_sgl_count - 1) ++ bounce_addr = ++ (unsigned long)kmap_atomic( ++ sg_page((&bounce_sgl[j])), KM_IRQ0); ++ } else if (destlen == 0 && i == orig_sgl_count - 1) { ++ /* unmap the last bounce that is < PAGE_SIZE */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ } ++ } ++ ++ kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), ++ KM_IRQ0); ++ } ++ ++ local_irq_restore(flags); ++ ++ return total_copied; ++} ++ ++/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ ++static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, ++ struct scatterlist *bounce_sgl, ++ unsigned int orig_sgl_count) ++{ ++ int i; ++ int j = 0; ++ unsigned long src, dest; ++ unsigned int srclen, destlen, copylen; ++ unsigned int total_copied = 0; ++ unsigned long bounce_addr = 0; ++ unsigned long src_addr = 0; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ for (i = 0; i < orig_sgl_count; i++) { ++ src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), ++ KM_IRQ0) + orig_sgl[i].offset; ++ src = src_addr; ++ srclen = orig_sgl[i].length; ++ ++ if (bounce_addr == 0) ++ bounce_addr = ++ (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), ++ KM_IRQ0); ++ ++ while (srclen) { ++ /* assume bounce offset always == 0 */ ++ dest = bounce_addr + bounce_sgl[j].length; ++ destlen = PAGE_SIZE - bounce_sgl[j].length; ++ ++ copylen = min(srclen, destlen); ++ memcpy((void *)dest, (void *)src, copylen); ++ ++ total_copied += copylen; ++ bounce_sgl[j].length += copylen; ++ srclen -= copylen; ++ src += copylen; ++ ++ if (bounce_sgl[j].length == PAGE_SIZE) { ++ /* full..move to next entry */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ j++; ++ ++ /* if we need to use another bounce buffer */ ++ if (srclen || i != orig_sgl_count - 1) ++ bounce_addr = ++ (unsigned long)kmap_atomic( ++ sg_page((&bounce_sgl[j])), KM_IRQ0); ++ ++ } else if (srclen == 0 && i == orig_sgl_count - 1) { ++ /* unmap the last bounce that is < PAGE_SIZE */ ++ kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ } ++ } ++ ++ kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); ++ } ++ ++ local_irq_restore(flags); ++ ++ return total_copied; ++} ++ ++static int storvsc_channel_init(struct hv_device *device) ++{ ++ struct storvsc_device *stor_device; ++ struct storvsc_cmd_request *request; ++ struct vstor_packet *vstor_packet; ++ int ret, t; ++ ++ stor_device = get_out_stor_device(device); ++ if (!stor_device) ++ return -ENODEV; ++ ++ request = &stor_device->init_request; ++ vstor_packet = &request->vstor_packet; ++ ++ /* ++ * Now, initiate the vsc/vsp initialization protocol on the open ++ * channel ++ */ ++ memset(request, 0, sizeof(struct storvsc_cmd_request)); ++ init_completion(&request->wait_event); ++ vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; ++ vstor_packet->flags = REQUEST_COMPLETION_FLAG; ++ ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || ++ vstor_packet->status != 0) ++ goto cleanup; ++ ++ ++ /* reuse the packet for version range supported */ ++ memset(vstor_packet, 0, sizeof(struct vstor_packet)); ++ vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; ++ vstor_packet->flags = REQUEST_COMPLETION_FLAG; ++ ++ vstor_packet->version.major_minor = ++ storvsc_get_version(VMSTOR_CURRENT_MAJOR, VMSTOR_CURRENT_MINOR); ++ ++ /* ++ * The revision number is only used in Windows; set it to 0. ++ */ ++ vstor_packet->version.revision = 0; ++ ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || ++ vstor_packet->status != 0) ++ goto cleanup; ++ ++ ++ memset(vstor_packet, 0, sizeof(struct vstor_packet)); ++ vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; ++ vstor_packet->flags = REQUEST_COMPLETION_FLAG; ++ vstor_packet->storage_channel_properties.port_number = ++ stor_device->port_number; ++ ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || ++ vstor_packet->status != 0) ++ goto cleanup; ++ ++ stor_device->path_id = vstor_packet->storage_channel_properties.path_id; ++ stor_device->target_id ++ = vstor_packet->storage_channel_properties.target_id; ++ ++ memset(vstor_packet, 0, sizeof(struct vstor_packet)); ++ vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; ++ vstor_packet->flags = REQUEST_COMPLETION_FLAG; ++ ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ ++ if (ret != 0) ++ goto cleanup; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) { ++ ret = -ETIMEDOUT; ++ goto cleanup; ++ } ++ ++ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || ++ vstor_packet->status != 0) ++ goto cleanup; ++ ++ ++cleanup: ++ return ret; ++} ++ ++ ++static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) ++{ ++ struct scsi_cmnd *scmnd = cmd_request->cmd; ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); ++ void (*scsi_done_fn)(struct scsi_cmnd *); ++ struct scsi_sense_hdr sense_hdr; ++ struct vmscsi_request *vm_srb; ++ struct storvsc_scan_work *wrk; ++ struct stor_mem_pools *memp = scmnd->device->hostdata; ++ ++ vm_srb = &cmd_request->vstor_packet.vm_srb; ++ if (cmd_request->bounce_sgl_count) { ++ if (vm_srb->data_in == READ_TYPE) ++ copy_from_bounce_buffer(scsi_sglist(scmnd), ++ cmd_request->bounce_sgl, ++ scsi_sg_count(scmnd), ++ cmd_request->bounce_sgl_count); ++ destroy_bounce_buffer(cmd_request->bounce_sgl, ++ cmd_request->bounce_sgl_count); ++ } ++ ++ /* ++ * If there is an error; offline the device since all ++ * error recovery strategies would have already been ++ * deployed on the host side. ++ */ ++ if (vm_srb->srb_status == SRB_STATUS_ERROR) ++ scmnd->result = DID_TARGET_FAILURE << 16; ++ else ++ scmnd->result = vm_srb->scsi_status; ++ ++ /* ++ * If the LUN is invalid; remove the device. ++ */ ++ if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { ++ struct storvsc_device *stor_dev; ++ struct hv_device *dev = host_dev->dev; ++ struct Scsi_Host *host; ++ ++ stor_dev = get_in_stor_device(dev); ++ host = stor_dev->host; ++ ++ wrk = kmalloc(sizeof(struct storvsc_scan_work), ++ GFP_ATOMIC); ++ if (!wrk) { ++ scmnd->result = DID_TARGET_FAILURE << 16; ++ } else { ++ wrk->host = host; ++ wrk->lun = vm_srb->lun; ++ INIT_WORK(&wrk->work, storvsc_remove_lun); ++ schedule_work(&wrk->work); ++ } ++ } ++ ++ if (scmnd->result) { ++ if (scsi_normalize_sense(scmnd->sense_buffer, ++ SCSI_SENSE_BUFFERSIZE, &sense_hdr)) ++ scsi_print_sense_hdr("storvsc", &sense_hdr); ++ } ++ ++ scsi_set_resid(scmnd, ++ cmd_request->data_buffer.len - ++ vm_srb->data_transfer_length); ++ ++ scsi_done_fn = scmnd->scsi_done; ++ ++ scmnd->host_scribble = NULL; ++ scmnd->scsi_done = NULL; ++ ++ scsi_done_fn(scmnd); ++ ++ mempool_free(cmd_request, memp->request_mempool); ++} ++ ++static void storvsc_on_io_completion(struct hv_device *device, ++ struct vstor_packet *vstor_packet, ++ struct storvsc_cmd_request *request) ++{ ++ struct storvsc_device *stor_device; ++ struct vstor_packet *stor_pkt; ++ ++ stor_device = hv_get_drvdata(device); ++ stor_pkt = &request->vstor_packet; ++ ++ /* ++ * The current SCSI handling on the host side does ++ * not correctly handle: ++ * INQUIRY command with page code parameter set to 0x80 ++ * MODE_SENSE command with cmd[2] == 0x1c ++ * ++ * Setup srb and scsi status so this won't be fatal. ++ * We do this so we can distinguish truly fatal failues ++ * (srb status == 0x4) and off-line the device in that case. ++ */ ++ ++ if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || ++ (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { ++ vstor_packet->vm_srb.scsi_status = 0; ++ vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; ++ } ++ ++ ++ /* Copy over the status...etc */ ++ stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; ++ stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; ++ stor_pkt->vm_srb.sense_info_length = ++ vstor_packet->vm_srb.sense_info_length; ++ ++ if (vstor_packet->vm_srb.scsi_status != 0 || ++ vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS){ ++ 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 & ++ SRB_STATUS_AUTOSENSE_VALID) { ++ /* autosense data available */ ++ dev_warn(&device->device, ++ "stor 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, ++ vstor_packet->vm_srb.sense_info_length); ++ ++ } ++ } ++ ++ stor_pkt->vm_srb.data_transfer_length = ++ vstor_packet->vm_srb.data_transfer_length; ++ ++ storvsc_command_completion(request); ++ ++ if (atomic_dec_and_test(&stor_device->num_outstanding_req) && ++ stor_device->drain_notify) ++ wake_up(&stor_device->waiting_to_drain); ++ ++ ++} ++ ++static void storvsc_on_receive(struct hv_device *device, ++ struct vstor_packet *vstor_packet, ++ struct storvsc_cmd_request *request) ++{ ++ struct storvsc_scan_work *work; ++ struct storvsc_device *stor_device; ++ ++ switch (vstor_packet->operation) { ++ case VSTOR_OPERATION_COMPLETE_IO: ++ storvsc_on_io_completion(device, vstor_packet, request); ++ break; ++ ++ case VSTOR_OPERATION_REMOVE_DEVICE: ++ case VSTOR_OPERATION_ENUMERATE_BUS: ++ stor_device = get_in_stor_device(device); ++ work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); ++ if (!work) ++ return; ++ ++ INIT_WORK(&work->work, storvsc_bus_scan); ++ work->host = stor_device->host; ++ schedule_work(&work->work); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++static void storvsc_on_channel_callback(void *context) ++{ ++ struct hv_device *device = (struct hv_device *)context; ++ struct storvsc_device *stor_device; ++ u32 bytes_recvd; ++ u64 request_id; ++ unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; ++ struct storvsc_cmd_request *request; ++ int ret; ++ ++ ++ stor_device = get_in_stor_device(device); ++ if (!stor_device) ++ return; ++ ++ do { ++ ret = vmbus_recvpacket(device->channel, packet, ++ ALIGN(sizeof(struct vstor_packet), 8), ++ &bytes_recvd, &request_id); ++ if (ret == 0 && bytes_recvd > 0) { ++ ++ request = (struct storvsc_cmd_request *) ++ (unsigned long)request_id; ++ ++ if ((request == &stor_device->init_request) || ++ (request == &stor_device->reset_request)) { ++ ++ memcpy(&request->vstor_packet, packet, ++ sizeof(struct vstor_packet)); ++ complete(&request->wait_event); ++ } else { ++ storvsc_on_receive(device, ++ (struct vstor_packet *)packet, ++ request); ++ } ++ } else { ++ break; ++ } ++ } while (1); ++ ++ return; ++} ++ ++static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) ++{ ++ struct vmstorage_channel_properties props; ++ int ret; ++ ++ memset(&props, 0, sizeof(struct vmstorage_channel_properties)); ++ ++ ret = vmbus_open(device->channel, ++ ring_size, ++ ring_size, ++ (void *)&props, ++ sizeof(struct vmstorage_channel_properties), ++ storvsc_on_channel_callback, device); ++ ++ if (ret != 0) ++ return ret; ++ ++ ret = storvsc_channel_init(device); ++ ++ return ret; ++} ++ ++static int storvsc_dev_remove(struct hv_device *device) ++{ ++ struct storvsc_device *stor_device; ++ unsigned long flags; ++ ++ stor_device = hv_get_drvdata(device); ++ ++ spin_lock_irqsave(&device->channel->inbound_lock, flags); ++ stor_device->destroy = true; ++ spin_unlock_irqrestore(&device->channel->inbound_lock, flags); ++ ++ /* ++ * At this point, all outbound traffic should be disable. We ++ * only allow inbound traffic (responses) to proceed so that ++ * outstanding requests can be completed. ++ */ ++ ++ storvsc_wait_to_drain(stor_device); ++ ++ /* ++ * Since we have already drained, we don't need to busy wait ++ * as was done in final_release_stor_device() ++ * Note that we cannot set the ext pointer to NULL until ++ * we have drained - to drain the outgoing packets, we need to ++ * allow incoming packets. ++ */ ++ spin_lock_irqsave(&device->channel->inbound_lock, flags); ++ hv_set_drvdata(device, NULL); ++ spin_unlock_irqrestore(&device->channel->inbound_lock, flags); ++ ++ /* Close the channel */ ++ vmbus_close(device->channel); ++ ++ kfree(stor_device); ++ return 0; ++} ++ ++static int storvsc_do_io(struct hv_device *device, ++ struct storvsc_cmd_request *request) ++{ ++ struct storvsc_device *stor_device; ++ struct vstor_packet *vstor_packet; ++ int ret = 0; ++ ++ vstor_packet = &request->vstor_packet; ++ stor_device = get_out_stor_device(device); ++ ++ if (!stor_device) ++ return -ENODEV; ++ ++ ++ request->device = device; ++ ++ ++ vstor_packet->flags |= REQUEST_COMPLETION_FLAG; ++ ++ vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); ++ ++ ++ vstor_packet->vm_srb.sense_info_length = STORVSC_SENSE_BUFFER_SIZE; ++ ++ ++ vstor_packet->vm_srb.data_transfer_length = ++ request->data_buffer.len; ++ ++ vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; ++ ++ if (request->data_buffer.len) { ++ ret = vmbus_sendpacket_multipagebuffer(device->channel, ++ &request->data_buffer, ++ vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request); ++ } else { ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ } ++ ++ if (ret != 0) ++ return ret; ++ ++ atomic_inc(&stor_device->num_outstanding_req); ++ ++ return ret; ++} ++ ++static int storvsc_device_alloc(struct scsi_device *sdevice) ++{ ++ struct stor_mem_pools *memp; ++ int number = STORVSC_MIN_BUF_NR; ++ ++ memp = kzalloc(sizeof(struct stor_mem_pools), GFP_KERNEL); ++ if (!memp) ++ return -ENOMEM; ++ ++ memp->request_pool = ++ kmem_cache_create(dev_name(&sdevice->sdev_dev), ++ sizeof(struct storvsc_cmd_request), 0, ++ SLAB_HWCACHE_ALIGN, NULL); ++ ++ if (!memp->request_pool) ++ goto err0; ++ ++ memp->request_mempool = mempool_create(number, mempool_alloc_slab, ++ mempool_free_slab, ++ memp->request_pool); ++ ++ if (!memp->request_mempool) ++ goto err1; ++ ++ sdevice->hostdata = memp; ++ ++ return 0; ++ ++err1: ++ kmem_cache_destroy(memp->request_pool); ++ ++err0: ++ kfree(memp); ++ return -ENOMEM; ++} ++ ++static void storvsc_device_destroy(struct scsi_device *sdevice) ++{ ++ struct stor_mem_pools *memp = sdevice->hostdata; ++ ++ mempool_destroy(memp->request_mempool); ++ kmem_cache_destroy(memp->request_pool); ++ kfree(memp); ++ sdevice->hostdata = NULL; ++} ++ ++static int storvsc_device_configure(struct scsi_device *sdevice) ++{ ++ scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, ++ STORVSC_MAX_IO_REQUESTS); ++ ++ blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); ++ ++ blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); ++ ++ return 0; ++} ++ ++static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, ++ sector_t capacity, int *info) ++{ ++ sector_t nsect = capacity; ++ sector_t cylinders = nsect; ++ int heads, sectors_pt; ++ ++ /* ++ * We are making up these values; let us keep it simple. ++ */ ++ heads = 0xff; ++ sectors_pt = 0x3f; /* Sectors per track */ ++ sector_div(cylinders, heads * sectors_pt); ++ if ((sector_t)(cylinders + 1) * heads * sectors_pt < nsect) ++ cylinders = 0xffff; ++ ++ info[0] = heads; ++ info[1] = sectors_pt; ++ info[2] = (int)cylinders; ++ ++ return 0; ++} ++ ++static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) ++{ ++ struct hv_host_device *host_dev = shost_priv(scmnd->device->host); ++ struct hv_device *device = host_dev->dev; ++ ++ struct storvsc_device *stor_device; ++ struct storvsc_cmd_request *request; ++ struct vstor_packet *vstor_packet; ++ int ret, t; ++ ++ ++ stor_device = get_out_stor_device(device); ++ if (!stor_device) ++ return FAILED; ++ ++ request = &stor_device->reset_request; ++ vstor_packet = &request->vstor_packet; ++ ++ init_completion(&request->wait_event); ++ ++ vstor_packet->operation = VSTOR_OPERATION_RESET_BUS; ++ vstor_packet->flags = REQUEST_COMPLETION_FLAG; ++ vstor_packet->vm_srb.path_id = stor_device->path_id; ++ ++ ret = vmbus_sendpacket(device->channel, vstor_packet, ++ sizeof(struct vstor_packet), ++ (unsigned long)&stor_device->reset_request, ++ VM_PKT_DATA_INBAND, ++ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); ++ if (ret != 0) ++ return FAILED; ++ ++ t = wait_for_completion_timeout(&request->wait_event, 5*HZ); ++ if (t == 0) ++ return TIMEOUT_ERROR; ++ ++ ++ /* ++ * At this point, all outstanding requests in the adapter ++ * should have been flushed out and return to us ++ */ ++ ++ return SUCCESS; ++} ++ ++static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) ++{ ++ bool allowed = true; ++ u8 scsi_op = scmnd->cmnd[0]; ++ ++ switch (scsi_op) { ++ /* ++ * smartd sends this command and the host does not handle ++ * this. So, don't send it. ++ */ ++ case SET_WINDOW: ++ scmnd->result = ILLEGAL_REQUEST << 16; ++ allowed = false; ++ break; ++ default: ++ break; ++ } ++ return allowed; ++} ++ ++static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) ++{ ++ int ret; ++ struct hv_host_device *host_dev = shost_priv(host); ++ struct hv_device *dev = host_dev->dev; ++ struct storvsc_cmd_request *cmd_request; ++ unsigned int request_size = 0; ++ int i; ++ struct scatterlist *sgl; ++ unsigned int sg_count = 0; ++ struct vmscsi_request *vm_srb; ++ struct stor_mem_pools *memp = scmnd->device->hostdata; ++ ++ if (!storvsc_scsi_cmd_ok(scmnd)) { ++ scmnd->scsi_done(scmnd); ++ return 0; ++ } ++ ++ request_size = sizeof(struct storvsc_cmd_request); ++ ++ cmd_request = mempool_alloc(memp->request_mempool, ++ GFP_ATOMIC); ++ ++ /* ++ * We might be invoked in an interrupt context; hence ++ * mempool_alloc() can fail. ++ */ ++ if (!cmd_request) ++ return SCSI_MLQUEUE_DEVICE_BUSY; ++ ++ memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); ++ ++ /* Setup the cmd request */ ++ cmd_request->cmd = scmnd; ++ ++ scmnd->host_scribble = (unsigned char *)cmd_request; ++ ++ vm_srb = &cmd_request->vstor_packet.vm_srb; ++ ++ ++ /* Build the SRB */ ++ switch (scmnd->sc_data_direction) { ++ case DMA_TO_DEVICE: ++ vm_srb->data_in = WRITE_TYPE; ++ break; ++ case DMA_FROM_DEVICE: ++ vm_srb->data_in = READ_TYPE; ++ break; ++ default: ++ vm_srb->data_in = UNKNOWN_TYPE; ++ break; ++ } ++ ++ ++ vm_srb->port_number = host_dev->port; ++ vm_srb->path_id = scmnd->device->channel; ++ vm_srb->target_id = scmnd->device->id; ++ vm_srb->lun = scmnd->device->lun; ++ ++ vm_srb->cdb_length = scmnd->cmd_len; ++ ++ memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); ++ ++ cmd_request->sense_buffer = scmnd->sense_buffer; ++ ++ ++ cmd_request->data_buffer.len = scsi_bufflen(scmnd); ++ if (scsi_sg_count(scmnd)) { ++ sgl = (struct scatterlist *)scsi_sglist(scmnd); ++ sg_count = scsi_sg_count(scmnd); ++ ++ /* check if we need to bounce the sgl */ ++ if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { ++ cmd_request->bounce_sgl = ++ create_bounce_buffer(sgl, scsi_sg_count(scmnd), ++ scsi_bufflen(scmnd), ++ vm_srb->data_in); ++ if (!cmd_request->bounce_sgl) { ++ ret = SCSI_MLQUEUE_HOST_BUSY; ++ goto queue_error; ++ } ++ ++ cmd_request->bounce_sgl_count = ++ ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> ++ PAGE_SHIFT; ++ ++ if (vm_srb->data_in == WRITE_TYPE) ++ copy_to_bounce_buffer(sgl, ++ cmd_request->bounce_sgl, ++ scsi_sg_count(scmnd)); ++ ++ sgl = cmd_request->bounce_sgl; ++ sg_count = cmd_request->bounce_sgl_count; ++ } ++ ++ cmd_request->data_buffer.offset = sgl[0].offset; ++ ++ for (i = 0; i < sg_count; i++) ++ cmd_request->data_buffer.pfn_array[i] = ++ page_to_pfn(sg_page((&sgl[i]))); ++ ++ } else if (scsi_sglist(scmnd)) { ++ cmd_request->data_buffer.offset = ++ virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1); ++ cmd_request->data_buffer.pfn_array[0] = ++ virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT; ++ } ++ ++ /* Invokes the vsc to start an IO */ ++ ret = storvsc_do_io(dev, cmd_request); ++ ++ if (ret == -EAGAIN) { ++ /* no more space */ ++ ++ if (cmd_request->bounce_sgl_count) { ++ destroy_bounce_buffer(cmd_request->bounce_sgl, ++ cmd_request->bounce_sgl_count); ++ ++ ret = SCSI_MLQUEUE_DEVICE_BUSY; ++ goto queue_error; ++ } ++ } ++ ++ return 0; ++ ++queue_error: ++ mempool_free(cmd_request, memp->request_mempool); ++ scmnd->host_scribble = NULL; ++ return ret; ++} ++ ++static struct scsi_host_template scsi_driver = { ++ .module = THIS_MODULE, ++ .name = "storvsc_host_t", ++ .bios_param = storvsc_get_chs, ++ .queuecommand = storvsc_queuecommand, ++ .eh_host_reset_handler = storvsc_host_reset_handler, ++ .slave_alloc = storvsc_device_alloc, ++ .slave_destroy = storvsc_device_destroy, ++ .slave_configure = storvsc_device_configure, ++ .cmd_per_lun = 1, ++ /* 64 max_queue * 1 target */ ++ .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, ++ .this_id = -1, ++ /* no use setting to 0 since ll_blk_rw reset it to 1 */ ++ /* currently 32 */ ++ .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, ++ .use_clustering = DISABLE_CLUSTERING, ++ /* Make sure we dont get a sg segment crosses a page boundary */ ++ .dma_boundary = PAGE_SIZE-1, ++}; ++ ++enum { ++ SCSI_GUID, ++ IDE_GUID, ++}; ++ ++static const struct hv_vmbus_device_id id_table[] = { ++ /* SCSI guid */ ++ { VMBUS_DEVICE(0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, ++ 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f) ++ .driver_data = SCSI_GUID }, ++ /* IDE guid */ ++ { VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, ++ 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5) ++ .driver_data = IDE_GUID }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(vmbus, id_table); ++ ++static int storvsc_probe(struct hv_device *device, ++ const struct hv_vmbus_device_id *dev_id) ++{ ++ int ret; ++ struct Scsi_Host *host; ++ struct hv_host_device *host_dev; ++ bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); ++ int target = 0; ++ struct storvsc_device *stor_device; ++ ++ host = scsi_host_alloc(&scsi_driver, ++ sizeof(struct hv_host_device)); ++ if (!host) ++ return -ENOMEM; ++ ++ host_dev = shost_priv(host); ++ memset(host_dev, 0, sizeof(struct hv_host_device)); ++ ++ host_dev->port = host->host_no; ++ host_dev->dev = device; ++ ++ ++ stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); ++ if (!stor_device) { ++ ret = -ENOMEM; ++ goto err_out0; ++ } ++ ++ stor_device->destroy = false; ++ init_waitqueue_head(&stor_device->waiting_to_drain); ++ stor_device->device = device; ++ stor_device->host = host; ++ hv_set_drvdata(device, stor_device); ++ ++ stor_device->port_number = host->host_no; ++ ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); ++ if (ret) ++ goto err_out1; ++ ++ host_dev->path = stor_device->path_id; ++ host_dev->target = stor_device->target_id; ++ ++ /* max # of devices per target */ ++ host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; ++ /* max # of targets per channel */ ++ host->max_id = STORVSC_MAX_TARGETS; ++ /* max # of channels */ ++ host->max_channel = STORVSC_MAX_CHANNELS - 1; ++ /* max cmd length */ ++ host->max_cmd_len = STORVSC_MAX_CMD_LEN; ++ ++ /* Register the HBA and start the scsi bus scan */ ++ ret = scsi_add_host(host, &device->device); ++ if (ret != 0) ++ goto err_out2; ++ ++ if (!dev_is_ide) { ++ scsi_scan_host(host); ++ } else { ++ target = (device->dev_instance.b[5] << 8 | ++ device->dev_instance.b[4]); ++ ret = scsi_add_device(host, 0, target, 0); ++ if (ret) { ++ scsi_remove_host(host); ++ goto err_out2; ++ } ++ } ++ return 0; ++ ++err_out2: ++ /* ++ * Once we have connected with the host, we would need to ++ * to invoke storvsc_dev_remove() to rollback this state and ++ * this call also frees up the stor_device; hence the jump around ++ * err_out1 label. ++ */ ++ storvsc_dev_remove(device); ++ goto err_out0; ++ ++err_out1: ++ kfree(stor_device); ++ ++err_out0: ++ scsi_host_put(host); ++ return ret; ++} ++ ++static int storvsc_remove(struct hv_device *dev) ++{ ++ struct storvsc_device *stor_device = hv_get_drvdata(dev); ++ struct Scsi_Host *host = stor_device->host; ++ ++ scsi_remove_host(host); ++ storvsc_dev_remove(dev); ++ scsi_host_put(host); ++ ++ return 0; ++} ++ ++static struct hv_driver storvsc_drv = { ++ .name = KBUILD_MODNAME, ++ .id_table = id_table, ++ .probe = storvsc_probe, ++ .remove = storvsc_remove, ++}; ++ ++static int __init storvsc_drv_init(void) ++{ ++ u32 max_outstanding_req_per_channel; ++ ++ /* ++ * Divide the ring buffer data size (which is 1 page less ++ * than the ring buffer size since that page is reserved for ++ * the ring buffer indices) by the max request size (which is ++ * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) ++ */ ++ max_outstanding_req_per_channel = ++ ((storvsc_ringbuffer_size - PAGE_SIZE) / ++ ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + ++ sizeof(struct vstor_packet) + sizeof(u64), ++ sizeof(u64))); ++ ++ if (max_outstanding_req_per_channel < ++ STORVSC_MAX_IO_REQUESTS) ++ return -EINVAL; ++ ++ return vmbus_driver_register(&storvsc_drv); ++} ++ ++static void __exit storvsc_drv_exit(void) ++{ ++ vmbus_driver_unregister(&storvsc_drv); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(HV_DRV_VERSION); ++MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); ++module_init(storvsc_drv_init); ++module_exit(storvsc_drv_exit); +diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig +index 25cdff3..ea57692 100644 +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -80,8 +80,6 @@ source "drivers/staging/vt6655/Kconfig" + + source "drivers/staging/vt6656/Kconfig" + +-source "drivers/staging/hv/Kconfig" +- + source "drivers/staging/vme/Kconfig" + + source "drivers/staging/sep/Kconfig" +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index a25f3f2..d9e474d 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -31,7 +31,6 @@ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/ + obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ + obj-$(CONFIG_VT6655) += vt6655/ + obj-$(CONFIG_VT6656) += vt6656/ +-obj-$(CONFIG_HYPERV) += hv/ + obj-$(CONFIG_VME_BUS) += vme/ + obj-$(CONFIG_DX_SEP) += sep/ + obj-$(CONFIG_IIO) += iio/ +diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig +deleted file mode 100644 +index 60ac479..0000000 +--- a/drivers/staging/hv/Kconfig ++++ /dev/null +@@ -1,5 +0,0 @@ +-config HYPERV_STORAGE +- tristate "Microsoft Hyper-V virtual storage driver" +- depends on HYPERV && SCSI +- help +- Select this option to enable the Hyper-V virtual storage driver. +diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile +deleted file mode 100644 +index af95a6b..0000000 +--- a/drivers/staging/hv/Makefile ++++ /dev/null +@@ -1,3 +0,0 @@ +-obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o +- +-hv_storvsc-y := storvsc_drv.o +diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO +deleted file mode 100644 +index dea7d92..0000000 +--- a/drivers/staging/hv/TODO ++++ /dev/null +@@ -1,5 +0,0 @@ +-TODO: +- - audit the scsi driver +- +-Please send patches for this code to Greg Kroah-Hartman , +-Haiyang Zhang , and K. Y. Srinivasan +diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c +deleted file mode 100644 +index 695ffc3..0000000 +--- a/drivers/staging/hv/storvsc_drv.c ++++ /dev/null +@@ -1,1548 +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 +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* +- * All wire protocol details (storage protocol between the guest and the host) +- * are consolidated here. +- * +- * Begin protocol definitions. +- */ +- +-/* +- * Version history: +- * V1 Beta: 0.1 +- * V1 RC < 2008/1/31: 1.0 +- * V1 RC > 2008/1/31: 2.0 +- * Win7: 4.2 +- */ +- +-#define VMSTOR_CURRENT_MAJOR 4 +-#define VMSTOR_CURRENT_MINOR 2 +- +- +-/* Packet structure describing virtual storage requests. */ +-enum vstor_packet_operation { +- VSTOR_OPERATION_COMPLETE_IO = 1, +- VSTOR_OPERATION_REMOVE_DEVICE = 2, +- VSTOR_OPERATION_EXECUTE_SRB = 3, +- VSTOR_OPERATION_RESET_LUN = 4, +- VSTOR_OPERATION_RESET_ADAPTER = 5, +- VSTOR_OPERATION_RESET_BUS = 6, +- VSTOR_OPERATION_BEGIN_INITIALIZATION = 7, +- VSTOR_OPERATION_END_INITIALIZATION = 8, +- VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, +- VSTOR_OPERATION_QUERY_PROPERTIES = 10, +- VSTOR_OPERATION_ENUMERATE_BUS = 11, +- VSTOR_OPERATION_MAXIMUM = 11 +-}; +- +-/* +- * Platform neutral description of a scsi request - +- * this remains the same across the write regardless of 32/64 bit +- * note: it's patterned off the SCSI_PASS_THROUGH structure +- */ +-#define STORVSC_MAX_CMD_LEN 0x10 +-#define STORVSC_SENSE_BUFFER_SIZE 0x12 +-#define STORVSC_MAX_BUF_LEN_WITH_PADDING 0x14 +- +-struct vmscsi_request { +- u16 length; +- u8 srb_status; +- u8 scsi_status; +- +- u8 port_number; +- u8 path_id; +- u8 target_id; +- u8 lun; +- +- u8 cdb_length; +- u8 sense_info_length; +- u8 data_in; +- u8 reserved; +- +- u32 data_transfer_length; +- +- union { +- u8 cdb[STORVSC_MAX_CMD_LEN]; +- u8 sense_data[STORVSC_SENSE_BUFFER_SIZE]; +- u8 reserved_array[STORVSC_MAX_BUF_LEN_WITH_PADDING]; +- }; +-} __attribute((packed)); +- +- +-/* +- * This structure is sent during the intialization phase to get the different +- * properties of the channel. +- */ +-struct vmstorage_channel_properties { +- u16 protocol_version; +- u8 path_id; +- u8 target_id; +- +- /* Note: port number is only really known on the client side */ +- u32 port_number; +- u32 flags; +- u32 max_transfer_bytes; +- +- /* +- * This id is unique for each channel and will correspond with +- * vendor specific data in the inquiry data. +- */ +- +- u64 unique_id; +-} __packed; +- +-/* This structure is sent during the storage protocol negotiations. */ +-struct vmstorage_protocol_version { +- /* Major (MSW) and minor (LSW) version numbers. */ +- u16 major_minor; +- +- /* +- * Revision number is auto-incremented whenever this file is changed +- * (See FILL_VMSTOR_REVISION macro above). Mismatch does not +- * definitely indicate incompatibility--but it does indicate mismatched +- * builds. +- * This is only used on the windows side. Just set it to 0. +- */ +- u16 revision; +-} __packed; +- +-/* Channel Property Flags */ +-#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 +-#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 +- +-struct vstor_packet { +- /* Requested operation type */ +- enum vstor_packet_operation operation; +- +- /* Flags - see below for values */ +- u32 flags; +- +- /* Status of the request returned from the server side. */ +- u32 status; +- +- /* Data payload area */ +- union { +- /* +- * Structure used to forward SCSI commands from the +- * client to the server. +- */ +- struct vmscsi_request vm_srb; +- +- /* Structure used to query channel properties. */ +- struct vmstorage_channel_properties storage_channel_properties; +- +- /* Used during version negotiations. */ +- struct vmstorage_protocol_version version; +- }; +-} __packed; +- +-/* +- * Packet Flags: +- * +- * This flag indicates that the server should send back a completion for this +- * packet. +- */ +- +-#define REQUEST_COMPLETION_FLAG 0x1 +- +-/* Matches Windows-end */ +-enum storvsc_request_type { +- WRITE_TYPE = 0, +- READ_TYPE, +- UNKNOWN_TYPE, +-}; +- +-/* +- * SRB status codes and masks; a subset of the codes used here. +- */ +- +-#define SRB_STATUS_AUTOSENSE_VALID 0x80 +-#define SRB_STATUS_INVALID_LUN 0x20 +-#define SRB_STATUS_SUCCESS 0x01 +-#define SRB_STATUS_ERROR 0x04 +- +-/* +- * This is the end of Protocol specific defines. +- */ +- +- +-/* +- * We setup a mempool to allocate request structures for this driver +- * on a per-lun basis. The following define specifies the number of +- * elements in the pool. +- */ +- +-#define STORVSC_MIN_BUF_NR 64 +-static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); +- +-module_param(storvsc_ringbuffer_size, int, S_IRUGO); +-MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); +- +-#define STORVSC_MAX_IO_REQUESTS 128 +- +-/* +- * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In +- * reality, the path/target is not used (ie always set to 0) so our +- * scsi host adapter essentially has 1 bus with 1 target that contains +- * up to 256 luns. +- */ +-#define STORVSC_MAX_LUNS_PER_TARGET 64 +-#define STORVSC_MAX_TARGETS 1 +-#define STORVSC_MAX_CHANNELS 1 +- +- +- +-struct storvsc_cmd_request { +- struct list_head entry; +- struct scsi_cmnd *cmd; +- +- unsigned int bounce_sgl_count; +- struct scatterlist *bounce_sgl; +- +- struct hv_device *device; +- +- /* Synchronize the request/response if needed */ +- struct completion wait_event; +- +- unsigned char *sense_buffer; +- struct hv_multipage_buffer data_buffer; +- struct vstor_packet vstor_packet; +-}; +- +- +-/* A storvsc device is a device object that contains a vmbus channel */ +-struct storvsc_device { +- struct hv_device *device; +- +- bool destroy; +- bool drain_notify; +- atomic_t num_outstanding_req; +- struct Scsi_Host *host; +- +- wait_queue_head_t waiting_to_drain; +- +- /* +- * Each unique Port/Path/Target represents 1 channel ie scsi +- * controller. In reality, the pathid, targetid is always 0 +- * and the port is set by us +- */ +- unsigned int port_number; +- unsigned char path_id; +- unsigned char target_id; +- +- /* Used for vsc/vsp channel reset process */ +- struct storvsc_cmd_request init_request; +- struct storvsc_cmd_request reset_request; +-}; +- +-struct stor_mem_pools { +- struct kmem_cache *request_pool; +- mempool_t *request_mempool; +-}; +- +-struct hv_host_device { +- struct hv_device *dev; +- unsigned int port; +- unsigned char path; +- unsigned char target; +-}; +- +-struct storvsc_scan_work { +- struct work_struct work; +- struct Scsi_Host *host; +- uint lun; +-}; +- +-static void storvsc_bus_scan(struct work_struct *work) +-{ +- struct storvsc_scan_work *wrk; +- int id, order_id; +- +- wrk = container_of(work, struct storvsc_scan_work, work); +- for (id = 0; id < wrk->host->max_id; ++id) { +- if (wrk->host->reverse_ordering) +- order_id = wrk->host->max_id - id - 1; +- else +- order_id = id; +- +- scsi_scan_target(&wrk->host->shost_gendev, 0, +- order_id, SCAN_WILD_CARD, 1); +- } +- kfree(wrk); +-} +- +-static void storvsc_remove_lun(struct work_struct *work) +-{ +- struct storvsc_scan_work *wrk; +- struct scsi_device *sdev; +- +- wrk = container_of(work, struct storvsc_scan_work, work); +- if (!scsi_host_get(wrk->host)) +- goto done; +- +- sdev = scsi_device_lookup(wrk->host, 0, 0, wrk->lun); +- +- if (sdev) { +- scsi_remove_device(sdev); +- scsi_device_put(sdev); +- } +- scsi_host_put(wrk->host); +- +-done: +- kfree(wrk); +-} +- +-/* +- * Major/minor macros. Minor version is in LSB, meaning that earlier flat +- * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). +- */ +- +-static inline u16 storvsc_get_version(u8 major, u8 minor) +-{ +- u16 version; +- +- version = ((major << 8) | minor); +- return version; +-} +- +-/* +- * We can get incoming messages from the host that are not in response to +- * messages that we have sent out. An example of this would be messages +- * received by the guest to notify dynamic addition/removal of LUNs. To +- * deal with potential race conditions where the driver may be in the +- * midst of being unloaded when we might receive an unsolicited message +- * from the host, we have implemented a mechanism to gurantee sequential +- * consistency: +- * +- * 1) Once the device is marked as being destroyed, we will fail all +- * outgoing messages. +- * 2) We permit incoming messages when the device is being destroyed, +- * only to properly account for messages already sent out. +- */ +- +-static inline struct storvsc_device *get_out_stor_device( +- struct hv_device *device) +-{ +- struct storvsc_device *stor_device; +- +- stor_device = hv_get_drvdata(device); +- +- if (stor_device && stor_device->destroy) +- stor_device = NULL; +- +- return stor_device; +-} +- +- +-static inline void storvsc_wait_to_drain(struct storvsc_device *dev) +-{ +- dev->drain_notify = true; +- wait_event(dev->waiting_to_drain, +- atomic_read(&dev->num_outstanding_req) == 0); +- dev->drain_notify = false; +-} +- +-static inline struct storvsc_device *get_in_stor_device( +- struct hv_device *device) +-{ +- struct storvsc_device *stor_device; +- +- stor_device = hv_get_drvdata(device); +- +- if (!stor_device) +- goto get_in_err; +- +- /* +- * If the device is being destroyed; allow incoming +- * traffic only to cleanup outstanding requests. +- */ +- +- if (stor_device->destroy && +- (atomic_read(&stor_device->num_outstanding_req) == 0)) +- stor_device = NULL; +- +-get_in_err: +- return stor_device; +- +-} +- +-static void destroy_bounce_buffer(struct scatterlist *sgl, +- unsigned int sg_count) +-{ +- int i; +- struct page *page_buf; +- +- for (i = 0; i < sg_count; i++) { +- page_buf = sg_page((&sgl[i])); +- if (page_buf != NULL) +- __free_page(page_buf); +- } +- +- kfree(sgl); +-} +- +-static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) +-{ +- int i; +- +- /* No need to check */ +- if (sg_count < 2) +- return -1; +- +- /* We have at least 2 sg entries */ +- for (i = 0; i < sg_count; i++) { +- if (i == 0) { +- /* make sure 1st one does not have hole */ +- if (sgl[i].offset + sgl[i].length != PAGE_SIZE) +- return i; +- } else if (i == sg_count - 1) { +- /* make sure last one does not have hole */ +- if (sgl[i].offset != 0) +- return i; +- } else { +- /* make sure no hole in the middle */ +- if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) +- return i; +- } +- } +- return -1; +-} +- +-static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, +- unsigned int sg_count, +- unsigned int len, +- int write) +-{ +- int i; +- int num_pages; +- struct scatterlist *bounce_sgl; +- struct page *page_buf; +- unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); +- +- num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; +- +- bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); +- if (!bounce_sgl) +- return NULL; +- +- for (i = 0; i < num_pages; i++) { +- page_buf = alloc_page(GFP_ATOMIC); +- if (!page_buf) +- goto cleanup; +- sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); +- } +- +- return bounce_sgl; +- +-cleanup: +- destroy_bounce_buffer(bounce_sgl, num_pages); +- return NULL; +-} +- +-/* Assume the original sgl has enough room */ +-static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, +- struct scatterlist *bounce_sgl, +- unsigned int orig_sgl_count, +- unsigned int bounce_sgl_count) +-{ +- int i; +- int j = 0; +- unsigned long src, dest; +- unsigned int srclen, destlen, copylen; +- unsigned int total_copied = 0; +- unsigned long bounce_addr = 0; +- unsigned long dest_addr = 0; +- unsigned long flags; +- +- local_irq_save(flags); +- +- for (i = 0; i < orig_sgl_count; i++) { +- dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; +- dest = dest_addr; +- destlen = orig_sgl[i].length; +- +- if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); +- +- while (destlen) { +- src = bounce_addr + bounce_sgl[j].offset; +- srclen = bounce_sgl[j].length - bounce_sgl[j].offset; +- +- copylen = min(srclen, destlen); +- memcpy((void *)dest, (void *)src, copylen); +- +- total_copied += copylen; +- bounce_sgl[j].offset += copylen; +- destlen -= copylen; +- dest += copylen; +- +- if (bounce_sgl[j].offset == bounce_sgl[j].length) { +- /* full */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- j++; +- +- /* +- * It is possible that the number of elements +- * in the bounce buffer may not be equal to +- * the number of elements in the original +- * scatter list. Handle this correctly. +- */ +- +- if (j == bounce_sgl_count) { +- /* +- * We are done; cleanup and return. +- */ +- kunmap_atomic((void *)(dest_addr - +- orig_sgl[i].offset), +- KM_IRQ0); +- local_irq_restore(flags); +- return total_copied; +- } +- +- /* if we need to use another bounce buffer */ +- if (destlen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); +- } else if (destlen == 0 && i == orig_sgl_count - 1) { +- /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- } +- } +- +- kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), +- KM_IRQ0); +- } +- +- local_irq_restore(flags); +- +- return total_copied; +-} +- +-/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ +-static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, +- struct scatterlist *bounce_sgl, +- unsigned int orig_sgl_count) +-{ +- int i; +- int j = 0; +- unsigned long src, dest; +- unsigned int srclen, destlen, copylen; +- unsigned int total_copied = 0; +- unsigned long bounce_addr = 0; +- unsigned long src_addr = 0; +- unsigned long flags; +- +- local_irq_save(flags); +- +- for (i = 0; i < orig_sgl_count; i++) { +- src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; +- src = src_addr; +- srclen = orig_sgl[i].length; +- +- if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); +- +- while (srclen) { +- /* assume bounce offset always == 0 */ +- dest = bounce_addr + bounce_sgl[j].length; +- destlen = PAGE_SIZE - bounce_sgl[j].length; +- +- copylen = min(srclen, destlen); +- memcpy((void *)dest, (void *)src, copylen); +- +- total_copied += copylen; +- bounce_sgl[j].length += copylen; +- srclen -= copylen; +- src += copylen; +- +- if (bounce_sgl[j].length == PAGE_SIZE) { +- /* full..move to next entry */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- j++; +- +- /* if we need to use another bounce buffer */ +- if (srclen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); +- +- } else if (srclen == 0 && i == orig_sgl_count - 1) { +- /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); +- } +- } +- +- kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); +- } +- +- local_irq_restore(flags); +- +- return total_copied; +-} +- +-static int storvsc_channel_init(struct hv_device *device) +-{ +- struct storvsc_device *stor_device; +- struct storvsc_cmd_request *request; +- struct vstor_packet *vstor_packet; +- int ret, t; +- +- stor_device = get_out_stor_device(device); +- if (!stor_device) +- return -ENODEV; +- +- request = &stor_device->init_request; +- vstor_packet = &request->vstor_packet; +- +- /* +- * Now, initiate the vsc/vsp initialization protocol on the open +- * channel +- */ +- memset(request, 0, sizeof(struct storvsc_cmd_request)); +- init_completion(&request->wait_event); +- vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; +- vstor_packet->flags = REQUEST_COMPLETION_FLAG; +- +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || +- vstor_packet->status != 0) +- goto cleanup; +- +- +- /* reuse the packet for version range supported */ +- memset(vstor_packet, 0, sizeof(struct vstor_packet)); +- vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; +- vstor_packet->flags = REQUEST_COMPLETION_FLAG; +- +- vstor_packet->version.major_minor = +- storvsc_get_version(VMSTOR_CURRENT_MAJOR, VMSTOR_CURRENT_MINOR); +- +- /* +- * The revision number is only used in Windows; set it to 0. +- */ +- vstor_packet->version.revision = 0; +- +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || +- vstor_packet->status != 0) +- goto cleanup; +- +- +- memset(vstor_packet, 0, sizeof(struct vstor_packet)); +- vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; +- vstor_packet->flags = REQUEST_COMPLETION_FLAG; +- vstor_packet->storage_channel_properties.port_number = +- stor_device->port_number; +- +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || +- vstor_packet->status != 0) +- goto cleanup; +- +- stor_device->path_id = vstor_packet->storage_channel_properties.path_id; +- stor_device->target_id +- = vstor_packet->storage_channel_properties.target_id; +- +- memset(vstor_packet, 0, sizeof(struct vstor_packet)); +- vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; +- vstor_packet->flags = REQUEST_COMPLETION_FLAG; +- +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- +- if (ret != 0) +- goto cleanup; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) { +- ret = -ETIMEDOUT; +- goto cleanup; +- } +- +- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || +- vstor_packet->status != 0) +- goto cleanup; +- +- +-cleanup: +- return ret; +-} +- +- +-static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) +-{ +- struct scsi_cmnd *scmnd = cmd_request->cmd; +- struct hv_host_device *host_dev = shost_priv(scmnd->device->host); +- void (*scsi_done_fn)(struct scsi_cmnd *); +- struct scsi_sense_hdr sense_hdr; +- struct vmscsi_request *vm_srb; +- struct storvsc_scan_work *wrk; +- struct stor_mem_pools *memp = scmnd->device->hostdata; +- +- vm_srb = &cmd_request->vstor_packet.vm_srb; +- if (cmd_request->bounce_sgl_count) { +- if (vm_srb->data_in == READ_TYPE) +- copy_from_bounce_buffer(scsi_sglist(scmnd), +- cmd_request->bounce_sgl, +- scsi_sg_count(scmnd), +- cmd_request->bounce_sgl_count); +- destroy_bounce_buffer(cmd_request->bounce_sgl, +- cmd_request->bounce_sgl_count); +- } +- +- /* +- * If there is an error; offline the device since all +- * error recovery strategies would have already been +- * deployed on the host side. +- */ +- if (vm_srb->srb_status == SRB_STATUS_ERROR) +- scmnd->result = DID_TARGET_FAILURE << 16; +- else +- scmnd->result = vm_srb->scsi_status; +- +- /* +- * If the LUN is invalid; remove the device. +- */ +- if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { +- struct storvsc_device *stor_dev; +- struct hv_device *dev = host_dev->dev; +- struct Scsi_Host *host; +- +- stor_dev = get_in_stor_device(dev); +- host = stor_dev->host; +- +- wrk = kmalloc(sizeof(struct storvsc_scan_work), +- GFP_ATOMIC); +- if (!wrk) { +- scmnd->result = DID_TARGET_FAILURE << 16; +- } else { +- wrk->host = host; +- wrk->lun = vm_srb->lun; +- INIT_WORK(&wrk->work, storvsc_remove_lun); +- schedule_work(&wrk->work); +- } +- } +- +- if (scmnd->result) { +- if (scsi_normalize_sense(scmnd->sense_buffer, +- SCSI_SENSE_BUFFERSIZE, &sense_hdr)) +- scsi_print_sense_hdr("storvsc", &sense_hdr); +- } +- +- scsi_set_resid(scmnd, +- cmd_request->data_buffer.len - +- vm_srb->data_transfer_length); +- +- scsi_done_fn = scmnd->scsi_done; +- +- scmnd->host_scribble = NULL; +- scmnd->scsi_done = NULL; +- +- scsi_done_fn(scmnd); +- +- mempool_free(cmd_request, memp->request_mempool); +-} +- +-static void storvsc_on_io_completion(struct hv_device *device, +- struct vstor_packet *vstor_packet, +- struct storvsc_cmd_request *request) +-{ +- struct storvsc_device *stor_device; +- struct vstor_packet *stor_pkt; +- +- stor_device = hv_get_drvdata(device); +- stor_pkt = &request->vstor_packet; +- +- /* +- * The current SCSI handling on the host side does +- * not correctly handle: +- * INQUIRY command with page code parameter set to 0x80 +- * MODE_SENSE command with cmd[2] == 0x1c +- * +- * Setup srb and scsi status so this won't be fatal. +- * We do this so we can distinguish truly fatal failues +- * (srb status == 0x4) and off-line the device in that case. +- */ +- +- if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || +- (stor_pkt->vm_srb.cdb[0] == MODE_SENSE)) { +- vstor_packet->vm_srb.scsi_status = 0; +- vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; +- } +- +- +- /* Copy over the status...etc */ +- stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; +- stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; +- stor_pkt->vm_srb.sense_info_length = +- vstor_packet->vm_srb.sense_info_length; +- +- if (vstor_packet->vm_srb.scsi_status != 0 || +- vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS){ +- 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 & +- SRB_STATUS_AUTOSENSE_VALID) { +- /* autosense data available */ +- dev_warn(&device->device, +- "stor 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, +- vstor_packet->vm_srb.sense_info_length); +- +- } +- } +- +- stor_pkt->vm_srb.data_transfer_length = +- vstor_packet->vm_srb.data_transfer_length; +- +- storvsc_command_completion(request); +- +- if (atomic_dec_and_test(&stor_device->num_outstanding_req) && +- stor_device->drain_notify) +- wake_up(&stor_device->waiting_to_drain); +- +- +-} +- +-static void storvsc_on_receive(struct hv_device *device, +- struct vstor_packet *vstor_packet, +- struct storvsc_cmd_request *request) +-{ +- struct storvsc_scan_work *work; +- struct storvsc_device *stor_device; +- +- switch (vstor_packet->operation) { +- case VSTOR_OPERATION_COMPLETE_IO: +- storvsc_on_io_completion(device, vstor_packet, request); +- break; +- +- case VSTOR_OPERATION_REMOVE_DEVICE: +- case VSTOR_OPERATION_ENUMERATE_BUS: +- stor_device = get_in_stor_device(device); +- work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); +- if (!work) +- return; +- +- INIT_WORK(&work->work, storvsc_bus_scan); +- work->host = stor_device->host; +- schedule_work(&work->work); +- break; +- +- default: +- break; +- } +-} +- +-static void storvsc_on_channel_callback(void *context) +-{ +- struct hv_device *device = (struct hv_device *)context; +- struct storvsc_device *stor_device; +- u32 bytes_recvd; +- u64 request_id; +- unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; +- struct storvsc_cmd_request *request; +- int ret; +- +- +- stor_device = get_in_stor_device(device); +- if (!stor_device) +- return; +- +- do { +- ret = vmbus_recvpacket(device->channel, packet, +- ALIGN(sizeof(struct vstor_packet), 8), +- &bytes_recvd, &request_id); +- if (ret == 0 && bytes_recvd > 0) { +- +- request = (struct storvsc_cmd_request *) +- (unsigned long)request_id; +- +- if ((request == &stor_device->init_request) || +- (request == &stor_device->reset_request)) { +- +- memcpy(&request->vstor_packet, packet, +- sizeof(struct vstor_packet)); +- complete(&request->wait_event); +- } else { +- storvsc_on_receive(device, +- (struct vstor_packet *)packet, +- request); +- } +- } else { +- break; +- } +- } while (1); +- +- return; +-} +- +-static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) +-{ +- struct vmstorage_channel_properties props; +- int ret; +- +- memset(&props, 0, sizeof(struct vmstorage_channel_properties)); +- +- ret = vmbus_open(device->channel, +- ring_size, +- ring_size, +- (void *)&props, +- sizeof(struct vmstorage_channel_properties), +- storvsc_on_channel_callback, device); +- +- if (ret != 0) +- return ret; +- +- ret = storvsc_channel_init(device); +- +- return ret; +-} +- +-static int storvsc_dev_remove(struct hv_device *device) +-{ +- struct storvsc_device *stor_device; +- unsigned long flags; +- +- stor_device = hv_get_drvdata(device); +- +- spin_lock_irqsave(&device->channel->inbound_lock, flags); +- stor_device->destroy = true; +- spin_unlock_irqrestore(&device->channel->inbound_lock, flags); +- +- /* +- * At this point, all outbound traffic should be disable. We +- * only allow inbound traffic (responses) to proceed so that +- * outstanding requests can be completed. +- */ +- +- storvsc_wait_to_drain(stor_device); +- +- /* +- * Since we have already drained, we don't need to busy wait +- * as was done in final_release_stor_device() +- * Note that we cannot set the ext pointer to NULL until +- * we have drained - to drain the outgoing packets, we need to +- * allow incoming packets. +- */ +- spin_lock_irqsave(&device->channel->inbound_lock, flags); +- hv_set_drvdata(device, NULL); +- spin_unlock_irqrestore(&device->channel->inbound_lock, flags); +- +- /* Close the channel */ +- vmbus_close(device->channel); +- +- kfree(stor_device); +- return 0; +-} +- +-static int storvsc_do_io(struct hv_device *device, +- struct storvsc_cmd_request *request) +-{ +- struct storvsc_device *stor_device; +- struct vstor_packet *vstor_packet; +- int ret = 0; +- +- vstor_packet = &request->vstor_packet; +- stor_device = get_out_stor_device(device); +- +- if (!stor_device) +- return -ENODEV; +- +- +- request->device = device; +- +- +- vstor_packet->flags |= REQUEST_COMPLETION_FLAG; +- +- vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); +- +- +- vstor_packet->vm_srb.sense_info_length = STORVSC_SENSE_BUFFER_SIZE; +- +- +- vstor_packet->vm_srb.data_transfer_length = +- request->data_buffer.len; +- +- vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; +- +- if (request->data_buffer.len) { +- ret = vmbus_sendpacket_multipagebuffer(device->channel, +- &request->data_buffer, +- vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request); +- } else { +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- } +- +- if (ret != 0) +- return ret; +- +- atomic_inc(&stor_device->num_outstanding_req); +- +- return ret; +-} +- +-static int storvsc_device_alloc(struct scsi_device *sdevice) +-{ +- struct stor_mem_pools *memp; +- int number = STORVSC_MIN_BUF_NR; +- +- memp = kzalloc(sizeof(struct stor_mem_pools), GFP_KERNEL); +- if (!memp) +- return -ENOMEM; +- +- memp->request_pool = +- kmem_cache_create(dev_name(&sdevice->sdev_dev), +- sizeof(struct storvsc_cmd_request), 0, +- SLAB_HWCACHE_ALIGN, NULL); +- +- if (!memp->request_pool) +- goto err0; +- +- memp->request_mempool = mempool_create(number, mempool_alloc_slab, +- mempool_free_slab, +- memp->request_pool); +- +- if (!memp->request_mempool) +- goto err1; +- +- sdevice->hostdata = memp; +- +- return 0; +- +-err1: +- kmem_cache_destroy(memp->request_pool); +- +-err0: +- kfree(memp); +- return -ENOMEM; +-} +- +-static void storvsc_device_destroy(struct scsi_device *sdevice) +-{ +- struct stor_mem_pools *memp = sdevice->hostdata; +- +- mempool_destroy(memp->request_mempool); +- kmem_cache_destroy(memp->request_pool); +- kfree(memp); +- sdevice->hostdata = NULL; +-} +- +-static int storvsc_device_configure(struct scsi_device *sdevice) +-{ +- scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG, +- STORVSC_MAX_IO_REQUESTS); +- +- blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE); +- +- blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); +- +- return 0; +-} +- +-static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev, +- sector_t capacity, int *info) +-{ +- sector_t nsect = capacity; +- sector_t cylinders = nsect; +- int heads, sectors_pt; +- +- /* +- * We are making up these values; let us keep it simple. +- */ +- heads = 0xff; +- sectors_pt = 0x3f; /* Sectors per track */ +- sector_div(cylinders, heads * sectors_pt); +- if ((sector_t)(cylinders + 1) * heads * sectors_pt < nsect) +- cylinders = 0xffff; +- +- info[0] = heads; +- info[1] = sectors_pt; +- info[2] = (int)cylinders; +- +- return 0; +-} +- +-static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) +-{ +- struct hv_host_device *host_dev = shost_priv(scmnd->device->host); +- struct hv_device *device = host_dev->dev; +- +- struct storvsc_device *stor_device; +- struct storvsc_cmd_request *request; +- struct vstor_packet *vstor_packet; +- int ret, t; +- +- +- stor_device = get_out_stor_device(device); +- if (!stor_device) +- return FAILED; +- +- request = &stor_device->reset_request; +- vstor_packet = &request->vstor_packet; +- +- init_completion(&request->wait_event); +- +- vstor_packet->operation = VSTOR_OPERATION_RESET_BUS; +- vstor_packet->flags = REQUEST_COMPLETION_FLAG; +- vstor_packet->vm_srb.path_id = stor_device->path_id; +- +- ret = vmbus_sendpacket(device->channel, vstor_packet, +- sizeof(struct vstor_packet), +- (unsigned long)&stor_device->reset_request, +- VM_PKT_DATA_INBAND, +- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +- if (ret != 0) +- return FAILED; +- +- t = wait_for_completion_timeout(&request->wait_event, 5*HZ); +- if (t == 0) +- return TIMEOUT_ERROR; +- +- +- /* +- * At this point, all outstanding requests in the adapter +- * should have been flushed out and return to us +- */ +- +- return SUCCESS; +-} +- +-static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) +-{ +- bool allowed = true; +- u8 scsi_op = scmnd->cmnd[0]; +- +- switch (scsi_op) { +- /* +- * smartd sends this command and the host does not handle +- * this. So, don't send it. +- */ +- case SET_WINDOW: +- scmnd->result = ILLEGAL_REQUEST << 16; +- allowed = false; +- break; +- default: +- break; +- } +- return allowed; +-} +- +-static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) +-{ +- int ret; +- struct hv_host_device *host_dev = shost_priv(host); +- struct hv_device *dev = host_dev->dev; +- struct storvsc_cmd_request *cmd_request; +- unsigned int request_size = 0; +- int i; +- struct scatterlist *sgl; +- unsigned int sg_count = 0; +- struct vmscsi_request *vm_srb; +- struct stor_mem_pools *memp = scmnd->device->hostdata; +- +- if (!storvsc_scsi_cmd_ok(scmnd)) { +- scmnd->scsi_done(scmnd); +- return 0; +- } +- +- request_size = sizeof(struct storvsc_cmd_request); +- +- cmd_request = mempool_alloc(memp->request_mempool, +- GFP_ATOMIC); +- +- /* +- * We might be invoked in an interrupt context; hence +- * mempool_alloc() can fail. +- */ +- if (!cmd_request) +- return SCSI_MLQUEUE_DEVICE_BUSY; +- +- memset(cmd_request, 0, sizeof(struct storvsc_cmd_request)); +- +- /* Setup the cmd request */ +- cmd_request->cmd = scmnd; +- +- scmnd->host_scribble = (unsigned char *)cmd_request; +- +- vm_srb = &cmd_request->vstor_packet.vm_srb; +- +- +- /* Build the SRB */ +- switch (scmnd->sc_data_direction) { +- case DMA_TO_DEVICE: +- vm_srb->data_in = WRITE_TYPE; +- break; +- case DMA_FROM_DEVICE: +- vm_srb->data_in = READ_TYPE; +- break; +- default: +- vm_srb->data_in = UNKNOWN_TYPE; +- break; +- } +- +- +- vm_srb->port_number = host_dev->port; +- vm_srb->path_id = scmnd->device->channel; +- vm_srb->target_id = scmnd->device->id; +- vm_srb->lun = scmnd->device->lun; +- +- vm_srb->cdb_length = scmnd->cmd_len; +- +- memcpy(vm_srb->cdb, scmnd->cmnd, vm_srb->cdb_length); +- +- cmd_request->sense_buffer = scmnd->sense_buffer; +- +- +- cmd_request->data_buffer.len = scsi_bufflen(scmnd); +- if (scsi_sg_count(scmnd)) { +- sgl = (struct scatterlist *)scsi_sglist(scmnd); +- sg_count = scsi_sg_count(scmnd); +- +- /* check if we need to bounce the sgl */ +- if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { +- cmd_request->bounce_sgl = +- create_bounce_buffer(sgl, scsi_sg_count(scmnd), +- scsi_bufflen(scmnd), +- vm_srb->data_in); +- if (!cmd_request->bounce_sgl) { +- ret = SCSI_MLQUEUE_HOST_BUSY; +- goto queue_error; +- } +- +- cmd_request->bounce_sgl_count = +- ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> +- PAGE_SHIFT; +- +- if (vm_srb->data_in == WRITE_TYPE) +- copy_to_bounce_buffer(sgl, +- cmd_request->bounce_sgl, +- scsi_sg_count(scmnd)); +- +- sgl = cmd_request->bounce_sgl; +- sg_count = cmd_request->bounce_sgl_count; +- } +- +- cmd_request->data_buffer.offset = sgl[0].offset; +- +- for (i = 0; i < sg_count; i++) +- cmd_request->data_buffer.pfn_array[i] = +- page_to_pfn(sg_page((&sgl[i]))); +- +- } else if (scsi_sglist(scmnd)) { +- cmd_request->data_buffer.offset = +- virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1); +- cmd_request->data_buffer.pfn_array[0] = +- virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT; +- } +- +- /* Invokes the vsc to start an IO */ +- ret = storvsc_do_io(dev, cmd_request); +- +- if (ret == -EAGAIN) { +- /* no more space */ +- +- if (cmd_request->bounce_sgl_count) { +- destroy_bounce_buffer(cmd_request->bounce_sgl, +- cmd_request->bounce_sgl_count); +- +- ret = SCSI_MLQUEUE_DEVICE_BUSY; +- goto queue_error; +- } +- } +- +- return 0; +- +-queue_error: +- mempool_free(cmd_request, memp->request_mempool); +- scmnd->host_scribble = NULL; +- return ret; +-} +- +-static struct scsi_host_template scsi_driver = { +- .module = THIS_MODULE, +- .name = "storvsc_host_t", +- .bios_param = storvsc_get_chs, +- .queuecommand = storvsc_queuecommand, +- .eh_host_reset_handler = storvsc_host_reset_handler, +- .slave_alloc = storvsc_device_alloc, +- .slave_destroy = storvsc_device_destroy, +- .slave_configure = storvsc_device_configure, +- .cmd_per_lun = 1, +- /* 64 max_queue * 1 target */ +- .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS, +- .this_id = -1, +- /* no use setting to 0 since ll_blk_rw reset it to 1 */ +- /* currently 32 */ +- .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT, +- .use_clustering = DISABLE_CLUSTERING, +- /* Make sure we dont get a sg segment crosses a page boundary */ +- .dma_boundary = PAGE_SIZE-1, +-}; +- +-enum { +- SCSI_GUID, +- IDE_GUID, +-}; +- +-static const struct hv_vmbus_device_id id_table[] = { +- /* SCSI guid */ +- { VMBUS_DEVICE(0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, +- 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f) +- .driver_data = SCSI_GUID }, +- /* IDE guid */ +- { VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, +- 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5) +- .driver_data = IDE_GUID }, +- { }, +-}; +- +-MODULE_DEVICE_TABLE(vmbus, id_table); +- +-static int storvsc_probe(struct hv_device *device, +- const struct hv_vmbus_device_id *dev_id) +-{ +- int ret; +- struct Scsi_Host *host; +- struct hv_host_device *host_dev; +- bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false); +- int target = 0; +- struct storvsc_device *stor_device; +- +- host = scsi_host_alloc(&scsi_driver, +- sizeof(struct hv_host_device)); +- if (!host) +- return -ENOMEM; +- +- host_dev = shost_priv(host); +- memset(host_dev, 0, sizeof(struct hv_host_device)); +- +- host_dev->port = host->host_no; +- host_dev->dev = device; +- +- +- stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); +- if (!stor_device) { +- ret = -ENOMEM; +- goto err_out0; +- } +- +- stor_device->destroy = false; +- init_waitqueue_head(&stor_device->waiting_to_drain); +- stor_device->device = device; +- stor_device->host = host; +- hv_set_drvdata(device, stor_device); +- +- stor_device->port_number = host->host_no; +- ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size); +- if (ret) +- goto err_out1; +- +- host_dev->path = stor_device->path_id; +- host_dev->target = stor_device->target_id; +- +- /* max # of devices per target */ +- host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; +- /* max # of targets per channel */ +- host->max_id = STORVSC_MAX_TARGETS; +- /* max # of channels */ +- host->max_channel = STORVSC_MAX_CHANNELS - 1; +- /* max cmd length */ +- host->max_cmd_len = STORVSC_MAX_CMD_LEN; +- +- /* Register the HBA and start the scsi bus scan */ +- ret = scsi_add_host(host, &device->device); +- if (ret != 0) +- goto err_out2; +- +- if (!dev_is_ide) { +- scsi_scan_host(host); +- } else { +- target = (device->dev_instance.b[5] << 8 | +- device->dev_instance.b[4]); +- ret = scsi_add_device(host, 0, target, 0); +- if (ret) { +- scsi_remove_host(host); +- goto err_out2; +- } +- } +- return 0; +- +-err_out2: +- /* +- * Once we have connected with the host, we would need to +- * to invoke storvsc_dev_remove() to rollback this state and +- * this call also frees up the stor_device; hence the jump around +- * err_out1 label. +- */ +- storvsc_dev_remove(device); +- goto err_out0; +- +-err_out1: +- kfree(stor_device); +- +-err_out0: +- scsi_host_put(host); +- return ret; +-} +- +-static int storvsc_remove(struct hv_device *dev) +-{ +- struct storvsc_device *stor_device = hv_get_drvdata(dev); +- struct Scsi_Host *host = stor_device->host; +- +- scsi_remove_host(host); +- storvsc_dev_remove(dev); +- scsi_host_put(host); +- +- return 0; +-} +- +-static struct hv_driver storvsc_drv = { +- .name = KBUILD_MODNAME, +- .id_table = id_table, +- .probe = storvsc_probe, +- .remove = storvsc_remove, +-}; +- +-static int __init storvsc_drv_init(void) +-{ +- u32 max_outstanding_req_per_channel; +- +- /* +- * Divide the ring buffer data size (which is 1 page less +- * than the ring buffer size since that page is reserved for +- * the ring buffer indices) by the max request size (which is +- * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) +- */ +- max_outstanding_req_per_channel = +- ((storvsc_ringbuffer_size - PAGE_SIZE) / +- ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + +- sizeof(struct vstor_packet) + sizeof(u64), +- sizeof(u64))); +- +- if (max_outstanding_req_per_channel < +- STORVSC_MAX_IO_REQUESTS) +- return -EINVAL; +- +- return vmbus_driver_register(&storvsc_drv); +-} +- +-static void __exit storvsc_drv_exit(void) +-{ +- vmbus_driver_unregister(&storvsc_drv); +-} +- +-MODULE_LICENSE("GPL"); +-MODULE_VERSION(HV_DRV_VERSION); +-MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); +-module_init(storvsc_drv_init); +-module_exit(storvsc_drv_exit); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0068-net-hyperv-Use-the-built-in-macro-KBUILD_MODNAME-for.patch b/debian/patches/features/x86/hyperv/0068-net-hyperv-Use-the-built-in-macro-KBUILD_MODNAME-for.patch new file mode 100644 index 000000000..4c9e8324e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0068-net-hyperv-Use-the-built-in-macro-KBUILD_MODNAME-for.patch @@ -0,0 +1,40 @@ +From: Haiyang Zhang +Date: Wed, 7 Mar 2012 10:02:00 +0000 +Subject: [PATCH 68/77] net/hyperv: Use the built-in macro KBUILD_MODNAME for + this driver + +commit d31b20fcc89efa8c5d3f5ea2720e08a286b69a36 upstream. + +Signed-off-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Cc: Olaf Hering +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 0ae7a1a..217dfed 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -310,7 +310,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, + static void netvsc_get_drvinfo(struct net_device *net, + struct ethtool_drvinfo *info) + { +- strcpy(info->driver, "hv_netvsc"); ++ strcpy(info->driver, KBUILD_MODNAME); + strcpy(info->version, HV_DRV_VERSION); + strcpy(info->fw_version, "N/A"); + } +@@ -482,7 +482,7 @@ MODULE_DEVICE_TABLE(vmbus, id_table); + + /* The one and only one */ + static struct hv_driver netvsc_drv = { +- .name = "netvsc", ++ .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = netvsc_probe, + .remove = netvsc_remove, +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0069-net-hyperv-Fix-data-corruption-in-rndis_filter_recei.patch b/debian/patches/features/x86/hyperv/0069-net-hyperv-Fix-data-corruption-in-rndis_filter_recei.patch new file mode 100644 index 000000000..33bbae56a --- /dev/null +++ b/debian/patches/features/x86/hyperv/0069-net-hyperv-Fix-data-corruption-in-rndis_filter_recei.patch @@ -0,0 +1,94 @@ +From: Haiyang Zhang +Date: Mon, 12 Mar 2012 10:20:49 +0000 +Subject: [PATCH 69/77] net/hyperv: Fix data corruption in + rndis_filter_receive() + +commit ef31bef6216db76950c38f1993b45953402f4c63 upstream. + +Limiting the memcpy to be the sizeof(struct rndis_message) can truncate +the message if there are Per-Packet-Info or Out-of-Band data. + +In my earlier patch (commit 45326342), the unnecessary kmap_atomic and +kunmap_atomic surrounding this memcpy have been removed because the memory +in the receive buffer is always mapped. This memcpy is not necessary +either. To fix the bug, I removed the memcpy. + +Signed-off-by: Haiyang Zhang +Reviewed-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/rndis_filter.c | 33 +++++++++------------------------ + 1 file changed, 9 insertions(+), 24 deletions(-) + +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index 136efd8..0c3d7d9 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -352,8 +352,7 @@ int rndis_filter_receive(struct hv_device *dev, + { + struct netvsc_device *net_dev = hv_get_drvdata(dev); + struct rndis_device *rndis_dev; +- struct rndis_message rndis_msg; +- struct rndis_message *rndis_hdr; ++ struct rndis_message *rndis_msg; + struct net_device *ndev; + + if (!net_dev) +@@ -375,46 +374,32 @@ int rndis_filter_receive(struct hv_device *dev, + return -ENODEV; + } + +- rndis_hdr = pkt->data; +- +- /* Make sure we got a valid rndis message */ +- if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && +- (rndis_hdr->msg_len > sizeof(struct rndis_message))) { +- netdev_err(ndev, "incoming rndis message buffer overflow " +- "detected (got %u, max %zu)..marking it an error!\n", +- rndis_hdr->msg_len, +- sizeof(struct rndis_message)); +- } ++ rndis_msg = pkt->data; + +- memcpy(&rndis_msg, rndis_hdr, +- (rndis_hdr->msg_len > sizeof(struct rndis_message)) ? +- sizeof(struct rndis_message) : +- rndis_hdr->msg_len); ++ dump_rndis_message(dev, rndis_msg); + +- dump_rndis_message(dev, &rndis_msg); +- +- switch (rndis_msg.ndis_msg_type) { ++ switch (rndis_msg->ndis_msg_type) { + case REMOTE_NDIS_PACKET_MSG: + /* data msg */ +- rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt); ++ rndis_filter_receive_data(rndis_dev, rndis_msg, pkt); + break; + + case REMOTE_NDIS_INITIALIZE_CMPLT: + case REMOTE_NDIS_QUERY_CMPLT: + case REMOTE_NDIS_SET_CMPLT: + /* completion msgs */ +- rndis_filter_receive_response(rndis_dev, &rndis_msg); ++ rndis_filter_receive_response(rndis_dev, rndis_msg); + break; + + case REMOTE_NDIS_INDICATE_STATUS_MSG: + /* notification msgs */ +- rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); ++ rndis_filter_receive_indicate_status(rndis_dev, rndis_msg); + break; + default: + netdev_err(ndev, + "unhandled rndis message (type %u len %u)\n", +- rndis_msg.ndis_msg_type, +- rndis_msg.msg_len); ++ rndis_msg->ndis_msg_type, ++ rndis_msg->msg_len); + break; + } + +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0070-net-hyperv-Add-support-for-vlan-trunking-from-guests.patch b/debian/patches/features/x86/hyperv/0070-net-hyperv-Add-support-for-vlan-trunking-from-guests.patch new file mode 100644 index 000000000..e0822f01b --- /dev/null +++ b/debian/patches/features/x86/hyperv/0070-net-hyperv-Add-support-for-vlan-trunking-from-guests.patch @@ -0,0 +1,250 @@ +From: Haiyang Zhang +Date: Mon, 12 Mar 2012 10:20:50 +0000 +Subject: [PATCH 70/77] net/hyperv: Add support for vlan trunking from guests + +commit 1f5f3a75e216fe771b8d6805e0bb2f43595a6ee1 upstream. + +With this feature, a Linux guest can now configure multiple vlans through +a single synthetic NIC on Win8 Hyper-V host. + +Signed-off-by: Haiyang Zhang +Reviewed-by: K. Y. Srinivasan +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/hyperv_net.h | 34 ++++++++++++++++++++- + drivers/net/hyperv/netvsc.c | 3 +- + drivers/net/hyperv/netvsc_drv.c | 8 +++-- + drivers/net/hyperv/rndis_filter.c | 60 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 101 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h +index dec5836..c358245 100644 +--- a/drivers/net/hyperv/hyperv_net.h ++++ b/drivers/net/hyperv/hyperv_net.h +@@ -49,6 +49,7 @@ struct hv_netvsc_packet { + + struct hv_device *device; + bool is_data_pkt; ++ u16 vlan_tci; + + /* + * Valid only for receives when we break a xfer page packet +@@ -926,9 +927,40 @@ struct rndis_oobd { + struct rndis_per_packet_info { + u32 size; + u32 type; +- u32 per_pkt_info_offset; ++ u32 ppi_offset; ++}; ++ ++enum ndis_per_pkt_info_type { ++ TCPIP_CHKSUM_PKTINFO, ++ IPSEC_PKTINFO, ++ TCP_LARGESEND_PKTINFO, ++ CLASSIFICATION_HANDLE_PKTINFO, ++ NDIS_RESERVED, ++ SG_LIST_PKTINFO, ++ IEEE_8021Q_INFO, ++ ORIGINAL_PKTINFO, ++ PACKET_CANCEL_ID, ++ ORIGINAL_NET_BUFLIST, ++ CACHED_NET_BUFLIST, ++ SHORT_PKT_PADINFO, ++ MAX_PER_PKT_INFO ++}; ++ ++struct ndis_pkt_8021q_info { ++ union { ++ struct { ++ u32 pri:3; /* User Priority */ ++ u32 cfi:1; /* Canonical Format ID */ ++ u32 vlanid:12; /* VLAN ID */ ++ u32 reserved:16; ++ }; ++ u32 value; ++ }; + }; + ++#define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ ++ sizeof(struct ndis_pkt_8021q_info)) ++ + /* Format of Information buffer passed in a SetRequest for the OID */ + /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ + struct rndis_config_parameter_info { +diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c +index 8965b45..d025c83 100644 +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -300,6 +300,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, + memset(init_packet, 0, sizeof(struct nvsp_message)); + init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; + init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu; ++ init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1; + + ret = vmbus_sendpacket(device->channel, init_packet, + sizeof(struct nvsp_message), +@@ -341,7 +342,7 @@ static int netvsc_connect_vsp(struct hv_device *device) + /* Send the ndis version */ + memset(init_packet, 0, sizeof(struct nvsp_message)); + +- ndis_version = 0x00050000; ++ ndis_version = 0x00050001; + + init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; + init_packet->msg.v1_msg. +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 217dfed..0f8e834 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -159,7 +159,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + /* Allocate a netvsc packet based on # of frags. */ + packet = kzalloc(sizeof(struct hv_netvsc_packet) + + (num_pages * sizeof(struct hv_page_buffer)) + +- sizeof(struct rndis_filter_packet), GFP_ATOMIC); ++ sizeof(struct rndis_filter_packet) + ++ NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); + if (!packet) { + /* out of memory, drop packet */ + netdev_err(net, "unable to allocate hv_netvsc_packet\n"); +@@ -169,6 +170,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + return NETDEV_TX_BUSY; + } + ++ packet->vlan_tci = skb->vlan_tci; ++ + packet->extension = (void *)(unsigned long)packet + + sizeof(struct hv_netvsc_packet) + + (num_pages * sizeof(struct hv_page_buffer)); +@@ -293,6 +296,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, + + skb->protocol = eth_type_trans(skb, net); + skb->ip_summed = CHECKSUM_NONE; ++ skb->vlan_tci = packet->vlan_tci; + + net->stats.rx_packets++; + net->stats.rx_bytes += packet->total_data_buflen; +@@ -407,7 +411,7 @@ static int netvsc_probe(struct hv_device *dev, + + /* TODO: Add GSO and Checksum offload */ + net->hw_features = NETIF_F_SG; +- net->features = NETIF_F_SG; ++ net->features = NETIF_F_SG | NETIF_F_HW_VLAN_TX; + + SET_ETHTOOL_OPS(net, ðtool_ops); + SET_NETDEV_DEV(net, &dev->device); +diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c +index 0c3d7d9..d6be64b 100644 +--- a/drivers/net/hyperv/rndis_filter.c ++++ b/drivers/net/hyperv/rndis_filter.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include "hyperv_net.h" + +@@ -303,12 +304,39 @@ static void rndis_filter_receive_indicate_status(struct rndis_device *dev, + } + } + ++/* ++ * Get the Per-Packet-Info with the specified type ++ * return NULL if not found. ++ */ ++static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) ++{ ++ struct rndis_per_packet_info *ppi; ++ int len; ++ ++ if (rpkt->per_pkt_info_offset == 0) ++ return NULL; ++ ++ ppi = (struct rndis_per_packet_info *)((ulong)rpkt + ++ rpkt->per_pkt_info_offset); ++ len = rpkt->per_pkt_info_len; ++ ++ while (len > 0) { ++ if (ppi->type == type) ++ return (void *)((ulong)ppi + ppi->ppi_offset); ++ len -= ppi->size; ++ ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); ++ } ++ ++ return NULL; ++} ++ + static void rndis_filter_receive_data(struct rndis_device *dev, + struct rndis_message *msg, + struct hv_netvsc_packet *pkt) + { + struct rndis_packet *rndis_pkt; + u32 data_offset; ++ struct ndis_pkt_8021q_info *vlan; + + rndis_pkt = &msg->msg.pkt; + +@@ -344,6 +372,14 @@ static void rndis_filter_receive_data(struct rndis_device *dev, + + pkt->is_data_pkt = true; + ++ vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); ++ if (vlan) { ++ pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | ++ (vlan->pri << VLAN_PRIO_SHIFT); ++ } else { ++ pkt->vlan_tci = 0; ++ } ++ + netvsc_recv_callback(dev->net_dev->dev, pkt); + } + +@@ -759,12 +795,15 @@ int rndis_filter_send(struct hv_device *dev, + struct rndis_message *rndis_msg; + struct rndis_packet *rndis_pkt; + u32 rndis_msg_size; ++ bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT; + + /* Add the rndis header */ + filter_pkt = (struct rndis_filter_packet *)pkt->extension; + + rndis_msg = &filter_pkt->msg; + rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); ++ if (isvlan) ++ rndis_msg_size += NDIS_VLAN_PPI_SIZE; + + rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; + rndis_msg->msg_len = pkt->total_data_buflen + +@@ -772,8 +811,29 @@ int rndis_filter_send(struct hv_device *dev, + + rndis_pkt = &rndis_msg->msg.pkt; + rndis_pkt->data_offset = sizeof(struct rndis_packet); ++ if (isvlan) ++ rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE; + rndis_pkt->data_len = pkt->total_data_buflen; + ++ if (isvlan) { ++ struct rndis_per_packet_info *ppi; ++ struct ndis_pkt_8021q_info *vlan; ++ ++ rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); ++ rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE; ++ ++ ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt + ++ rndis_pkt->per_pkt_info_offset); ++ ppi->size = NDIS_VLAN_PPI_SIZE; ++ ppi->type = IEEE_8021Q_INFO; ++ ppi->ppi_offset = sizeof(struct rndis_per_packet_info); ++ ++ vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi + ++ ppi->ppi_offset); ++ vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK; ++ vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; ++ } ++ + pkt->is_data_pkt = true; + pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; + pkt->page_buf[0].offset = +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0071-Drivers-hv-Add-new-message-types-to-enhance-KVP.patch b/debian/patches/features/x86/hyperv/0071-Drivers-hv-Add-new-message-types-to-enhance-KVP.patch new file mode 100644 index 000000000..a70bd60f0 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0071-Drivers-hv-Add-new-message-types-to-enhance-KVP.patch @@ -0,0 +1,109 @@ +From: "K. Y. Srinivasan" +Date: Sat, 10 Mar 2012 15:32:08 -0800 +Subject: [PATCH 71/77] Drivers: hv: Add new message types to enhance KVP + +commit e485ceac9ebd43901ef0ce13622385d509e072e7 upstream. + +Add additional KVP (Key Value Pair) protocol messages to +enhance KVP functionality for Linux guests on Hyper-V. As part of this, +patch define an explicit version negoitiation message. + +Reviewed-by: Haiyang Zhang +Signed-off-by: K. Y. Srinivasan +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 5 +++-- + include/linux/hyperv.h | 30 +++++++++++++++++++++++++++--- + tools/hv/hv_kvp_daemon.c | 2 +- + 3 files changed, 31 insertions(+), 6 deletions(-) + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index 0ef4c1f..779109b 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -78,7 +78,7 @@ kvp_register(void) + + if (msg) { + kvp_msg = (struct hv_kvp_msg *)msg->data; +- version = kvp_msg->body.kvp_version; ++ version = kvp_msg->body.kvp_register.version; + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + +@@ -122,7 +122,8 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&kvp_work)) +- kvp_respond_to_host(data->data.key, data->data.value, ++ kvp_respond_to_host(data->data.key, ++ data->data.value, + !strlen(data->data.key)); + } + } +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index e57a6c6..a2d8c54 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -149,7 +149,11 @@ struct hv_kvp_exchg_msg_value { + __u32 key_size; + __u32 value_size; + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +- __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; ++ union { ++ __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; ++ __u32 value_u32; ++ __u64 value_u64; ++ }; + } __attribute__((packed)); + + struct hv_kvp_msg_enumerate { +@@ -157,11 +161,31 @@ struct hv_kvp_msg_enumerate { + struct hv_kvp_exchg_msg_value data; + } __attribute__((packed)); + ++struct hv_kvp_msg_get { ++ struct hv_kvp_exchg_msg_value data; ++}; ++ ++struct hv_kvp_msg_set { ++ struct hv_kvp_exchg_msg_value data; ++}; ++ ++struct hv_kvp_msg_delete { ++ __u32 key_size; ++ __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ++}; ++ ++struct hv_kvp_register { ++ __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ++}; ++ + struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + union { +- struct hv_kvp_msg_enumerate kvp_enum_data; +- char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ++ struct hv_kvp_msg_get kvp_get; ++ struct hv_kvp_msg_set kvp_set; ++ struct hv_kvp_msg_delete kvp_delete; ++ struct hv_kvp_msg_enumerate kvp_enum_data; ++ struct hv_kvp_register kvp_register; + } body; + } __attribute__((packed)); + +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index 4ebf703..00d3f7c 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -378,7 +378,7 @@ int main(void) + * Driver is registering with us; stash away the version + * information. + */ +- p = (char *)hv_msg->body.kvp_version; ++ p = (char *)hv_msg->body.kvp_register.version; + lic_version = malloc(strlen(p) + 1); + if (lic_version) { + strcpy(lic_version, p); +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0072-net-hyperv-fix-erroneous-NETDEV_TX_BUSY-use.patch b/debian/patches/features/x86/hyperv/0072-net-hyperv-fix-erroneous-NETDEV_TX_BUSY-use.patch new file mode 100644 index 000000000..5790225be --- /dev/null +++ b/debian/patches/features/x86/hyperv/0072-net-hyperv-fix-erroneous-NETDEV_TX_BUSY-use.patch @@ -0,0 +1,53 @@ +From: Eric Dumazet +Date: Wed, 14 Mar 2012 08:53:34 +0000 +Subject: [PATCH 72/77] net/hyperv: fix erroneous NETDEV_TX_BUSY use + +commit bb6d5e76fb4fba9aa36726db41404512f3286c0f upstream. + +A driver start_xmit() method cannot free skb and return NETDEV_TX_BUSY, +since caller is going to reuse freed skb. + +This is mostly a revert of commit bf769375c (staging: hv: fix the return +status of netvsc_start_xmit()) + +In fact netif_tx_stop_queue() / netif_stop_queue() is needed before +returning NETDEV_TX_BUSY or you can trigger a ksoftirqd fatal loop. + +In case of memory allocation error, only safe way is to drop the packet +and return NETDEV_TX_OK + +Signed-off-by: Eric Dumazet +Cc: "K. Y. Srinivasan" +Cc: Haiyang Zhang +Cc: Greg Kroah-Hartman +Reviewed-by: Haiyang Zhang +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 0f8e834..2517d20 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -167,7 +167,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + + dev_kfree_skb(skb); + net->stats.tx_dropped++; +- return NETDEV_TX_BUSY; ++ return NETDEV_TX_OK; + } + + packet->vlan_tci = skb->vlan_tci; +@@ -229,7 +229,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + dev_kfree_skb_any(skb); + } + +- return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; ++ return NETDEV_TX_OK; + } + + /* +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0073-Drivers-hv-Support-the-newly-introduced-KVP-messages.patch b/debian/patches/features/x86/hyperv/0073-Drivers-hv-Support-the-newly-introduced-KVP-messages.patch new file mode 100644 index 000000000..8dcdf8b10 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0073-Drivers-hv-Support-the-newly-introduced-KVP-messages.patch @@ -0,0 +1,401 @@ +From: "K. Y. Srinivasan" +Date: Fri, 16 Mar 2012 08:02:25 -0700 +Subject: [PATCH 73/77] Drivers: hv: Support the newly introduced KVP messages + in the driver + +commit fa3d5b85c681518b6e4ec515814dcb2d5b702b89 upstream. + +Support the newly defined KVP message types. It turns out that the host +pushes a set of standard key value pairs as soon as the guest opens the KVP channel. +Since we cannot handle these tuples until the user level daemon loads up, defer +reading the KVP channel until the user level daemon is launched. + +Signed-off-by: K. Y. Srinivasan +Reviewed-by: Haiyang Zhang +Reviewed-by: Dan Carpenter +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 218 +++++++++++++++++++++++++++++++++++----------- + include/linux/hyperv.h | 2 + + tools/hv/hv_kvp_daemon.c | 7 ++ + 3 files changed, 176 insertions(+), 51 deletions(-) + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index 779109b..cfe60b0 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -42,9 +42,10 @@ + static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ +- int index; /* current index */ ++ struct hv_kvp_msg *kvp_msg; /* current message */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ ++ void *kvp_context; /* for the channel callback */ + } kvp_transaction; + + static void kvp_send_key(struct work_struct *dummy); +@@ -110,12 +111,15 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) + struct hv_kvp_msg_enumerate *data; + + message = (struct hv_kvp_msg *)msg->data; +- if (message->kvp_hdr.operation == KVP_OP_REGISTER) { ++ switch (message->kvp_hdr.operation) { ++ case KVP_OP_REGISTER: + pr_info("KVP: user-mode registering done.\n"); + kvp_register(); +- } ++ kvp_transaction.active = false; ++ hv_kvp_onchannelcallback(kvp_transaction.kvp_context); ++ break; + +- if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { ++ default: + data = &message->body.kvp_enum_data; + /* + * Complete the transaction by forwarding the key value +@@ -133,21 +137,104 @@ kvp_send_key(struct work_struct *dummy) + { + struct cn_msg *msg; + struct hv_kvp_msg *message; +- int index = kvp_transaction.index; ++ struct hv_kvp_msg *in_msg; ++ __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation; ++ __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool; ++ __u32 val32; ++ __u64 val64; + + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); ++ if (!msg) ++ return; + +- if (msg) { +- msg->id.idx = CN_KVP_IDX; +- msg->id.val = CN_KVP_VAL; ++ msg->id.idx = CN_KVP_IDX; ++ msg->id.val = CN_KVP_VAL; + +- message = (struct hv_kvp_msg *)msg->data; +- message->kvp_hdr.operation = KVP_OP_ENUMERATE; +- message->body.kvp_enum_data.index = index; +- msg->len = sizeof(struct hv_kvp_msg); +- cn_netlink_send(msg, 0, GFP_ATOMIC); +- kfree(msg); ++ message = (struct hv_kvp_msg *)msg->data; ++ message->kvp_hdr.operation = operation; ++ message->kvp_hdr.pool = pool; ++ in_msg = kvp_transaction.kvp_msg; ++ ++ /* ++ * The key/value strings sent from the host are encoded in ++ * in utf16; convert it to utf8 strings. ++ * The host assures us that the utf16 strings will not exceed ++ * the max lengths specified. We will however, reserve room ++ * for the string terminating character - in the utf16s_utf8s() ++ * function we limit the size of the buffer where the converted ++ * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee ++ * that the strings can be properly terminated! ++ */ ++ ++ switch (message->kvp_hdr.operation) { ++ case KVP_OP_SET: ++ switch (in_msg->body.kvp_set.data.value_type) { ++ case REG_SZ: ++ /* ++ * The value is a string - utf16 encoding. ++ */ ++ message->body.kvp_set.data.value_size = ++ utf16s_to_utf8s( ++ (wchar_t *)in_msg->body.kvp_set.data.value, ++ in_msg->body.kvp_set.data.value_size, ++ UTF16_LITTLE_ENDIAN, ++ message->body.kvp_set.data.value, ++ HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; ++ break; ++ ++ case REG_U32: ++ /* ++ * The value is a 32 bit scalar. ++ * We save this as a utf8 string. ++ */ ++ val32 = in_msg->body.kvp_set.data.value_u32; ++ message->body.kvp_set.data.value_size = ++ sprintf(message->body.kvp_set.data.value, ++ "%d", val32) + 1; ++ break; ++ ++ case REG_U64: ++ /* ++ * The value is a 64 bit scalar. ++ * We save this as a utf8 string. ++ */ ++ val64 = in_msg->body.kvp_set.data.value_u64; ++ message->body.kvp_set.data.value_size = ++ sprintf(message->body.kvp_set.data.value, ++ "%llu", val64) + 1; ++ break; ++ ++ } ++ case KVP_OP_GET: ++ message->body.kvp_set.data.key_size = ++ utf16s_to_utf8s( ++ (wchar_t *)in_msg->body.kvp_set.data.key, ++ in_msg->body.kvp_set.data.key_size, ++ UTF16_LITTLE_ENDIAN, ++ message->body.kvp_set.data.key, ++ HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; ++ break; ++ ++ case KVP_OP_DELETE: ++ message->body.kvp_delete.key_size = ++ utf16s_to_utf8s( ++ (wchar_t *)in_msg->body.kvp_delete.key, ++ in_msg->body.kvp_delete.key_size, ++ UTF16_LITTLE_ENDIAN, ++ message->body.kvp_delete.key, ++ HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; ++ break; ++ ++ case KVP_OP_ENUMERATE: ++ message->body.kvp_enum_data.index = ++ in_msg->body.kvp_enum_data.index; ++ break; + } ++ ++ msg->len = sizeof(struct hv_kvp_msg); ++ cn_netlink_send(msg, 0, GFP_ATOMIC); ++ kfree(msg); ++ + return; + } + +@@ -159,10 +246,11 @@ 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; ++ struct hv_kvp_exchg_msg_value *kvp_data; + char *key_name; + struct icmsg_hdr *icmsghdrp; +- int keylen, valuelen; ++ int keylen = 0; ++ int valuelen = 0; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; +@@ -189,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error) + + kvp_transaction.active = false; + ++ icmsghdrp = (struct icmsg_hdr *) ++ &recv_buffer[sizeof(struct vmbuspipe_hdr)]; ++ + if (channel->onchannel_callback == NULL) + /* + * We have raced with util driver being unloaded; +@@ -196,41 +287,66 @@ kvp_respond_to_host(char *key, char *value, int error) + */ + 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->body.kvp_enum_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; ++ * Something failed or the we have timedout; + * terminate the host-side iteration by returning an error. + */ + icmsghdrp->status = HV_E_FAIL; + goto response_done; + } + ++ icmsghdrp->status = HV_S_OK; ++ ++ kvp_msg = (struct hv_kvp_msg *) ++ &recv_buffer[sizeof(struct vmbuspipe_hdr) + ++ sizeof(struct icmsg_hdr)]; ++ ++ switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { ++ case KVP_OP_GET: ++ kvp_data = &kvp_msg->body.kvp_get.data; ++ goto copy_value; ++ ++ case KVP_OP_SET: ++ case KVP_OP_DELETE: ++ goto response_done; ++ ++ default: ++ break; ++ } ++ ++ kvp_data = &kvp_msg->body.kvp_enum_data.data; ++ key_name = key; ++ + /* + * The windows host expects the key/value pair to be encoded +- * in utf16. ++ * in utf16. Ensure that the key/value size reported to the host ++ * will be less than or equal to the MAX size (including the ++ * terminating character). + */ + keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, +- (wchar_t *) kvp_data->data.key, +- HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); +- kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ ++ (wchar_t *) kvp_data->key, ++ (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2); ++ kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */ ++ ++copy_value: + valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, +- (wchar_t *) kvp_data->data.value, +- HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); +- kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ ++ (wchar_t *) kvp_data->value, ++ (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2); ++ kvp_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; ++ /* ++ * If the utf8s to utf16s conversion failed; notify host ++ * of the error. ++ */ ++ if ((keylen < 0) || (valuelen < 0)) ++ icmsghdrp->status = HV_E_FAIL; ++ ++ kvp_data->value_type = REG_SZ; /* all our values are strings */ + + response_done: + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; +@@ -257,11 +373,18 @@ void hv_kvp_onchannelcallback(void *context) + u64 requestid; + + struct hv_kvp_msg *kvp_msg; +- struct hv_kvp_msg_enumerate *kvp_data; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + ++ if (kvp_transaction.active) { ++ /* ++ * We will defer processing this callback once ++ * the current transaction is complete. ++ */ ++ kvp_transaction.kvp_context = context; ++ return; ++ } + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); + +@@ -276,29 +399,16 @@ void hv_kvp_onchannelcallback(void *context) + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + +- kvp_data = &kvp_msg->body.kvp_enum_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; ++ kvp_transaction.kvp_msg = kvp_msg; + + /* + * Get the information from the +@@ -316,8 +426,6 @@ void hv_kvp_onchannelcallback(void *context) + + } + +-callback_done: +- + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + +@@ -338,6 +446,14 @@ hv_kvp_init(struct hv_util_service *srv) + return err; + recv_buffer = srv->recv_buffer; + ++ /* ++ * When this driver loads, the user level daemon that ++ * processes the host requests may not yet be running. ++ * Defer processing channel callbacks until the daemon ++ * has registered. ++ */ ++ kvp_transaction.active = true; ++ + return 0; + } + +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index a2d8c54..e88a979 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -119,6 +119,8 @@ + */ + + #define REG_SZ 1 ++#define REG_U32 4 ++#define REG_U64 8 + + enum hv_kvp_exchg_op { + KVP_OP_GET = 0, +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index 00d3f7c..a98878c 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -389,10 +389,16 @@ int main(void) + } + continue; + ++ case KVP_OP_SET: ++ case KVP_OP_GET: ++ case KVP_OP_DELETE: + default: + break; + } + ++ if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) ++ goto kvp_done; ++ + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->body.kvp_enum_data.data.key; + key_value = (char *)hv_msg->body.kvp_enum_data.data.value; +@@ -454,6 +460,7 @@ int main(void) + * already in the receive buffer. Update the cn_msg header to + * reflect the key value that has been added to the message + */ ++kvp_done: + + incoming_cn_msg->id.idx = CN_KVP_IDX; + incoming_cn_msg->id.val = CN_KVP_VAL; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0074-Tools-hv-Fully-support-the-new-KVP-verbs-in-the-user.patch b/debian/patches/features/x86/hyperv/0074-Tools-hv-Fully-support-the-new-KVP-verbs-in-the-user.patch new file mode 100644 index 000000000..a2ebd0848 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0074-Tools-hv-Fully-support-the-new-KVP-verbs-in-the-user.patch @@ -0,0 +1,339 @@ +From: "K. Y. Srinivasan" +Date: Fri, 16 Mar 2012 08:02:26 -0700 +Subject: [PATCH 74/77] Tools: hv: Fully support the new KVP verbs in the user + level daemon + +commit db425334e5bb7fa65bbbd7bea9d79842f65bcf45 upstream. + +Now fully support the new KVP messages in the user level daemon. Hyper-V defines +multiple persistent pools to which the host can write/read/modify KVP tuples. +In this patch we implement a file for each specified pool, where the KVP tuples +will be stored in the guest. + +Signed-off-by: K. Y. Srinivasan +Reviewed-by: Haiyang Zhang +Reviewed-by: Dan Carpenter +Signed-off-by: Greg Kroah-Hartman +--- + tools/hv/hv_kvp_daemon.c | 281 +++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 280 insertions(+), 1 deletion(-) + +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index a98878c..2fb9c3d 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -39,7 +39,8 @@ + #include + #include + #include +- ++#include ++#include + + /* + * KVP protocol: The user mode component first registers with the +@@ -79,6 +80,250 @@ static char *os_build; + static char *lic_version; + static struct utsname uts_buf; + ++ ++#define MAX_FILE_NAME 100 ++#define ENTRIES_PER_BLOCK 50 ++ ++struct kvp_record { ++ __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ++ __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; ++}; ++ ++struct kvp_file_state { ++ int fd; ++ int num_blocks; ++ struct kvp_record *records; ++ int num_records; ++ __u8 fname[MAX_FILE_NAME]; ++}; ++ ++static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; ++ ++static void kvp_acquire_lock(int pool) ++{ ++ struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; ++ fl.l_pid = getpid(); ++ ++ if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { ++ syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); ++ exit(-1); ++ } ++} ++ ++static void kvp_release_lock(int pool) ++{ ++ struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; ++ fl.l_pid = getpid(); ++ ++ if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { ++ perror("fcntl"); ++ syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); ++ exit(-1); ++ } ++} ++ ++static void kvp_update_file(int pool) ++{ ++ FILE *filep; ++ size_t bytes_written; ++ ++ /* ++ * We are going to write our in-memory registry out to ++ * disk; acquire the lock first. ++ */ ++ kvp_acquire_lock(pool); ++ ++ filep = fopen(kvp_file_info[pool].fname, "w"); ++ if (!filep) { ++ kvp_release_lock(pool); ++ syslog(LOG_ERR, "Failed to open file, pool: %d", pool); ++ exit(-1); ++ } ++ ++ bytes_written = fwrite(kvp_file_info[pool].records, ++ sizeof(struct kvp_record), ++ kvp_file_info[pool].num_records, filep); ++ ++ fflush(filep); ++ kvp_release_lock(pool); ++} ++ ++static int kvp_file_init(void) ++{ ++ int ret, fd; ++ FILE *filep; ++ size_t records_read; ++ __u8 *fname; ++ struct kvp_record *record; ++ struct kvp_record *readp; ++ int num_blocks; ++ int i; ++ int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; ++ ++ if (access("/var/opt/hyperv", F_OK)) { ++ if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { ++ syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); ++ exit(-1); ++ } ++ } ++ ++ for (i = 0; i < KVP_POOL_COUNT; i++) { ++ fname = kvp_file_info[i].fname; ++ records_read = 0; ++ num_blocks = 1; ++ sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); ++ fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); ++ ++ if (fd == -1) ++ return 1; ++ ++ ++ filep = fopen(fname, "r"); ++ if (!filep) ++ return 1; ++ ++ record = malloc(alloc_unit * num_blocks); ++ if (record == NULL) { ++ fclose(filep); ++ return 1; ++ } ++ while (!feof(filep)) { ++ readp = &record[records_read]; ++ records_read += fread(readp, sizeof(struct kvp_record), ++ ENTRIES_PER_BLOCK, ++ filep); ++ ++ if (!feof(filep)) { ++ /* ++ * We have more data to read. ++ */ ++ num_blocks++; ++ record = realloc(record, alloc_unit * ++ num_blocks); ++ if (record == NULL) { ++ fclose(filep); ++ return 1; ++ } ++ continue; ++ } ++ break; ++ } ++ kvp_file_info[i].fd = fd; ++ kvp_file_info[i].num_blocks = num_blocks; ++ kvp_file_info[i].records = record; ++ kvp_file_info[i].num_records = records_read; ++ fclose(filep); ++ ++ } ++ ++ return 0; ++} ++ ++static int kvp_key_delete(int pool, __u8 *key, int key_size) ++{ ++ int i; ++ int j, k; ++ int num_records = kvp_file_info[pool].num_records; ++ struct kvp_record *record = kvp_file_info[pool].records; ++ ++ for (i = 0; i < num_records; i++) { ++ if (memcmp(key, record[i].key, key_size)) ++ continue; ++ /* ++ * Found a match; just move the remaining ++ * entries up. ++ */ ++ if (i == num_records) { ++ kvp_file_info[pool].num_records--; ++ kvp_update_file(pool); ++ return 0; ++ } ++ ++ j = i; ++ k = j + 1; ++ for (; k < num_records; k++) { ++ strcpy(record[j].key, record[k].key); ++ strcpy(record[j].value, record[k].value); ++ j++; ++ } ++ ++ kvp_file_info[pool].num_records--; ++ kvp_update_file(pool); ++ return 0; ++ } ++ return 1; ++} ++ ++static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, ++ int value_size) ++{ ++ int i; ++ int j, k; ++ int num_records = kvp_file_info[pool].num_records; ++ struct kvp_record *record = kvp_file_info[pool].records; ++ int num_blocks = kvp_file_info[pool].num_blocks; ++ ++ if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || ++ (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) ++ return 1; ++ ++ for (i = 0; i < num_records; i++) { ++ if (memcmp(key, record[i].key, key_size)) ++ continue; ++ /* ++ * Found a match; just update the value - ++ * this is the modify case. ++ */ ++ memcpy(record[i].value, value, value_size); ++ kvp_update_file(pool); ++ return 0; ++ } ++ ++ /* ++ * Need to add a new entry; ++ */ ++ if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { ++ /* Need to allocate a larger array for reg entries. */ ++ record = realloc(record, sizeof(struct kvp_record) * ++ ENTRIES_PER_BLOCK * (num_blocks + 1)); ++ ++ if (record == NULL) ++ return 1; ++ kvp_file_info[pool].num_blocks++; ++ ++ } ++ memcpy(record[i].value, value, value_size); ++ memcpy(record[i].key, key, key_size); ++ kvp_file_info[pool].records = record; ++ kvp_file_info[pool].num_records++; ++ kvp_update_file(pool); ++ return 0; ++} ++ ++static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, ++ int value_size) ++{ ++ int i; ++ int num_records = kvp_file_info[pool].num_records; ++ struct kvp_record *record = kvp_file_info[pool].records; ++ ++ if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || ++ (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) ++ return 1; ++ ++ for (i = 0; i < num_records; i++) { ++ if (memcmp(key, record[i].key, key_size)) ++ continue; ++ /* ++ * Found a match; just copy the value out. ++ */ ++ memcpy(value, record[i].value, value_size); ++ return 0; ++ } ++ ++ return 1; ++} ++ + void kvp_get_os_info(void) + { + FILE *file; +@@ -315,6 +560,11 @@ int main(void) + */ + kvp_get_os_info(); + ++ if (kvp_file_init()) { ++ syslog(LOG_ERR, "Failed to initialize the pools"); ++ exit(-1); ++ } ++ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); +@@ -389,9 +639,38 @@ int main(void) + } + continue; + ++ /* ++ * The current protocol with the kernel component uses a ++ * NULL key name to pass an error condition. ++ * For the SET, GET and DELETE operations, ++ * use the existing protocol to pass back error. ++ */ ++ + case KVP_OP_SET: ++ if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, ++ hv_msg->body.kvp_set.data.key, ++ hv_msg->body.kvp_set.data.key_size, ++ hv_msg->body.kvp_set.data.value, ++ hv_msg->body.kvp_set.data.value_size)) ++ strcpy(hv_msg->body.kvp_set.data.key, ""); ++ break; ++ + case KVP_OP_GET: ++ if (kvp_get_value(hv_msg->kvp_hdr.pool, ++ hv_msg->body.kvp_set.data.key, ++ hv_msg->body.kvp_set.data.key_size, ++ hv_msg->body.kvp_set.data.value, ++ hv_msg->body.kvp_set.data.value_size)) ++ strcpy(hv_msg->body.kvp_set.data.key, ""); ++ break; ++ + case KVP_OP_DELETE: ++ if (kvp_key_delete(hv_msg->kvp_hdr.pool, ++ hv_msg->body.kvp_delete.key, ++ hv_msg->body.kvp_delete.key_size)) ++ strcpy(hv_msg->body.kvp_delete.key, ""); ++ break; ++ + default: + break; + } +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0075-Tools-hv-Support-enumeration-from-all-the-pools.patch b/debian/patches/features/x86/hyperv/0075-Tools-hv-Support-enumeration-from-all-the-pools.patch new file mode 100644 index 000000000..3801ff154 --- /dev/null +++ b/debian/patches/features/x86/hyperv/0075-Tools-hv-Support-enumeration-from-all-the-pools.patch @@ -0,0 +1,237 @@ +From: "K. Y. Srinivasan" +Date: Fri, 16 Mar 2012 08:02:27 -0700 +Subject: [PATCH 75/77] Tools: hv: Support enumeration from all the pools + +commit adc80ae60eae24a43a357bf5b30fb496f34aa605 upstream. + +We have only supported enumeration only from the AUTO pool. Now support +enumeration from all the available pools. + +Signed-off-by: K. Y. Srinivasan +Reviewed-by: Haiyang Zhang +Reviewed-by: Dan Carpenter +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hv/hv_kvp.c | 7 +-- + include/linux/hyperv.h | 1 + + tools/hv/hv_kvp_daemon.c | 124 +++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 122 insertions(+), 10 deletions(-) + +diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c +index cfe60b0..6186025 100644 +--- a/drivers/hv/hv_kvp.c ++++ b/drivers/hv/hv_kvp.c +@@ -289,14 +289,15 @@ kvp_respond_to_host(char *key, char *value, int error) + + + /* +- * If the error parameter is set, terminate the host's enumeration. ++ * If the error parameter is set, terminate the host's enumeration ++ * on this pool. + */ + if (error) { + /* + * Something failed or the we have timedout; +- * terminate the host-side iteration by returning an error. ++ * terminate the current host-side iteration. + */ +- icmsghdrp->status = HV_E_FAIL; ++ icmsghdrp->status = HV_S_CONT; + goto response_done; + } + +diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h +index e88a979..5852545 100644 +--- a/include/linux/hyperv.h ++++ b/include/linux/hyperv.h +@@ -952,6 +952,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); + + #define HV_S_OK 0x00000000 + #define HV_E_FAIL 0x80004005 ++#define HV_S_CONT 0x80070103 + #define HV_ERROR_NOT_SUPPORTED 0x80070032 + #define HV_ERROR_MACHINE_LOCKED 0x800704F7 + +diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c +index 2fb9c3d..146fd61 100644 +--- a/tools/hv/hv_kvp_daemon.c ++++ b/tools/hv/hv_kvp_daemon.c +@@ -148,6 +148,51 @@ static void kvp_update_file(int pool) + kvp_release_lock(pool); + } + ++static void kvp_update_mem_state(int pool) ++{ ++ FILE *filep; ++ size_t records_read = 0; ++ struct kvp_record *record = kvp_file_info[pool].records; ++ struct kvp_record *readp; ++ int num_blocks = kvp_file_info[pool].num_blocks; ++ int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; ++ ++ kvp_acquire_lock(pool); ++ ++ filep = fopen(kvp_file_info[pool].fname, "r"); ++ if (!filep) { ++ kvp_release_lock(pool); ++ syslog(LOG_ERR, "Failed to open file, pool: %d", pool); ++ exit(-1); ++ } ++ while (!feof(filep)) { ++ readp = &record[records_read]; ++ records_read += fread(readp, sizeof(struct kvp_record), ++ ENTRIES_PER_BLOCK * num_blocks, ++ filep); ++ ++ if (!feof(filep)) { ++ /* ++ * We have more data to read. ++ */ ++ num_blocks++; ++ record = realloc(record, alloc_unit * num_blocks); ++ ++ if (record == NULL) { ++ syslog(LOG_ERR, "malloc failed"); ++ exit(-1); ++ } ++ continue; ++ } ++ break; ++ } ++ ++ kvp_file_info[pool].num_blocks = num_blocks; ++ kvp_file_info[pool].records = record; ++ kvp_file_info[pool].num_records = records_read; ++ ++ kvp_release_lock(pool); ++} + static int kvp_file_init(void) + { + int ret, fd; +@@ -223,8 +268,16 @@ static int kvp_key_delete(int pool, __u8 *key, int key_size) + { + int i; + int j, k; +- int num_records = kvp_file_info[pool].num_records; +- struct kvp_record *record = kvp_file_info[pool].records; ++ int num_records; ++ struct kvp_record *record; ++ ++ /* ++ * First update the in-memory state. ++ */ ++ kvp_update_mem_state(pool); ++ ++ num_records = kvp_file_info[pool].num_records; ++ record = kvp_file_info[pool].records; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) +@@ -259,14 +312,23 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, + { + int i; + int j, k; +- int num_records = kvp_file_info[pool].num_records; +- struct kvp_record *record = kvp_file_info[pool].records; +- int num_blocks = kvp_file_info[pool].num_blocks; ++ int num_records; ++ struct kvp_record *record; ++ int num_blocks; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + ++ /* ++ * First update the in-memory state. ++ */ ++ kvp_update_mem_state(pool); ++ ++ num_records = kvp_file_info[pool].num_records; ++ record = kvp_file_info[pool].records; ++ num_blocks = kvp_file_info[pool].num_blocks; ++ + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; +@@ -304,13 +366,21 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, + int value_size) + { + int i; +- int num_records = kvp_file_info[pool].num_records; +- struct kvp_record *record = kvp_file_info[pool].records; ++ int num_records; ++ struct kvp_record *record; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + ++ /* ++ * First update the in-memory state. ++ */ ++ kvp_update_mem_state(pool); ++ ++ num_records = kvp_file_info[pool].num_records; ++ record = kvp_file_info[pool].records; ++ + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; +@@ -324,6 +394,31 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, + return 1; + } + ++static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, ++ __u8 *value, int value_size) ++{ ++ struct kvp_record *record; ++ ++ /* ++ * First update our in-memory database. ++ */ ++ kvp_update_mem_state(pool); ++ record = kvp_file_info[pool].records; ++ ++ if (index >= kvp_file_info[pool].num_records) { ++ /* ++ * This is an invalid index; terminate enumeration; ++ * - a NULL value will do the trick. ++ */ ++ strcpy(value, ""); ++ return; ++ } ++ ++ memcpy(key, record[index].key, key_size); ++ memcpy(value, record[index].value, value_size); ++} ++ ++ + void kvp_get_os_info(void) + { + FILE *file; +@@ -678,6 +773,21 @@ int main(void) + if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) + goto kvp_done; + ++ /* ++ * If the pool is KVP_POOL_AUTO, dynamically generate ++ * both the key and the value; if not read from the ++ * appropriate pool. ++ */ ++ if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { ++ kvp_pool_enumerate(hv_msg->kvp_hdr.pool, ++ hv_msg->body.kvp_enum_data.index, ++ hv_msg->body.kvp_enum_data.data.key, ++ HV_KVP_EXCHANGE_MAX_KEY_SIZE, ++ hv_msg->body.kvp_enum_data.data.value, ++ HV_KVP_EXCHANGE_MAX_VALUE_SIZE); ++ goto kvp_done; ++ } ++ + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->body.kvp_enum_data.data.key; + key_value = (char *)hv_msg->body.kvp_enum_data.data.value; +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0076-net-hyperv-Fix-the-code-handling-tx-busy.patch b/debian/patches/features/x86/hyperv/0076-net-hyperv-Fix-the-code-handling-tx-busy.patch new file mode 100644 index 000000000..a2f9b4a8e --- /dev/null +++ b/debian/patches/features/x86/hyperv/0076-net-hyperv-Fix-the-code-handling-tx-busy.patch @@ -0,0 +1,46 @@ +From: Haiyang Zhang +Date: Mon, 19 Mar 2012 17:27:06 -0400 +Subject: [PATCH 76/77] net/hyperv: Fix the code handling tx busy + +commit da24e906cbfc08171d24a2b7cf66fad565fdd121 upstream. + +Instead of dropping the packet, we keep the skb buffer, and return +NETDEV_TX_BUSY to let upper layer retry send. This will not cause +endless loop, because the host is taking data away from ring buffer, +and we have called the stop_queue before returning NETDEV_TX_BUSY. + +The stop_queue was called in the function netvsc_send() in file +netvsc.c, then it returns to rndis_filter_send(), which returns to +netvsc_start_xmit() in file netvsc_drv.c. So the NETDEV_TX_BUSY is +indeed returned AFTER queue is stopped. + +Signed-off-by: Haiyang Zhang +Reviewed-by: K. Y. Srinivasan +Acked-by: Eric Dumazet +Signed-off-by: David S. Miller +--- + drivers/net/hyperv/netvsc_drv.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c +index 2517d20..dd29478 100644 +--- a/drivers/net/hyperv/netvsc_drv.c ++++ b/drivers/net/hyperv/netvsc_drv.c +@@ -223,13 +223,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) + net->stats.tx_bytes += skb->len; + net->stats.tx_packets++; + } else { +- /* we are shutting down or bus overloaded, just drop packet */ +- net->stats.tx_dropped++; + kfree(packet); +- dev_kfree_skb_any(skb); + } + +- return NETDEV_TX_OK; ++ return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; + } + + /* +-- +1.7.9.5 + diff --git a/debian/patches/features/x86/hyperv/0077-hv-remove-the-second-argument-of-k-un-map_atomic.patch b/debian/patches/features/x86/hyperv/0077-hv-remove-the-second-argument-of-k-un-map_atomic.patch new file mode 100644 index 000000000..79de49e6f --- /dev/null +++ b/debian/patches/features/x86/hyperv/0077-hv-remove-the-second-argument-of-k-un-map_atomic.patch @@ -0,0 +1,144 @@ +From: Cong Wang +Date: Fri, 25 Nov 2011 23:14:24 +0800 +Subject: [PATCH 77/77] hv: remove the second argument of k[un]map_atomic() + +commit 86cbce4de23f26f1aee8df789d30b0f1ba78c967 upstream. + +Acked-by: Greg Kroah-Hartman +Signed-off-by: Cong Wang +[bwh: Adjust filename to apply after move to drivers/scsi; apply changes + from Linux's merge commit 9f3938346a5c1fa504647670edb5fea5756cfb00.] +--- + drivers/scsi/storvsc_drv.c | 52 ++++++++++++++++++++++---------------------- + 1 file changed, 26 insertions(+), 26 deletions(-) + +diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c +index 695ffc3..83a1972 100644 +--- a/drivers/scsi/storvsc_drv.c ++++ b/drivers/scsi/storvsc_drv.c +@@ -481,6 +481,19 @@ cleanup: + return NULL; + } + ++/* Disgusting wrapper functions */ ++static inline unsigned long sg_kmap_atomic(struct scatterlist *sgl, int idx) ++{ ++ void *addr = kmap_atomic(sg_page(sgl + idx)); ++ return (unsigned long)addr; ++} ++ ++static inline void sg_kunmap_atomic(unsigned long addr) ++{ ++ kunmap_atomic((void *)addr); ++} ++ ++ + /* Assume the original sgl has enough room */ + static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + struct scatterlist *bounce_sgl, +@@ -499,15 +512,12 @@ static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + local_irq_save(flags); + + for (i = 0; i < orig_sgl_count; i++) { +- dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; ++ dest_addr = sg_kmap_atomic(orig_sgl,i) + orig_sgl[i].offset; + dest = dest_addr; + destlen = orig_sgl[i].length; + + if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); ++ bounce_addr = sg_kmap_atomic(bounce_sgl,j); + + while (destlen) { + src = bounce_addr + bounce_sgl[j].offset; +@@ -523,7 +533,7 @@ static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + + if (bounce_sgl[j].offset == bounce_sgl[j].length) { + /* full */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ sg_kunmap_atomic(bounce_addr); + j++; + + /* +@@ -537,26 +547,21 @@ static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, + /* + * We are done; cleanup and return. + */ +- kunmap_atomic((void *)(dest_addr - +- orig_sgl[i].offset), +- KM_IRQ0); ++ sg_kunmap_atomic(dest_addr - orig_sgl[i].offset); + local_irq_restore(flags); + return total_copied; + } + + /* if we need to use another bounce buffer */ + if (destlen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); ++ bounce_addr = sg_kmap_atomic(bounce_sgl,j); + } else if (destlen == 0 && i == orig_sgl_count - 1) { + /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ sg_kunmap_atomic(bounce_addr); + } + } + +- kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset), +- KM_IRQ0); ++ sg_kunmap_atomic(dest_addr - orig_sgl[i].offset); + } + + local_irq_restore(flags); +@@ -581,15 +586,12 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, + local_irq_save(flags); + + for (i = 0; i < orig_sgl_count; i++) { +- src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])), +- KM_IRQ0) + orig_sgl[i].offset; ++ src_addr = sg_kmap_atomic(orig_sgl,i) + orig_sgl[i].offset; + src = src_addr; + srclen = orig_sgl[i].length; + + if (bounce_addr == 0) +- bounce_addr = +- (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), +- KM_IRQ0); ++ bounce_addr = sg_kmap_atomic(bounce_sgl,j); + + while (srclen) { + /* assume bounce offset always == 0 */ +@@ -606,22 +608,20 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, + + if (bounce_sgl[j].length == PAGE_SIZE) { + /* full..move to next entry */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ sg_kunmap_atomic(bounce_addr); + j++; + + /* if we need to use another bounce buffer */ + if (srclen || i != orig_sgl_count - 1) +- bounce_addr = +- (unsigned long)kmap_atomic( +- sg_page((&bounce_sgl[j])), KM_IRQ0); ++ bounce_addr = sg_kmap_atomic(bounce_sgl,j); + + } else if (srclen == 0 && i == orig_sgl_count - 1) { + /* unmap the last bounce that is < PAGE_SIZE */ +- kunmap_atomic((void *)bounce_addr, KM_IRQ0); ++ sg_kunmap_atomic(bounce_addr); + } + } + +- kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0); ++ sg_kunmap_atomic(src_addr - orig_sgl[i].offset); + } + + local_irq_restore(flags); +-- +1.7.9.5 + diff --git a/debian/patches/series/base b/debian/patches/series/base index 1e97ba4cc..ef75de46d 100644 --- a/debian/patches/series/base +++ b/debian/patches/series/base @@ -89,11 +89,91 @@ # Temporary, until the Xen regression is fixed + debian/revert-x86-ioapic-Add-register-level-checks-to-detec.patch -# Temporary, until the next ABI bump -+ debian/revert-rtc-Provide-flag-for-rtc-devices-that-don-t-s.patch - + bugfix/all/net-fix-proc-net-dev-regression.patch + bugfix/arm/ARM-orion5x-Fix-GPIO-enable-bits-for-MPP9.patch + bugfix/x86/drm-i915-mask-transcoder-select-bits-before-setting-.patch + bugfix/all/TOMOYO-Fix-mount-flags-checking-order.patch + bugfix/all/drm-radeon-kms-fix-fans-after-resume.patch + +# Update all Hyper-V drivers to 3.4-rc1 (no longer staging) ++ features/x86/hyperv/0001-NLS-improve-UTF8-UTF16-string-conversion-routine.patch ++ features/x86/hyperv/0002-HID-Move-the-hid-hyperv-driver-out-of-staging.patch ++ features/x86/hyperv/0003-Staging-hv-storvsc-Use-mempools-to-allocate-struct-s.patch ++ features/x86/hyperv/0004-Staging-hv-storvsc-Cleanup-error-handling-in-the-pro.patch ++ features/x86/hyperv/0005-Staging-hv-storvsc-Fixup-the-error-when-processing-S.patch ++ features/x86/hyperv/0006-Staging-hv-storvsc-Fix-error-handling-storvsc_host_r.patch ++ features/x86/hyperv/0007-Staging-hv-storvsc-Use-the-accessor-function-shost_p.patch ++ features/x86/hyperv/0008-Staging-hv-storvsc-Use-the-unlocked-version-queuecom.patch ++ features/x86/hyperv/0009-Staging-hv-storvsc-use-the-macro-KBUILD_MODNAME.patch ++ features/x86/hyperv/0010-Staging-hv-storvsc-Get-rid-of-an-unnecessary-forward.patch ++ features/x86/hyperv/0011-Staging-hv-storvsc-Upgrade-the-vmstor-protocol-versi.patch ++ features/x86/hyperv/0012-Staging-hv-storvsc-Support-hot-add-of-scsi-disks.patch ++ features/x86/hyperv/0013-Staging-hv-storvsc-Support-hot-removing-of-scsi-devi.patch ++ features/x86/hyperv/0014-staging-hv-Use-kmemdup-rather-than-duplicating-its-i.patch ++ features/x86/hyperv/0015-Staging-hv-vmbus-Support-building-the-vmbus-driver-a.patch ++ features/x86/hyperv/0016-hv-Add-Kconfig-menu-entry.patch ++ features/x86/hyperv/0017-Staging-hv-mousevsc-Remove-the-mouse-driver-from-the.patch ++ features/x86/hyperv/0018-staging-hv-move-hv_netvsc-out-of-staging-area.patch ++ features/x86/hyperv/0019-net-hyperv-Fix-long-lines-in-netvsc.c.patch ++ features/x86/hyperv/0020-net-hyperv-Add-support-for-promiscuous-mode-setting.patch ++ features/x86/hyperv/0021-Staging-hv-storvsc-Disable-clustering.patch ++ features/x86/hyperv/0022-Staging-hv-storvsc-Cleanup-storvsc_device_alloc.patch ++ features/x86/hyperv/0023-Staging-hv-storvsc-Fix-a-bug-in-storvsc_command_comp.patch ++ features/x86/hyperv/0024-Staging-hv-storvsc-Fix-a-bug-in-copy_from_bounce_buf.patch ++ features/x86/hyperv/0025-Staging-hv-storvsc-Implement-per-device-memory-pools.patch ++ features/x86/hyperv/0026-Staging-hv-update-TODO-file.patch ++ features/x86/hyperv/0027-HID-hv_mouse-Properly-add-the-hid-device.patch ++ features/x86/hyperv/0028-Staging-hv-storvsc-Fix-a-bug-in-create_bounce_buffer.patch ++ features/x86/hyperv/0029-net-hyperv-Fix-the-stop-wake-queue-mechanism.patch ++ features/x86/hyperv/0030-Drivers-hv-Fix-a-memory-leak.patch ++ features/x86/hyperv/0031-Drivers-hv-Make-the-vmbus-driver-unloadable.patch ++ features/x86/hyperv/0032-Drivers-hv-Get-rid-of-an-unnecessary-check-in-hv.c.patch ++ features/x86/hyperv/0033-net-hyperv-Remove-unnecessary-kmap_atomic-in-netvsc-.patch ++ features/x86/hyperv/0034-net-hyperv-Add-NETVSP-protocol-version-negotiation.patch ++ features/x86/hyperv/0035-net-hyperv-Add-support-for-jumbo-frame-up-to-64KB.patch ++ features/x86/hyperv/0036-net-hyperv-fix-possible-memory-leak-in-do_set_multic.patch ++ features/x86/hyperv/0037-drivers-hv-Get-rid-of-some-unnecessary-code.patch ++ features/x86/hyperv/0038-net-hyperv-rx_bytes-should-account-the-ether-header-.patch ++ features/x86/hyperv/0039-HID-hyperv-Properly-disconnect-the-input-device.patch ++ features/x86/hyperv/0040-net-hyperv-fix-the-issue-that-large-packets-be-dropp.patch ++ features/x86/hyperv/0041-net-hyperv-Use-netif_tx_disable-instead-of-netif_sto.patch ++ features/x86/hyperv/0042-net-hyperv-Fix-the-page-buffer-when-an-RNDIS-message.patch ++ features/x86/hyperv/0043-drivers-hv-kvp-Add-cleanup-connector-defines.patch ++ features/x86/hyperv/0044-drivers-hv-kvp-Move-the-contents-of-hv_kvp.h-to-hype.patch ++ features/x86/hyperv/0045-net-hyperv-Convert-camel-cased-variables-in-rndis_fi.patch ++ features/x86/hyperv/0046-net-hyperv-Correct-the-assignment-in-netvsc_recv_cal.patch ++ features/x86/hyperv/0047-net-hyperv-Remove-the-unnecessary-memset-in-rndis_fi.patch ++ features/x86/hyperv/0048-Staging-hv-storvsc-Cleanup-some-comments.patch ++ features/x86/hyperv/0049-Staging-hv-storvsc-Cleanup-storvsc_probe.patch ++ features/x86/hyperv/0050-Staging-hv-storvsc-Cleanup-storvsc_queuecommand.patch ++ features/x86/hyperv/0051-Staging-hv-storvsc-Introduce-defines-for-srb-status-.patch ++ features/x86/hyperv/0052-Staging-hv-storvsc-Cleanup-storvsc_host_reset_handle.patch ++ features/x86/hyperv/0053-Staging-hv-storvsc-Move-and-cleanup-storvsc_remove.patch ++ features/x86/hyperv/0054-Staging-hv-storvsc-Add-a-comment-to-explain-life-cyc.patch ++ features/x86/hyperv/0055-Staging-hv-storvsc-Get-rid-of-the-on_io_completion-i.patch ++ features/x86/hyperv/0056-Staging-hv-storvsc-Rename-the-context-field-in-hv_st.patch ++ features/x86/hyperv/0057-Staging-hv-storvsc-Miscellaneous-cleanup-of-storvsc-.patch ++ features/x86/hyperv/0058-Staging-hv-storvsc-Cleanup-the-code-for-generating-p.patch ++ features/x86/hyperv/0059-Staging-hv-storvsc-Cleanup-some-protocol-related-con.patch ++ features/x86/hyperv/0060-Staging-hv-storvsc-Get-rid-of-some-unused-defines.patch ++ features/x86/hyperv/0061-Staging-hv-storvsc-Consolidate-the-request-structure.patch ++ features/x86/hyperv/0062-Staging-hv-storvsc-Consolidate-all-the-wire-protocol.patch ++ features/x86/hyperv/0063-drivers-hv-Cleanup-the-kvp-related-state-in-hyperv.h.patch ++ features/x86/hyperv/0064-tools-hv-Use-hyperv.h-to-get-the-KVP-definitions.patch ++ features/x86/hyperv/0065-drivers-hv-kvp-Cleanup-the-kernel-user-protocol.patch ++ features/x86/hyperv/0066-drivers-hv-Increase-the-number-of-VCPUs-supported-in.patch ++ features/x86/hyperv/0067-Staging-hv-storvsc-Move-the-storage-driver-out-of-th.patch ++ features/x86/hyperv/0068-net-hyperv-Use-the-built-in-macro-KBUILD_MODNAME-for.patch ++ features/x86/hyperv/0069-net-hyperv-Fix-data-corruption-in-rndis_filter_recei.patch ++ features/x86/hyperv/0070-net-hyperv-Add-support-for-vlan-trunking-from-guests.patch ++ features/x86/hyperv/0071-Drivers-hv-Add-new-message-types-to-enhance-KVP.patch ++ features/x86/hyperv/0072-net-hyperv-fix-erroneous-NETDEV_TX_BUSY-use.patch ++ features/x86/hyperv/0073-Drivers-hv-Support-the-newly-introduced-KVP-messages.patch ++ features/x86/hyperv/0074-Tools-hv-Fully-support-the-new-KVP-verbs-in-the-user.patch ++ features/x86/hyperv/0075-Tools-hv-Support-enumeration-from-all-the-pools.patch ++ features/x86/hyperv/0076-net-hyperv-Fix-the-code-handling-tx-busy.patch ++ features/x86/hyperv/0077-hv-remove-the-second-argument-of-k-un-map_atomic.patch + +# Temporary, until the next ABI bump ++ debian/revert-rtc-Provide-flag-for-rtc-devices-that-don-t-s.patch ++ debian/nls-Avoid-ABI-change-from-improvement-to-utf8s_to_ut.patch