/* * 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: * Hank Janssen */ #include #include #include #include #include "osd.h" #include "logging.h" #include "NetVsc.h" #include "RndisFilter.h" /* Globals */ static const char *gDriverName = "netvsc"; /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ static const struct hv_guid gNetVscDeviceType = { .data = { 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E } }; static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo); static int NetVscOnDeviceRemove(struct hv_device *Device); static void NetVscOnCleanup(struct hv_driver *Driver); static void NetVscOnChannelCallback(void *context); static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device); static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device); static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice); static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice); static int NetVscConnectToVsp(struct hv_device *Device); static void NetVscOnSendCompletion(struct hv_device *Device, struct vmpacket_descriptor *Packet); static int NetVscOnSend(struct hv_device *Device, struct hv_netvsc_packet *Packet); static void NetVscOnReceive(struct hv_device *Device, struct vmpacket_descriptor *Packet); static void NetVscOnReceiveCompletion(void *Context); static void NetVscSendReceiveCompletion(struct hv_device *Device, u64 TransactionId); static struct netvsc_device *AllocNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); if (!netDevice) return NULL; /* Set to 2 to allow both inbound and outbound traffic */ atomic_cmpxchg(&netDevice->RefCount, 0, 2); netDevice->Device = Device; Device->Extension = netDevice; return netDevice; } static void FreeNetDevice(struct netvsc_device *Device) { ASSERT(atomic_read(&Device->RefCount) == 0); Device->Device->Extension = NULL; kfree(Device); } /* Get the net device object iff exists and its refcount > 1 */ static struct netvsc_device *GetOutboundNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = Device->Extension; if (netDevice && atomic_read(&netDevice->RefCount) > 1) atomic_inc(&netDevice->RefCount); else netDevice = NULL; return netDevice; } /* Get the net device object iff exists and its refcount > 0 */ static struct netvsc_device *GetInboundNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = Device->Extension; if (netDevice && atomic_read(&netDevice->RefCount)) atomic_inc(&netDevice->RefCount); else netDevice = NULL; return netDevice; } static void PutNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = Device->Extension; ASSERT(netDevice); atomic_dec(&netDevice->RefCount); } static struct netvsc_device *ReleaseOutboundNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = Device->Extension; if (netDevice == NULL) return NULL; /* Busy wait until the ref drop to 2, then set it to 1 */ while (atomic_cmpxchg(&netDevice->RefCount, 2, 1) != 2) udelay(100); return netDevice; } static struct netvsc_device *ReleaseInboundNetDevice(struct hv_device *Device) { struct netvsc_device *netDevice; netDevice = Device->Extension; if (netDevice == NULL) return NULL; /* Busy wait until the ref drop to 1, then set it to 0 */ while (atomic_cmpxchg(&netDevice->RefCount, 1, 0) != 1) udelay(100); Device->Extension = NULL; return netDevice; } /** * NetVscInitialize - Main entry point */ int NetVscInitialize(struct hv_driver *drv) { struct netvsc_driver *driver = (struct netvsc_driver *)drv; DPRINT_ENTER(NETVSC); DPRINT_DBG(NETVSC, "sizeof(struct hv_netvsc_packet)=%zd, " "sizeof(struct nvsp_message)=%zd, " "sizeof(struct vmtransfer_page_packet_header)=%zd", sizeof(struct hv_netvsc_packet), sizeof(struct nvsp_message), sizeof(struct vmtransfer_page_packet_header)); /* Make sure we are at least 2 pages since 1 page is used for control */ ASSERT(driver->RingBufferSize >= (PAGE_SIZE << 1)); drv->name = gDriverName; memcpy(&drv->deviceType, &gNetVscDeviceType, sizeof(struct hv_guid)); /* Make sure it is set by the caller */ ASSERT(driver->OnReceiveCallback); ASSERT(driver->OnLinkStatusChanged); /* Setup the dispatch table */ driver->Base.OnDeviceAdd = NetVscOnDeviceAdd; driver->Base.OnDeviceRemove = NetVscOnDeviceRemove; driver->Base.OnCleanup = NetVscOnCleanup; driver->OnSend = NetVscOnSend; RndisFilterInit(driver); DPRINT_EXIT(NETVSC); return 0; } static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device) { int ret = 0; struct netvsc_device *netDevice; struct nvsp_message *initPacket; DPRINT_ENTER(NETVSC); netDevice = GetOutboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return -1; } ASSERT(netDevice->ReceiveBufferSize > 0); /* page-size grandularity */ ASSERT((netDevice->ReceiveBufferSize & (PAGE_SIZE - 1)) == 0); netDevice->ReceiveBuffer = osd_PageAlloc(netDevice->ReceiveBufferSize >> PAGE_SHIFT); if (!netDevice->ReceiveBuffer) { DPRINT_ERR(NETVSC, "unable to allocate receive buffer of size %d", netDevice->ReceiveBufferSize); ret = -1; goto Cleanup; } /* page-aligned buffer */ ASSERT(((unsigned long)netDevice->ReceiveBuffer & (PAGE_SIZE - 1)) == 0); DPRINT_INFO(NETVSC, "Establishing receive buffer's GPADL..."); /* * 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 = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device, netDevice->ReceiveBuffer, netDevice->ReceiveBufferSize, &netDevice->ReceiveBufferGpadlHandle); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to establish receive buffer's gpadl"); goto Cleanup; } /* osd_WaitEventWait(ext->ChannelInitEvent); */ /* Notify the NetVsp of the gpadl handle */ DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendReceiveBuffer..."); initPacket = &netDevice->ChannelInitPacket; memset(initPacket, 0, sizeof(struct nvsp_message)); initPacket->Header.MessageType = NvspMessage1TypeSendReceiveBuffer; initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->ReceiveBufferGpadlHandle; initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID; /* Send the gpadl notification request */ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, initPacket, sizeof(struct nvsp_message), (unsigned long)initPacket, VmbusPacketTypeDataInBand, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send receive buffer's gpadl to netvsp"); goto Cleanup; } osd_WaitEventWait(netDevice->ChannelInitEvent); /* Check the response */ if (initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status != NvspStatusSuccess) { DPRINT_ERR(NETVSC, "Unable to complete receive buffer " "initialzation with NetVsp - status %d", initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status); ret = -1; goto Cleanup; } /* Parse the response */ ASSERT(netDevice->ReceiveSectionCount == 0); ASSERT(netDevice->ReceiveSections == NULL); netDevice->ReceiveSectionCount = initPacket->Messages.Version1Messages.SendReceiveBufferComplete.NumSections; netDevice->ReceiveSections = kmalloc(netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL); if (netDevice->ReceiveSections == NULL) { ret = -1; goto Cleanup; } memcpy(netDevice->ReceiveSections, initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Sections, netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section)); DPRINT_INFO(NETVSC, "Receive sections info (count %d, offset %d, " "endoffset %d, suballoc size %d, num suballocs %d)", netDevice->ReceiveSectionCount, netDevice->ReceiveSections[0].Offset, netDevice->ReceiveSections[0].EndOffset, netDevice->ReceiveSections[0].SubAllocationSize, netDevice->ReceiveSections[0].NumSubAllocations); /* * For 1st release, there should only be 1 section that represents the * entire receive buffer */ if (netDevice->ReceiveSectionCount != 1 || netDevice->ReceiveSections->Offset != 0) { ret = -1; goto Cleanup; } goto Exit; Cleanup: NetVscDestroyReceiveBuffer(netDevice); Exit: PutNetDevice(Device); DPRINT_EXIT(NETVSC); return ret; } static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device) { int ret = 0; struct netvsc_device *netDevice; struct nvsp_message *initPacket; DPRINT_ENTER(NETVSC); netDevice = GetOutboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return -1; } ASSERT(netDevice->SendBufferSize > 0); /* page-size grandularity */ ASSERT((netDevice->SendBufferSize & (PAGE_SIZE - 1)) == 0); netDevice->SendBuffer = osd_PageAlloc(netDevice->SendBufferSize >> PAGE_SHIFT); if (!netDevice->SendBuffer) { DPRINT_ERR(NETVSC, "unable to allocate send buffer of size %d", netDevice->SendBufferSize); ret = -1; goto Cleanup; } /* page-aligned buffer */ ASSERT(((unsigned long)netDevice->SendBuffer & (PAGE_SIZE - 1)) == 0); DPRINT_INFO(NETVSC, "Establishing send buffer's GPADL..."); /* * 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 = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device, netDevice->SendBuffer, netDevice->SendBufferSize, &netDevice->SendBufferGpadlHandle); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to establish send buffer's gpadl"); goto Cleanup; } /* osd_WaitEventWait(ext->ChannelInitEvent); */ /* Notify the NetVsp of the gpadl handle */ DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendSendBuffer..."); initPacket = &netDevice->ChannelInitPacket; memset(initPacket, 0, sizeof(struct nvsp_message)); initPacket->Header.MessageType = NvspMessage1TypeSendSendBuffer; initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->SendBufferGpadlHandle; initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_SEND_BUFFER_ID; /* Send the gpadl notification request */ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, initPacket, sizeof(struct nvsp_message), (unsigned long)initPacket, VmbusPacketTypeDataInBand, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send receive buffer's gpadl to netvsp"); goto Cleanup; } osd_WaitEventWait(netDevice->ChannelInitEvent); /* Check the response */ if (initPacket->Messages.Version1Messages.SendSendBufferComplete.Status != NvspStatusSuccess) { DPRINT_ERR(NETVSC, "Unable to complete send buffer " "initialzation with NetVsp - status %d", initPacket->Messages.Version1Messages.SendSendBufferComplete.Status); ret = -1; goto Cleanup; } netDevice->SendSectionSize = initPacket->Messages.Version1Messages.SendSendBufferComplete.SectionSize; goto Exit; Cleanup: NetVscDestroySendBuffer(netDevice); Exit: PutNetDevice(Device); DPRINT_EXIT(NETVSC); return ret; } static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice) { struct nvsp_message *revokePacket; int ret = 0; DPRINT_ENTER(NETVSC); /* * 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 (NetDevice->ReceiveSectionCount) { DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeRevokeReceiveBuffer..."); /* Send the revoke receive buffer */ revokePacket = &NetDevice->RevokePacket; memset(revokePacket, 0, sizeof(struct nvsp_message)); revokePacket->Header.MessageType = NvspMessage1TypeRevokeReceiveBuffer; revokePacket->Messages.Version1Messages.RevokeReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID; ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket( NetDevice->Device, revokePacket, sizeof(struct nvsp_message), (unsigned long)revokePacket, VmbusPacketTypeDataInBand, 0); /* * If we failed here, we might as well return and * have a leak rather than continue and a bugchk */ if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send revoke receive " "buffer to netvsp"); DPRINT_EXIT(NETVSC); return -1; } } /* Teardown the gpadl on the vsp end */ if (NetDevice->ReceiveBufferGpadlHandle) { DPRINT_INFO(NETVSC, "Tearing down receive buffer's GPADL..."); ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl( NetDevice->Device, NetDevice->ReceiveBufferGpadlHandle); /* If we failed here, we might as well return and have a leak rather than continue and a bugchk */ if (ret != 0) { DPRINT_ERR(NETVSC, "unable to teardown receive buffer's gpadl"); DPRINT_EXIT(NETVSC); return -1; } NetDevice->ReceiveBufferGpadlHandle = 0; } if (NetDevice->ReceiveBuffer) { DPRINT_INFO(NETVSC, "Freeing up receive buffer..."); /* Free up the receive buffer */ osd_PageFree(NetDevice->ReceiveBuffer, NetDevice->ReceiveBufferSize >> PAGE_SHIFT); NetDevice->ReceiveBuffer = NULL; } if (NetDevice->ReceiveSections) { NetDevice->ReceiveSectionCount = 0; kfree(NetDevice->ReceiveSections); NetDevice->ReceiveSections = NULL; } DPRINT_EXIT(NETVSC); return ret; } static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice) { struct nvsp_message *revokePacket; int ret = 0; DPRINT_ENTER(NETVSC); /* * 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 (NetDevice->SendSectionSize) { DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeRevokeSendBuffer..."); /* Send the revoke send buffer */ revokePacket = &NetDevice->RevokePacket; memset(revokePacket, 0, sizeof(struct nvsp_message)); revokePacket->Header.MessageType = NvspMessage1TypeRevokeSendBuffer; revokePacket->Messages.Version1Messages.RevokeSendBuffer.Id = NETVSC_SEND_BUFFER_ID; ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(NetDevice->Device, revokePacket, sizeof(struct nvsp_message), (unsigned long)revokePacket, VmbusPacketTypeDataInBand, 0); /* * If we failed here, we might as well return and have a leak * rather than continue and a bugchk */ if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send revoke send buffer " "to netvsp"); DPRINT_EXIT(NETVSC); return -1; } } /* Teardown the gpadl on the vsp end */ if (NetDevice->SendBufferGpadlHandle) { DPRINT_INFO(NETVSC, "Tearing down send buffer's GPADL..."); ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(NetDevice->Device, NetDevice->SendBufferGpadlHandle); /* * If we failed here, we might as well return and have a leak * rather than continue and a bugchk */ if (ret != 0) { DPRINT_ERR(NETVSC, "unable to teardown send buffer's " "gpadl"); DPRINT_EXIT(NETVSC); return -1; } NetDevice->SendBufferGpadlHandle = 0; } if (NetDevice->SendBuffer) { DPRINT_INFO(NETVSC, "Freeing up send buffer..."); /* Free up the receive buffer */ osd_PageFree(NetDevice->SendBuffer, NetDevice->SendBufferSize >> PAGE_SHIFT); NetDevice->SendBuffer = NULL; } DPRINT_EXIT(NETVSC); return ret; } static int NetVscConnectToVsp(struct hv_device *Device) { int ret; struct netvsc_device *netDevice; struct nvsp_message *initPacket; int ndisVersion; DPRINT_ENTER(NETVSC); netDevice = GetOutboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return -1; } initPacket = &netDevice->ChannelInitPacket; memset(initPacket, 0, sizeof(struct nvsp_message)); initPacket->Header.MessageType = NvspMessageTypeInit; initPacket->Messages.InitMessages.Init.MinProtocolVersion = NVSP_MIN_PROTOCOL_VERSION; initPacket->Messages.InitMessages.Init.MaxProtocolVersion = NVSP_MAX_PROTOCOL_VERSION; DPRINT_INFO(NETVSC, "Sending NvspMessageTypeInit..."); /* Send the init request */ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, initPacket, sizeof(struct nvsp_message), (unsigned long)initPacket, VmbusPacketTypeDataInBand, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send NvspMessageTypeInit"); goto Cleanup; } osd_WaitEventWait(netDevice->ChannelInitEvent); /* Now, check the response */ /* ASSERT(initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength <= MAX_MULTIPAGE_BUFFER_COUNT); */ DPRINT_INFO(NETVSC, "NvspMessageTypeInit status(%d) max mdl chain (%d)", initPacket->Messages.InitMessages.InitComplete.Status, initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength); if (initPacket->Messages.InitMessages.InitComplete.Status != NvspStatusSuccess) { DPRINT_ERR(NETVSC, "unable to initialize with netvsp (status 0x%x)", initPacket->Messages.InitMessages.InitComplete.Status); ret = -1; goto Cleanup; } if (initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion != NVSP_PROTOCOL_VERSION_1) { DPRINT_ERR(NETVSC, "unable to initialize with netvsp " "(version expected 1 got %d)", initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion); ret = -1; goto Cleanup; } DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendNdisVersion..."); /* Send the ndis version */ memset(initPacket, 0, sizeof(struct nvsp_message)); ndisVersion = 0x00050000; initPacket->Header.MessageType = NvspMessage1TypeSendNdisVersion; initPacket->Messages.Version1Messages.SendNdisVersion.NdisMajorVersion = (ndisVersion & 0xFFFF0000) >> 16; initPacket->Messages.Version1Messages.SendNdisVersion.NdisMinorVersion = ndisVersion & 0xFFFF; /* Send the init request */ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, initPacket, sizeof(struct nvsp_message), (unsigned long)initPacket, VmbusPacketTypeDataInBand, 0); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to send NvspMessage1TypeSendNdisVersion"); ret = -1; goto Cleanup; } /* * BUGBUG - We have to wait for the above msg since the * netvsp uses KMCL which acknowledges packet (completion * packet) since our Vmbus always set the * VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag */ /* osd_WaitEventWait(NetVscChannel->ChannelInitEvent); */ /* Post the big receive buffer to NetVSP */ ret = NetVscInitializeReceiveBufferWithNetVsp(Device); if (ret == 0) ret = NetVscInitializeSendBufferWithNetVsp(Device); Cleanup: PutNetDevice(Device); DPRINT_EXIT(NETVSC); return ret; } static void NetVscDisconnectFromVsp(struct netvsc_device *NetDevice) { DPRINT_ENTER(NETVSC); NetVscDestroyReceiveBuffer(NetDevice); NetVscDestroySendBuffer(NetDevice); DPRINT_EXIT(NETVSC); } /** * NetVscOnDeviceAdd - Callback when the device belonging to this driver is added */ static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) { int ret = 0; int i; struct netvsc_device *netDevice; struct hv_netvsc_packet *packet, *pos; struct netvsc_driver *netDriver = (struct netvsc_driver *)Device->Driver; DPRINT_ENTER(NETVSC); netDevice = AllocNetDevice(Device); if (!netDevice) { ret = -1; goto Cleanup; } DPRINT_DBG(NETVSC, "netvsc channel object allocated - %p", netDevice); /* Initialize the NetVSC channel extension */ netDevice->ReceiveBufferSize = NETVSC_RECEIVE_BUFFER_SIZE; spin_lock_init(&netDevice->receive_packet_list_lock); netDevice->SendBufferSize = NETVSC_SEND_BUFFER_SIZE; INIT_LIST_HEAD(&netDevice->ReceivePacketList); 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) { DPRINT_DBG(NETVSC, "unable to allocate netvsc pkts " "for receive pool (wanted %d got %d)", NETVSC_RECEIVE_PACKETLIST_COUNT, i); break; } list_add_tail(&packet->ListEntry, &netDevice->ReceivePacketList); } netDevice->ChannelInitEvent = osd_WaitEventCreate(); /* Open the channel */ ret = Device->Driver->VmbusChannelInterface.Open(Device, netDriver->RingBufferSize, netDriver->RingBufferSize, NULL, 0, NetVscOnChannelCallback, Device); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to open channel: %d", ret); ret = -1; goto Cleanup; } /* Channel is opened */ DPRINT_INFO(NETVSC, "*** NetVSC channel opened successfully! ***"); /* Connect with the NetVsp */ ret = NetVscConnectToVsp(Device); if (ret != 0) { DPRINT_ERR(NETVSC, "unable to connect to NetVSP - %d", ret); ret = -1; goto Close; } DPRINT_INFO(NETVSC, "*** NetVSC channel handshake result - %d ***", ret); DPRINT_EXIT(NETVSC); return ret; Close: /* Now, we can close the channel safely */ Device->Driver->VmbusChannelInterface.Close(Device); Cleanup: if (netDevice) { kfree(netDevice->ChannelInitEvent); list_for_each_entry_safe(packet, pos, &netDevice->ReceivePacketList, ListEntry) { list_del(&packet->ListEntry); kfree(packet); } ReleaseOutboundNetDevice(Device); ReleaseInboundNetDevice(Device); FreeNetDevice(netDevice); } DPRINT_EXIT(NETVSC); return ret; } /** * NetVscOnDeviceRemove - Callback when the root bus device is removed */ static int NetVscOnDeviceRemove(struct hv_device *Device) { struct netvsc_device *netDevice; struct hv_netvsc_packet *netvscPacket, *pos; DPRINT_ENTER(NETVSC); DPRINT_INFO(NETVSC, "Disabling outbound traffic on net device (%p)...", Device->Extension); /* Stop outbound traffic ie sends and receives completions */ netDevice = ReleaseOutboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "No net device present!!"); return -1; } /* Wait for all send completions */ while (atomic_read(&netDevice->NumOutstandingSends)) { DPRINT_INFO(NETVSC, "waiting for %d requests to complete...", atomic_read(&netDevice->NumOutstandingSends)); udelay(100); } DPRINT_INFO(NETVSC, "Disconnecting from netvsp..."); NetVscDisconnectFromVsp(netDevice); DPRINT_INFO(NETVSC, "Disabling inbound traffic on net device (%p)...", Device->Extension); /* Stop inbound traffic ie receives and sends completions */ netDevice = ReleaseInboundNetDevice(Device); /* At this point, no one should be accessing netDevice except in here */ DPRINT_INFO(NETVSC, "net device (%p) safe to remove", netDevice); /* Now, we can close the channel safely */ Device->Driver->VmbusChannelInterface.Close(Device); /* Release all resources */ list_for_each_entry_safe(netvscPacket, pos, &netDevice->ReceivePacketList, ListEntry) { list_del(&netvscPacket->ListEntry); kfree(netvscPacket); } kfree(netDevice->ChannelInitEvent); FreeNetDevice(netDevice); DPRINT_EXIT(NETVSC); return 0; } /** * NetVscOnCleanup - Perform any cleanup when the driver is removed */ static void NetVscOnCleanup(struct hv_driver *drv) { DPRINT_ENTER(NETVSC); DPRINT_EXIT(NETVSC); } static void NetVscOnSendCompletion(struct hv_device *Device, struct vmpacket_descriptor *Packet) { struct netvsc_device *netDevice; struct nvsp_message *nvspPacket; struct hv_netvsc_packet *nvscPacket; DPRINT_ENTER(NETVSC); netDevice = GetInboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return; } nvspPacket = (struct nvsp_message *)((unsigned long)Packet + (Packet->DataOffset8 << 3)); DPRINT_DBG(NETVSC, "send completion packet - type %d", nvspPacket->Header.MessageType); if ((nvspPacket->Header.MessageType == NvspMessageTypeInitComplete) || (nvspPacket->Header.MessageType == NvspMessage1TypeSendReceiveBufferComplete) || (nvspPacket->Header.MessageType == NvspMessage1TypeSendSendBufferComplete)) { /* Copy the response back */ memcpy(&netDevice->ChannelInitPacket, nvspPacket, sizeof(struct nvsp_message)); osd_WaitEventSet(netDevice->ChannelInitEvent); } else if (nvspPacket->Header.MessageType == NvspMessage1TypeSendRNDISPacketComplete) { /* Get the send context */ nvscPacket = (struct hv_netvsc_packet *)(unsigned long)Packet->TransactionId; ASSERT(nvscPacket); /* Notify the layer above us */ nvscPacket->Completion.Send.OnSendCompletion(nvscPacket->Completion.Send.SendCompletionContext); atomic_dec(&netDevice->NumOutstandingSends); } else { DPRINT_ERR(NETVSC, "Unknown send completion packet type - " "%d received!!", nvspPacket->Header.MessageType); } PutNetDevice(Device); DPRINT_EXIT(NETVSC); } static int NetVscOnSend(struct hv_device *Device, struct hv_netvsc_packet *Packet) { struct netvsc_device *netDevice; int ret = 0; struct nvsp_message sendMessage; DPRINT_ENTER(NETVSC); netDevice = GetOutboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "net device (%p) shutting down..." "ignoring outbound packets", netDevice); DPRINT_EXIT(NETVSC); return -2; } sendMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacket; if (Packet->IsDataPacket) { /* 0 is RMC_DATA; */ sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 0; } else { /* 1 is RMC_CONTROL; */ sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 1; } /* Not using send buffer section */ sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionIndex = 0xFFFFFFFF; sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionSize = 0; if (Packet->PageBufferCount) { ret = Device->Driver->VmbusChannelInterface.SendPacketPageBuffer( Device, Packet->PageBuffers, Packet->PageBufferCount, &sendMessage, sizeof(struct nvsp_message), (unsigned long)Packet); } else { ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, &sendMessage, sizeof(struct nvsp_message), (unsigned long)Packet, VmbusPacketTypeDataInBand, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); } if (ret != 0) DPRINT_ERR(NETVSC, "Unable to send packet %p ret %d", Packet, ret); atomic_inc(&netDevice->NumOutstandingSends); PutNetDevice(Device); DPRINT_EXIT(NETVSC); return ret; } static void NetVscOnReceive(struct hv_device *Device, struct vmpacket_descriptor *Packet) { struct netvsc_device *netDevice; struct vmtransfer_page_packet_header *vmxferpagePacket; struct nvsp_message *nvspPacket; struct hv_netvsc_packet *netvscPacket = NULL; unsigned long start; unsigned long end, endVirtual; /* struct netvsc_driver *netvscDriver; */ struct xferpage_packet *xferpagePacket = NULL; int i, j; int count = 0, bytesRemain = 0; unsigned long flags; LIST_HEAD(listHead); DPRINT_ENTER(NETVSC); netDevice = GetInboundNetDevice(Device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return; } /* * All inbound packets other than send completion should be xfer page * packet */ if (Packet->Type != VmbusPacketTypeDataUsingTransferPages) { DPRINT_ERR(NETVSC, "Unknown packet type received - %d", Packet->Type); PutNetDevice(Device); return; } nvspPacket = (struct nvsp_message *)((unsigned long)Packet + (Packet->DataOffset8 << 3)); /* Make sure this is a valid nvsp packet */ if (nvspPacket->Header.MessageType != NvspMessage1TypeSendRNDISPacket) { DPRINT_ERR(NETVSC, "Unknown nvsp packet type received - %d", nvspPacket->Header.MessageType); PutNetDevice(Device); return; } DPRINT_DBG(NETVSC, "NVSP packet received - type %d", nvspPacket->Header.MessageType); vmxferpagePacket = (struct vmtransfer_page_packet_header *)Packet; if (vmxferpagePacket->TransferPageSetId != NETVSC_RECEIVE_BUFFER_ID) { DPRINT_ERR(NETVSC, "Invalid xfer page set id - " "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, vmxferpagePacket->TransferPageSetId); PutNetDevice(Device); return; } DPRINT_DBG(NETVSC, "xfer page - range count %d", vmxferpagePacket->RangeCount); /* * 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(&netDevice->receive_packet_list_lock, flags); while (!list_empty(&netDevice->ReceivePacketList)) { list_move_tail(netDevice->ReceivePacketList.next, &listHead); if (++count == vmxferpagePacket->RangeCount + 1) break; } spin_unlock_irqrestore(&netDevice->receive_packet_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) { DPRINT_ERR(NETVSC, "Got only %d netvsc pkt...needed %d pkts. " "Dropping this xfer page packet completely!", count, vmxferpagePacket->RangeCount + 1); /* Return it to the freelist */ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); for (i = count; i != 0; i--) { list_move_tail(listHead.next, &netDevice->ReceivePacketList); } spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, flags); NetVscSendReceiveCompletion(Device, vmxferpagePacket->d.TransactionId); PutNetDevice(Device); return; } /* Remove the 1st packet to represent the xfer page packet itself */ xferpagePacket = (struct xferpage_packet*)listHead.next; list_del(&xferpagePacket->ListEntry); /* This is how much we can satisfy */ xferpagePacket->Count = count - 1; ASSERT(xferpagePacket->Count > 0 && xferpagePacket->Count <= vmxferpagePacket->RangeCount); if (xferpagePacket->Count != vmxferpagePacket->RangeCount) { DPRINT_INFO(NETVSC, "Needed %d netvsc pkts to satisy this xfer " "page...got %d", vmxferpagePacket->RangeCount, xferpagePacket->Count); } /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < (count - 1); i++) { netvscPacket = (struct hv_netvsc_packet*)listHead.next; list_del(&netvscPacket->ListEntry); /* Initialize the netvsc packet */ netvscPacket->XferPagePacket = xferpagePacket; netvscPacket->Completion.Recv.OnReceiveCompletion = NetVscOnReceiveCompletion; netvscPacket->Completion.Recv.ReceiveCompletionContext = netvscPacket; netvscPacket->Device = Device; /* Save this so that we can send it back */ netvscPacket->Completion.Recv.ReceiveCompletionTid = vmxferpagePacket->d.TransactionId; netvscPacket->TotalDataBufferLength = vmxferpagePacket->Ranges[i].ByteCount; netvscPacket->PageBufferCount = 1; ASSERT(vmxferpagePacket->Ranges[i].ByteOffset + vmxferpagePacket->Ranges[i].ByteCount < netDevice->ReceiveBufferSize); netvscPacket->PageBuffers[0].Length = vmxferpagePacket->Ranges[i].ByteCount; start = virt_to_phys((void *)((unsigned long)netDevice->ReceiveBuffer + vmxferpagePacket->Ranges[i].ByteOffset)); netvscPacket->PageBuffers[0].Pfn = start >> PAGE_SHIFT; endVirtual = (unsigned long)netDevice->ReceiveBuffer + vmxferpagePacket->Ranges[i].ByteOffset + vmxferpagePacket->Ranges[i].ByteCount - 1; end = virt_to_phys((void *)endVirtual); /* Calculate the page relative offset */ netvscPacket->PageBuffers[0].Offset = vmxferpagePacket->Ranges[i].ByteOffset & (PAGE_SIZE - 1); if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) { /* Handle frame across multiple pages: */ netvscPacket->PageBuffers[0].Length = (netvscPacket->PageBuffers[0].Pfn << PAGE_SHIFT) + PAGE_SIZE - start; bytesRemain = netvscPacket->TotalDataBufferLength - netvscPacket->PageBuffers[0].Length; for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) { netvscPacket->PageBuffers[j].Offset = 0; if (bytesRemain <= PAGE_SIZE) { netvscPacket->PageBuffers[j].Length = bytesRemain; bytesRemain = 0; } else { netvscPacket->PageBuffers[j].Length = PAGE_SIZE; bytesRemain -= PAGE_SIZE; } netvscPacket->PageBuffers[j].Pfn = virt_to_phys((void *)(endVirtual - bytesRemain)) >> PAGE_SHIFT; netvscPacket->PageBufferCount++; if (bytesRemain == 0) break; } ASSERT(bytesRemain == 0); } DPRINT_DBG(NETVSC, "[%d] - (abs offset %u len %u) => " "(pfn %llx, offset %u, len %u)", i, vmxferpagePacket->Ranges[i].ByteOffset, vmxferpagePacket->Ranges[i].ByteCount, netvscPacket->PageBuffers[0].Pfn, netvscPacket->PageBuffers[0].Offset, netvscPacket->PageBuffers[0].Length); /* Pass it to the upper layer */ ((struct netvsc_driver *)Device->Driver)->OnReceiveCallback(Device, netvscPacket); NetVscOnReceiveCompletion(netvscPacket->Completion.Recv.ReceiveCompletionContext); } ASSERT(list_empty(&listHead)); PutNetDevice(Device); DPRINT_EXIT(NETVSC); } static void NetVscSendReceiveCompletion(struct hv_device *Device, u64 TransactionId) { struct nvsp_message recvcompMessage; int retries = 0; int ret; DPRINT_DBG(NETVSC, "Sending receive completion pkt - %llx", TransactionId); recvcompMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacketComplete; /* FIXME: Pass in the status */ recvcompMessage.Messages.Version1Messages.SendRNDISPacketComplete.Status = NvspStatusSuccess; retry_send_cmplt: /* Send the completion */ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device, &recvcompMessage, sizeof(struct nvsp_message), TransactionId, VmbusPacketTypeCompletion, 0); if (ret == 0) { /* success */ /* no-op */ } else if (ret == -1) { /* no more room...wait a bit and attempt to retry 3 times */ retries++; DPRINT_ERR(NETVSC, "unable to send receive completion pkt " "(tid %llx)...retrying %d", TransactionId, retries); if (retries < 4) { udelay(100); goto retry_send_cmplt; } else { DPRINT_ERR(NETVSC, "unable to send receive completion " "pkt (tid %llx)...give up retrying", TransactionId); } } else { DPRINT_ERR(NETVSC, "unable to send receive completion pkt - " "%llx", TransactionId); } } /* Send a receive completion packet to RNDIS device (ie NetVsp) */ static void NetVscOnReceiveCompletion(void *Context) { struct hv_netvsc_packet *packet = Context; struct hv_device *device = (struct hv_device *)packet->Device; struct netvsc_device *netDevice; u64 transactionId = 0; bool fSendReceiveComp = false; unsigned long flags; DPRINT_ENTER(NETVSC); ASSERT(packet->XferPagePacket); /* * 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. */ netDevice = GetInboundNetDevice(device); if (!netDevice) { DPRINT_ERR(NETVSC, "unable to get net device..." "device being destroyed?"); DPRINT_EXIT(NETVSC); return; } /* Overloading use of the lock. */ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); ASSERT(packet->XferPagePacket->Count > 0); packet->XferPagePacket->Count--; /* * Last one in the line that represent 1 xfer page packet. * Return the xfer page packet itself to the freelist */ if (packet->XferPagePacket->Count == 0) { fSendReceiveComp = true; transactionId = packet->Completion.Recv.ReceiveCompletionTid; list_add_tail(&packet->XferPagePacket->ListEntry, &netDevice->ReceivePacketList); } /* Put the packet back */ list_add_tail(&packet->ListEntry, &netDevice->ReceivePacketList); spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, flags); /* Send a receive completion for the xfer page packet */ if (fSendReceiveComp) NetVscSendReceiveCompletion(device, transactionId); PutNetDevice(device); DPRINT_EXIT(NETVSC); } void NetVscOnChannelCallback(void *Context) { const int netPacketSize = 2048; int ret; struct hv_device *device = Context; struct netvsc_device *netDevice; u32 bytesRecvd; u64 requestId; unsigned char packet[netPacketSize]; struct vmpacket_descriptor *desc; unsigned char *buffer = packet; int bufferlen = netPacketSize; DPRINT_ENTER(NETVSC); ASSERT(device); netDevice = GetInboundNetDevice(device); if (!netDevice) { DPRINT_ERR(NETVSC, "net device (%p) shutting down..." "ignoring inbound packets", netDevice); DPRINT_EXIT(NETVSC); return; } do { ret = device->Driver->VmbusChannelInterface.RecvPacketRaw( device, buffer, bufferlen, &bytesRecvd, &requestId); if (ret == 0) { if (bytesRecvd > 0) { DPRINT_DBG(NETVSC, "receive %d bytes, tid %llx", bytesRecvd, requestId); desc = (struct vmpacket_descriptor *)buffer; switch (desc->Type) { case VmbusPacketTypeCompletion: NetVscOnSendCompletion(device, desc); break; case VmbusPacketTypeDataUsingTransferPages: NetVscOnReceive(device, desc); break; default: DPRINT_ERR(NETVSC, "unhandled packet type %d, " "tid %llx len %d\n", desc->Type, requestId, bytesRecvd); break; } /* reset */ if (bufferlen > netPacketSize) { kfree(buffer); buffer = packet; bufferlen = netPacketSize; } } else { /* reset */ if (bufferlen > netPacketSize) { kfree(buffer); buffer = packet; bufferlen = netPacketSize; } break; } } else if (ret == -2) { /* Handle large packet */ buffer = kmalloc(bytesRecvd, GFP_ATOMIC); if (buffer == NULL) { /* Try again next time around */ DPRINT_ERR(NETVSC, "unable to allocate buffer of size " "(%d)!!", bytesRecvd); break; } bufferlen = bytesRecvd; } else { ASSERT(0); } } while (1); PutNetDevice(device); DPRINT_EXIT(NETVSC); return; }