Author: whu Date: Wed Jun 24 06:01:29 2015 New Revision: 284746 URL: https://svnweb.freebsd.org/changeset/base/284746
Log: TSO and checksum offloading support for Netvsc driver on Hyper-V. Submitted by: whu Reviewed by: royger Approved by: royger MFC after: 1 week Relnotes: yes Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D2517 Modified: head/sys/dev/hyperv/include/hyperv.h head/sys/dev/hyperv/netvsc/hv_net_vsc.c head/sys/dev/hyperv/netvsc/hv_net_vsc.h head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c head/sys/dev/hyperv/netvsc/hv_rndis.h head/sys/dev/hyperv/netvsc/hv_rndis_filter.c head/sys/dev/hyperv/netvsc/hv_rndis_filter.h Modified: head/sys/dev/hyperv/include/hyperv.h ============================================================================== --- head/sys/dev/hyperv/include/hyperv.h Wed Jun 24 01:48:44 2015 (r284745) +++ head/sys/dev/hyperv/include/hyperv.h Wed Jun 24 06:01:29 2015 (r284746) @@ -107,7 +107,7 @@ typedef uint8_t hv_bool_uint8_t; #define HV_MAX_PIPE_USER_DEFINED_BYTES 116 -#define HV_MAX_PAGE_BUFFER_COUNT 16 +#define HV_MAX_PAGE_BUFFER_COUNT 32 #define HV_MAX_MULTIPAGE_BUFFER_COUNT 32 #define HV_ALIGN_UP(value, align) \ Modified: head/sys/dev/hyperv/netvsc/hv_net_vsc.c ============================================================================== --- head/sys/dev/hyperv/netvsc/hv_net_vsc.c Wed Jun 24 01:48:44 2015 (r284745) +++ head/sys/dev/hyperv/netvsc/hv_net_vsc.c Wed Jun 24 06:01:29 2015 (r284746) @@ -49,6 +49,7 @@ #include "hv_rndis.h" #include "hv_rndis_filter.h" +MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver"); /* * Forward declarations @@ -59,13 +60,10 @@ static int hv_nv_init_rx_buffer_with_ne static int hv_nv_destroy_send_buffer(netvsc_dev *net_dev); static int hv_nv_destroy_rx_buffer(netvsc_dev *net_dev); static int hv_nv_connect_to_vsp(struct hv_device *device); -static void hv_nv_on_send_completion(struct hv_device *device, - hv_vm_packet_descriptor *pkt); -static void hv_nv_on_receive(struct hv_device *device, - hv_vm_packet_descriptor *pkt); -static void hv_nv_send_receive_completion(struct hv_device *device, - uint64_t tid); - +static void hv_nv_on_send_completion(netvsc_dev *net_dev, + struct hv_device *device, hv_vm_packet_descriptor *pkt); +static void hv_nv_on_receive(netvsc_dev *net_dev, + struct hv_device *device, hv_vm_packet_descriptor *pkt); /* * @@ -76,7 +74,7 @@ hv_nv_alloc_net_device(struct hv_device netvsc_dev *net_dev; hn_softc_t *sc = device_get_softc(device->device); - net_dev = malloc(sizeof(netvsc_dev), M_DEVBUF, M_NOWAIT | M_ZERO); + net_dev = malloc(sizeof(netvsc_dev), M_NETVSC, M_NOWAIT | M_ZERO); if (net_dev == NULL) { return (NULL); } @@ -128,6 +126,34 @@ hv_nv_get_inbound_net_device(struct hv_d return (net_dev); } +int +hv_nv_get_next_send_section(netvsc_dev *net_dev) +{ + unsigned long bitsmap_words = net_dev->bitsmap_words; + unsigned long *bitsmap = net_dev->send_section_bitsmap; + unsigned long idx; + int ret = NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX; + int i; + + for (i = 0; i < bitsmap_words; i++) { + idx = ffs(~bitsmap[i]); + if (0 == idx) + continue; + + idx--; + if (i * BITS_PER_LONG + idx >= net_dev->send_section_count) + return (ret); + + if (synch_test_and_set_bit(idx, &bitsmap[i])) + continue; + + ret = i * BITS_PER_LONG + idx; + break; + } + + return (ret); +} + /* * Net VSC initialize receive buffer with net VSP * @@ -146,12 +172,8 @@ hv_nv_init_rx_buffer_with_net_vsp(struct return (ENODEV); } - net_dev->rx_buf = contigmalloc(net_dev->rx_buf_size, M_DEVBUF, + net_dev->rx_buf = contigmalloc(net_dev->rx_buf_size, M_NETVSC, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); - if (net_dev->rx_buf == NULL) { - ret = ENOMEM; - goto cleanup; - } /* * Establish the GPADL handle for this buffer on this channel. @@ -202,7 +224,7 @@ hv_nv_init_rx_buffer_with_net_vsp(struct init_pkt->msgs.vers_1_msgs.send_rx_buf_complete.num_sections; net_dev->rx_sections = malloc(net_dev->rx_section_count * - sizeof(nvsp_1_rx_buf_section), M_DEVBUF, M_NOWAIT); + sizeof(nvsp_1_rx_buf_section), M_NETVSC, M_NOWAIT); if (net_dev->rx_sections == NULL) { ret = EINVAL; goto cleanup; @@ -246,7 +268,7 @@ hv_nv_init_send_buffer_with_net_vsp(stru return (ENODEV); } - net_dev->send_buf = contigmalloc(net_dev->send_buf_size, M_DEVBUF, + net_dev->send_buf = contigmalloc(net_dev->send_buf_size, M_NETVSC, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); if (net_dev->send_buf == NULL) { ret = ENOMEM; @@ -259,7 +281,7 @@ hv_nv_init_send_buffer_with_net_vsp(stru * channel to establish the gpadl handle. */ ret = hv_vmbus_channel_establish_gpadl(device->channel, - net_dev->send_buf, net_dev->send_buf_size, + net_dev->send_buf, net_dev->send_buf_size, &net_dev->send_buf_gpadl_handle); if (ret != 0) { goto cleanup; @@ -280,7 +302,7 @@ hv_nv_init_send_buffer_with_net_vsp(stru /* Send the gpadl notification request */ ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, - sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt, + sizeof(nvsp_msg), (uint64_t)init_pkt, HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) { @@ -298,6 +320,17 @@ hv_nv_init_send_buffer_with_net_vsp(stru net_dev->send_section_size = init_pkt->msgs.vers_1_msgs.send_send_buf_complete.section_size; + net_dev->send_section_count = + net_dev->send_buf_size / net_dev->send_section_size; + net_dev->bitsmap_words = howmany(net_dev->send_section_count, + BITS_PER_LONG); + net_dev->send_section_bitsmap = + malloc(net_dev->bitsmap_words * sizeof(long), M_NETVSC, + M_NOWAIT | M_ZERO); + if (NULL == net_dev->send_section_bitsmap) { + ret = ENOMEM; + goto cleanup; + } goto exit; @@ -362,12 +395,12 @@ hv_nv_destroy_rx_buffer(netvsc_dev *net_ if (net_dev->rx_buf) { /* Free up the receive buffer */ - contigfree(net_dev->rx_buf, net_dev->rx_buf_size, M_DEVBUF); + contigfree(net_dev->rx_buf, net_dev->rx_buf_size, M_NETVSC); net_dev->rx_buf = NULL; } if (net_dev->rx_sections) { - free(net_dev->rx_sections, M_DEVBUF); + free(net_dev->rx_sections, M_NETVSC); net_dev->rx_sections = NULL; net_dev->rx_section_count = 0; } @@ -430,10 +463,14 @@ hv_nv_destroy_send_buffer(netvsc_dev *ne if (net_dev->send_buf) { /* Free up the receive buffer */ - contigfree(net_dev->send_buf, net_dev->send_buf_size, M_DEVBUF); + contigfree(net_dev->send_buf, net_dev->send_buf_size, M_NETVSC); net_dev->send_buf = NULL; } + if (net_dev->send_section_bitsmap) { + free(net_dev->send_section_bitsmap, M_NETVSC); + } + return (ret); } @@ -447,7 +484,7 @@ hv_nv_destroy_send_buffer(netvsc_dev *ne */ static int hv_nv_negotiate_nvsp_protocol(struct hv_device *device, netvsc_dev *net_dev, - uint32_t nvsp_ver) + uint32_t nvsp_ver) { nvsp_msg *init_pkt; int ret; @@ -524,8 +561,13 @@ hv_nv_connect_to_vsp(struct hv_device *d { netvsc_dev *net_dev; nvsp_msg *init_pkt; - uint32_t nvsp_vers; uint32_t ndis_version; + uint32_t protocol_list[] = { NVSP_PROTOCOL_VERSION_1, + NVSP_PROTOCOL_VERSION_2, + NVSP_PROTOCOL_VERSION_4, + NVSP_PROTOCOL_VERSION_5 }; + int i; + int protocol_number = nitems(protocol_list); int ret = 0; device_t dev = device->device; hn_softc_t *sc = device_get_softc(dev); @@ -537,26 +579,31 @@ hv_nv_connect_to_vsp(struct hv_device *d } /* - * Negotiate the NVSP version. Try NVSP v2 first. + * Negotiate the NVSP version. Try the latest NVSP first. */ - nvsp_vers = NVSP_PROTOCOL_VERSION_2; - ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); - if (ret != 0) { - /* NVSP v2 failed, try NVSP v1 */ - nvsp_vers = NVSP_PROTOCOL_VERSION_1; - ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); - if (ret != 0) { - /* NVSP v1 failed, return bad status */ - return (ret); + for (i = protocol_number - 1; i >= 0; i--) { + if (hv_nv_negotiate_nvsp_protocol(device, net_dev, + protocol_list[i]) == 0) { + net_dev->nvsp_version = protocol_list[i]; + if (bootverbose) + device_printf(dev, "Netvsc: got version 0x%x\n", + net_dev->nvsp_version); + break; } } - net_dev->nvsp_version = nvsp_vers; + + if (i < 0) { + if (bootverbose) + device_printf(dev, "failed to negotiate a valid " + "protocol.\n"); + return (EPROTO); + } /* * Set the MTU if supported by this NVSP protocol version * This needs to be right after the NVSP init message per Haiyang */ - if (nvsp_vers >= NVSP_PROTOCOL_VERSION_2) + if (net_dev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) ret = hv_nv_send_ndis_config(device, ifp->if_mtu); /* @@ -566,10 +613,11 @@ hv_nv_connect_to_vsp(struct hv_device *d memset(init_pkt, 0, sizeof(nvsp_msg)); - /* - * Updated to version 5.1, minimum, for VLAN per Haiyang - */ - ndis_version = NDIS_VERSION; + if (net_dev->nvsp_version <= NVSP_PROTOCOL_VERSION_4) { + ndis_version = NDIS_VERSION_6_1; + } else { + ndis_version = NDIS_VERSION_6_30; + } init_pkt->hdr.msg_type = nvsp_msg_1_type_send_ndis_vers; init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_major_vers = @@ -621,9 +669,7 @@ netvsc_dev * hv_nv_on_device_add(struct hv_device *device, void *additional_info) { netvsc_dev *net_dev; - netvsc_packet *packet; - netvsc_packet *next_packet; - int i, ret = 0; + int ret = 0; net_dev = hv_nv_alloc_net_device(device); if (!net_dev) @@ -631,29 +677,9 @@ hv_nv_on_device_add(struct hv_device *de /* Initialize the NetVSC channel extension */ net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; - mtx_init(&net_dev->rx_pkt_list_lock, "HV-RPL", NULL, - MTX_SPIN | MTX_RECURSE); net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE; - /* Same effect as STAILQ_HEAD_INITIALIZER() static initializer */ - STAILQ_INIT(&net_dev->myrx_packet_list); - - /* - * malloc a sufficient number of netvsc_packet buffers to hold - * a packet list. Add them to the netvsc device packet queue. - */ - for (i=0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { - packet = malloc(sizeof(netvsc_packet) + - (NETVSC_RECEIVE_SG_COUNT * sizeof(hv_vmbus_page_buffer)), - M_DEVBUF, M_NOWAIT | M_ZERO); - if (!packet) { - break; - } - STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, packet, - mylist_entry); - } - sema_init(&net_dev->channel_init_sema, 0, "netdev_sema"); /* @@ -686,19 +712,7 @@ cleanup: */ if (net_dev) { sema_destroy(&net_dev->channel_init_sema); - - packet = STAILQ_FIRST(&net_dev->myrx_packet_list); - while (packet != NULL) { - next_packet = STAILQ_NEXT(packet, mylist_entry); - free(packet, M_DEVBUF); - packet = next_packet; - } - /* Reset the list to initial state */ - STAILQ_INIT(&net_dev->myrx_packet_list); - - mtx_destroy(&net_dev->rx_pkt_list_lock); - - free(net_dev, M_DEVBUF); + free(net_dev, M_NETVSC); } return (NULL); @@ -710,8 +724,6 @@ cleanup: int hv_nv_on_device_remove(struct hv_device *device, boolean_t destroy_channel) { - netvsc_packet *net_vsc_pkt; - netvsc_packet *next_net_vsc_pkt; hn_softc_t *sc = device_get_softc(device->device); netvsc_dev *net_dev = sc->net_dev;; @@ -738,20 +750,8 @@ hv_nv_on_device_remove(struct hv_device hv_vmbus_channel_close(device->channel); - /* Release all resources */ - net_vsc_pkt = STAILQ_FIRST(&net_dev->myrx_packet_list); - while (net_vsc_pkt != NULL) { - next_net_vsc_pkt = STAILQ_NEXT(net_vsc_pkt, mylist_entry); - free(net_vsc_pkt, M_DEVBUF); - net_vsc_pkt = next_net_vsc_pkt; - } - - /* Reset the list to initial state */ - STAILQ_INIT(&net_dev->myrx_packet_list); - - mtx_destroy(&net_dev->rx_pkt_list_lock); sema_destroy(&net_dev->channel_init_sema); - free(net_dev, M_DEVBUF); + free(net_dev, M_NETVSC); return (0); } @@ -759,18 +759,13 @@ hv_nv_on_device_remove(struct hv_device /* * Net VSC on send completion */ -static void -hv_nv_on_send_completion(struct hv_device *device, hv_vm_packet_descriptor *pkt) +static void +hv_nv_on_send_completion(netvsc_dev *net_dev, + struct hv_device *device, hv_vm_packet_descriptor *pkt) { - netvsc_dev *net_dev; nvsp_msg *nvsp_msg_pkt; netvsc_packet *net_vsc_pkt; - net_dev = hv_nv_get_inbound_net_device(device); - if (!net_dev) { - return; - } - nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3)); @@ -781,17 +776,25 @@ hv_nv_on_send_completion(struct hv_devic == nvsp_msg_1_type_send_send_buf_complete) { /* Copy the response back */ memcpy(&net_dev->channel_init_packet, nvsp_msg_pkt, - sizeof(nvsp_msg)); + sizeof(nvsp_msg)); sema_post(&net_dev->channel_init_sema); } else if (nvsp_msg_pkt->hdr.msg_type == - nvsp_msg_1_type_send_rndis_pkt_complete) { + nvsp_msg_1_type_send_rndis_pkt_complete) { /* Get the send context */ net_vsc_pkt = (netvsc_packet *)(unsigned long)pkt->transaction_id; + if (NULL != net_vsc_pkt) { + if (net_vsc_pkt->send_buf_section_idx != + NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX) { + synch_change_bit(net_vsc_pkt->send_buf_section_idx, + net_dev->send_section_bitsmap); + } + + /* Notify the layer above us */ + net_vsc_pkt->compl.send.on_send_completion( + net_vsc_pkt->compl.send.send_completion_context); - /* Notify the layer above us */ - net_vsc_pkt->compl.send.on_send_completion( - net_vsc_pkt->compl.send.send_completion_context); + } atomic_subtract_int(&net_dev->num_outstanding_sends, 1); } @@ -822,10 +825,10 @@ hv_nv_on_send(struct hv_device *device, send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 1; } - /* Not using send buffer section */ send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_idx = - 0xFFFFFFFF; - send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size = 0; + pkt->send_buf_section_idx; + send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size = + pkt->send_buf_section_size; if (pkt->page_buf_count) { ret = hv_vmbus_channel_send_packet_pagebuffer(device->channel, @@ -851,157 +854,81 @@ hv_nv_on_send(struct hv_device *device, * In the FreeBSD Hyper-V virtual world, this function deals exclusively * with virtual addresses. */ -static void -hv_nv_on_receive(struct hv_device *device, hv_vm_packet_descriptor *pkt) +static void +hv_nv_on_receive(netvsc_dev *net_dev, struct hv_device *device, + hv_vm_packet_descriptor *pkt) { - netvsc_dev *net_dev; hv_vm_transfer_page_packet_header *vm_xfer_page_pkt; nvsp_msg *nvsp_msg_pkt; - netvsc_packet *net_vsc_pkt = NULL; - unsigned long start; - xfer_page_packet *xfer_page_pkt = NULL; - STAILQ_HEAD(PKT_LIST, netvsc_packet_) mylist_head = - STAILQ_HEAD_INITIALIZER(mylist_head); + netvsc_packet vsc_pkt; + netvsc_packet *net_vsc_pkt = &vsc_pkt; + device_t dev = device->device; int count = 0; int i = 0; - - net_dev = hv_nv_get_inbound_net_device(device); - if (!net_dev) - return; + int status = nvsp_status_success; /* * All inbound packets other than send completion should be * xfer page packet. */ - if (pkt->type != HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES) + if (pkt->type != HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES) { + device_printf(dev, "packet type %d is invalid!\n", pkt->type); return; + } nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3)); /* Make sure this is a valid nvsp packet */ - if (nvsp_msg_pkt->hdr.msg_type != nvsp_msg_1_type_send_rndis_pkt) + if (nvsp_msg_pkt->hdr.msg_type != nvsp_msg_1_type_send_rndis_pkt) { + device_printf(dev, "packet hdr type %d is invalid!\n", + pkt->type); return; + } vm_xfer_page_pkt = (hv_vm_transfer_page_packet_header *)pkt; - if (vm_xfer_page_pkt->transfer_page_set_id - != NETVSC_RECEIVE_BUFFER_ID) { + if (vm_xfer_page_pkt->transfer_page_set_id != + NETVSC_RECEIVE_BUFFER_ID) { + device_printf(dev, "transfer_page_set_id %d is invalid!\n", + vm_xfer_page_pkt->transfer_page_set_id); return; } - STAILQ_INIT(&mylist_head); - - /* - * 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 fulfill. - */ - mtx_lock_spin(&net_dev->rx_pkt_list_lock); - while (!STAILQ_EMPTY(&net_dev->myrx_packet_list)) { - net_vsc_pkt = STAILQ_FIRST(&net_dev->myrx_packet_list); - STAILQ_REMOVE_HEAD(&net_dev->myrx_packet_list, mylist_entry); - - STAILQ_INSERT_TAIL(&mylist_head, net_vsc_pkt, mylist_entry); - - if (++count == vm_xfer_page_pkt->range_count + 1) - break; - } - - mtx_unlock_spin(&net_dev->rx_pkt_list_lock); - - /* - * We need at least 2 netvsc pkts (1 to represent the xfer page - * and at least 1 for the range) i.e. we can handle some of the - * xfer page packet ranges... - */ - if (count < 2) { - /* Return netvsc packet to the freelist */ - mtx_lock_spin(&net_dev->rx_pkt_list_lock); - for (i=count; i != 0; i--) { - net_vsc_pkt = STAILQ_FIRST(&mylist_head); - STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); - - STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, - net_vsc_pkt, mylist_entry); - } - mtx_unlock_spin(&net_dev->rx_pkt_list_lock); - - hv_nv_send_receive_completion(device, - vm_xfer_page_pkt->d.transaction_id); - - return; - } - - /* Take the first packet in the list */ - xfer_page_pkt = (xfer_page_packet *)STAILQ_FIRST(&mylist_head); - STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); - - /* This is how many data packets we can supply */ - xfer_page_pkt->count = count - 1; + count = vm_xfer_page_pkt->range_count; + net_vsc_pkt->device = device; /* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ - for (i=0; i < (count - 1); i++) { - net_vsc_pkt = STAILQ_FIRST(&mylist_head); - STAILQ_REMOVE_HEAD(&mylist_head, mylist_entry); - - /* - * Initialize the netvsc packet - */ - net_vsc_pkt->xfer_page_pkt = xfer_page_pkt; - net_vsc_pkt->compl.rx.rx_completion_context = net_vsc_pkt; - net_vsc_pkt->device = device; - /* Save this so that we can send it back */ - net_vsc_pkt->compl.rx.rx_completion_tid = - vm_xfer_page_pkt->d.transaction_id; - - net_vsc_pkt->tot_data_buf_len = - vm_xfer_page_pkt->ranges[i].byte_count; - net_vsc_pkt->page_buf_count = 1; - - net_vsc_pkt->page_buffers[0].length = - vm_xfer_page_pkt->ranges[i].byte_count; - - /* The virtual address of the packet in the receive buffer */ - start = ((unsigned long)net_dev->rx_buf + + for (i = 0; i < count; i++) { + net_vsc_pkt->status = nvsp_status_success; + net_vsc_pkt->data = (void *)((unsigned long)net_dev->rx_buf + vm_xfer_page_pkt->ranges[i].byte_offset); - start = ((unsigned long)start) & ~(PAGE_SIZE - 1); - - /* Page number of the virtual page containing packet start */ - net_vsc_pkt->page_buffers[0].pfn = start >> PAGE_SHIFT; - - /* Calculate the page relative offset */ - net_vsc_pkt->page_buffers[0].offset = - vm_xfer_page_pkt->ranges[i].byte_offset & (PAGE_SIZE - 1); - - /* - * In this implementation, we are dealing with virtual - * addresses exclusively. Since we aren't using physical - * addresses at all, we don't care if a packet crosses a - * page boundary. For this reason, the original code to - * check for and handle page crossings has been removed. - */ - - /* - * Pass it to the upper layer. The receive completion call - * has been moved into this function. - */ - hv_rf_on_receive(device, net_vsc_pkt); + net_vsc_pkt->tot_data_buf_len = + vm_xfer_page_pkt->ranges[i].byte_count; - /* - * Moved completion call back here so that all received - * messages (not just data messages) will trigger a response - * message back to the host. - */ - hv_nv_on_receive_completion(net_vsc_pkt); + hv_rf_on_receive(net_dev, device, net_vsc_pkt); + if (net_vsc_pkt->status != nvsp_status_success) { + status = nvsp_status_failure; + } } + + /* + * Moved completion call back here so that all received + * messages (not just data messages) will trigger a response + * message back to the host. + */ + hv_nv_on_receive_completion(device, vm_xfer_page_pkt->d.transaction_id, + status); } /* - * Net VSC send receive completion + * Net VSC on receive completion + * + * Send a receive completion packet to RNDIS device (ie NetVsp) */ -static void -hv_nv_send_receive_completion(struct hv_device *device, uint64_t tid) +void +hv_nv_on_receive_completion(struct hv_device *device, uint64_t tid, + uint32_t status) { nvsp_msg rx_comp_msg; int retries = 0; @@ -1011,7 +938,7 @@ hv_nv_send_receive_completion(struct hv_ /* Pass in the status */ rx_comp_msg.msgs.vers_1_msgs.send_rndis_pkt_complete.status = - nvsp_status_success; + status; retry_send_cmplt: /* Send the completion */ @@ -1032,81 +959,26 @@ retry_send_cmplt: } /* - * Net VSC on receive completion - * - * Send a receive completion packet to RNDIS device (ie NetVsp) - */ -void -hv_nv_on_receive_completion(void *context) -{ - netvsc_packet *packet = (netvsc_packet *)context; - struct hv_device *device = (struct hv_device *)packet->device; - netvsc_dev *net_dev; - uint64_t tid = 0; - boolean_t send_rx_completion = FALSE; - - /* - * Even though it seems logical to do a hv_nv_get_outbound_net_device() - * here to send out receive completion, we are using - * hv_nv_get_inbound_net_device() since we may have disabled - * outbound traffic already. - */ - net_dev = hv_nv_get_inbound_net_device(device); - if (net_dev == NULL) - return; - - /* Overloading use of the lock. */ - mtx_lock_spin(&net_dev->rx_pkt_list_lock); - - packet->xfer_page_pkt->count--; - - /* - * Last one in the line that represent 1 xfer page packet. - * Return the xfer page packet itself to the free list. - */ - if (packet->xfer_page_pkt->count == 0) { - send_rx_completion = TRUE; - tid = packet->compl.rx.rx_completion_tid; - STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, - (netvsc_packet *)(packet->xfer_page_pkt), mylist_entry); - } - - /* Put the packet back on the free list */ - STAILQ_INSERT_TAIL(&net_dev->myrx_packet_list, packet, mylist_entry); - mtx_unlock_spin(&net_dev->rx_pkt_list_lock); - - /* Send a receive completion for the xfer page packet */ - if (send_rx_completion) - hv_nv_send_receive_completion(device, tid); -} - -/* * Net VSC on channel callback */ static void hv_nv_on_channel_callback(void *context) { - /* Fixme: Magic number */ - const int net_pkt_size = 2048; struct hv_device *device = (struct hv_device *)context; netvsc_dev *net_dev; + device_t dev = device->device; uint32_t bytes_rxed; uint64_t request_id; - uint8_t *packet; - hv_vm_packet_descriptor *desc; + hv_vm_packet_descriptor *desc; uint8_t *buffer; - int bufferlen = net_pkt_size; - int ret = 0; - - packet = malloc(net_pkt_size * sizeof(uint8_t), M_DEVBUF, M_NOWAIT); - if (!packet) - return; - - buffer = packet; + int bufferlen = NETVSC_PACKET_SIZE; + int ret = 0; net_dev = hv_nv_get_inbound_net_device(device); if (net_dev == NULL) - goto out; + return; + + buffer = net_dev->callback_buf; do { ret = hv_vmbus_channel_recv_packet_raw(device->channel, @@ -1116,12 +988,15 @@ hv_nv_on_channel_callback(void *context) desc = (hv_vm_packet_descriptor *)buffer; switch (desc->type) { case HV_VMBUS_PACKET_TYPE_COMPLETION: - hv_nv_on_send_completion(device, desc); + hv_nv_on_send_completion(net_dev, device, desc); break; case HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES: - hv_nv_on_receive(device, desc); + hv_nv_on_receive(net_dev, device, desc); break; default: + device_printf(dev, + "hv_cb recv unknow type %d " + " packet\n", desc->type); break; } } else { @@ -1129,16 +1004,24 @@ hv_nv_on_channel_callback(void *context) } } else if (ret == ENOBUFS) { /* Handle large packet */ - free(buffer, M_DEVBUF); - buffer = malloc(bytes_rxed, M_DEVBUF, M_NOWAIT); + if (bufferlen > NETVSC_PACKET_SIZE) { + free(buffer, M_NETVSC); + buffer = NULL; + } + + /* alloc new buffer */ + buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT); if (buffer == NULL) { + device_printf(dev, + "hv_cb malloc buffer failed, len=%u\n", + bytes_rxed); + bufferlen = 0; break; } bufferlen = bytes_rxed; } } while (1); -out: - free(buffer, M_DEVBUF); + if (bufferlen > NETVSC_PACKET_SIZE) + free(buffer, M_NETVSC); } - Modified: head/sys/dev/hyperv/netvsc/hv_net_vsc.h ============================================================================== --- head/sys/dev/hyperv/netvsc/hv_net_vsc.h Wed Jun 24 01:48:44 2015 (r284745) +++ head/sys/dev/hyperv/netvsc/hv_net_vsc.h Wed Jun 24 06:01:29 2015 (r284746) @@ -41,20 +41,26 @@ #include <sys/types.h> #include <sys/param.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/sx.h> #include <dev/hyperv/include/hyperv.h> +MALLOC_DECLARE(M_NETVSC); #define NVSP_INVALID_PROTOCOL_VERSION (0xFFFFFFFF) #define NVSP_PROTOCOL_VERSION_1 2 #define NVSP_PROTOCOL_VERSION_2 0x30002 +#define NVSP_PROTOCOL_VERSION_4 0x40000 +#define NVSP_PROTOCOL_VERSION_5 0x50000 #define NVSP_MIN_PROTOCOL_VERSION (NVSP_PROTOCOL_VERSION_1) #define NVSP_MAX_PROTOCOL_VERSION (NVSP_PROTOCOL_VERSION_2) #define NVSP_PROTOCOL_VERSION_CURRENT NVSP_PROTOCOL_VERSION_2 +#define VERSION_4_OFFLOAD_SIZE 22 + #define NVSP_OPERATIONAL_STATUS_OK (0x00000000) #define NVSP_OPERATIONAL_STATUS_DEGRADED (0x00000001) #define NVSP_OPERATIONAL_STATUS_NONRECOVERABLE (0x00000002) @@ -544,7 +550,7 @@ typedef struct nvsp_2_msg_indicate_chimn #define NVSP_1_CHIMNEY_SEND_INVALID_OOB_INDEX 0xffffu -#define NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX 0xffffu +#define NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX 0xffffffff /* * NvspMessage2TypeSendChimneyPacket @@ -842,11 +848,11 @@ typedef struct nvsp_msg_ { * Defines */ -#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */ +#define NETVSC_SEND_BUFFER_SIZE (1024*1024*15) /* 15M */ #define NETVSC_SEND_BUFFER_ID 0xface -#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ +#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*16) /* 16MB */ #define NETVSC_RECEIVE_BUFFER_ID 0xcafe @@ -862,6 +868,8 @@ typedef struct nvsp_msg_ { */ #define NETVSC_MAX_CONFIGURABLE_MTU (9 * 1024) +#define NETVSC_PACKET_SIZE PAGE_SIZE + /* * Data types */ @@ -873,15 +881,14 @@ typedef struct netvsc_dev_ { struct hv_device *dev; int num_outstanding_sends; - /* List of free preallocated NETVSC_PACKET to represent RX packet */ - STAILQ_HEAD(PQ, netvsc_packet_) myrx_packet_list; - struct mtx rx_pkt_list_lock; - /* Send buffer allocated by us but manages by NetVSP */ void *send_buf; uint32_t send_buf_size; uint32_t send_buf_gpadl_handle; uint32_t send_section_size; + uint32_t send_section_count; + unsigned long bitsmap_words; + unsigned long *send_section_bitsmap; /* Receive buffer allocated by us but managed by NetVSP */ void *rx_buf; @@ -903,35 +910,43 @@ typedef struct netvsc_dev_ { hv_bool_uint8_t destroy; /* Negotiated NVSP version */ uint32_t nvsp_version; + + uint8_t callback_buf[NETVSC_PACKET_SIZE]; } netvsc_dev; typedef void (*pfn_on_send_rx_completion)(void *); -#define NETVSC_DEVICE_RING_BUFFER_SIZE (64 * PAGE_SIZE) -#define NETVSC_PACKET_MAXPAGE 16 - +#define NETVSC_DEVICE_RING_BUFFER_SIZE (128 * PAGE_SIZE) +#define NETVSC_PACKET_MAXPAGE 32 -typedef struct xfer_page_packet_ { - /* - * This needs to be here because the network RX code casts - * an instantiation of this structure to a netvsc_packet. - */ - STAILQ_ENTRY(netvsc_packet_) mylist_entry; - uint32_t count; -} xfer_page_packet; +#define NETVSC_VLAN_PRIO_MASK 0xe000 +#define NETVSC_VLAN_PRIO_SHIFT 13 +#define NETVSC_VLAN_VID_MASK 0x0fff + +#define TYPE_IPV4 2 +#define TYPE_IPV6 4 +#define TYPE_TCP 2 +#define TYPE_UDP 4 + +#define TRANSPORT_TYPE_NOT_IP 0 +#define TRANSPORT_TYPE_IPV4_TCP ((TYPE_IPV4 << 16) | TYPE_TCP) +#define TRANSPORT_TYPE_IPV4_UDP ((TYPE_IPV4 << 16) | TYPE_UDP) +#define TRANSPORT_TYPE_IPV6_TCP ((TYPE_IPV6 << 16) | TYPE_TCP) +#define TRANSPORT_TYPE_IPV6_UDP ((TYPE_IPV6 << 16) | TYPE_UDP) + +#ifdef __LP64__ +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif typedef struct netvsc_packet_ { - /* - * List used when enqueued on &net_dev->rx_packet_list, - * and when enqueued within the netvsc code - */ - STAILQ_ENTRY(netvsc_packet_) mylist_entry; struct hv_device *device; hv_bool_uint8_t is_data_pkt; /* One byte */ uint16_t vlan_tci; - xfer_page_packet *xfer_page_pkt; + uint32_t status; /* Completion */ union { @@ -948,9 +963,12 @@ typedef struct netvsc_packet_ { pfn_on_send_rx_completion on_send_completion; } send; } compl; + uint32_t send_buf_section_idx; + uint32_t send_buf_section_size; - void *extension; + void *rndis_mesg; uint32_t tot_data_buf_len; + void *data; uint32_t page_buf_count; hv_vmbus_page_buffer page_buffers[NETVSC_PACKET_MAXPAGE]; } netvsc_packet; @@ -983,16 +1001,16 @@ typedef struct hn_softc { */ extern int hv_promisc_mode; -extern void netvsc_linkstatus_callback(struct hv_device *device_obj, - uint32_t status); -extern int netvsc_recv(struct hv_device *device_obj, netvsc_packet *packet); -extern void netvsc_xmit_completion(void *context); - -extern void hv_nv_on_receive_completion(void *context); -extern netvsc_dev *hv_nv_on_device_add(struct hv_device *device, void *additional_info); -extern int hv_nv_on_device_remove(struct hv_device *device, - boolean_t destroy_channel); -extern int hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt); +void netvsc_linkstatus_callback(struct hv_device *device_obj, uint32_t status); +void netvsc_xmit_completion(void *context); +void hv_nv_on_receive_completion(struct hv_device *device, + uint64_t tid, uint32_t status); +netvsc_dev *hv_nv_on_device_add(struct hv_device *device, + void *additional_info); +int hv_nv_on_device_remove(struct hv_device *device, + boolean_t destroy_channel); +int hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt); +int hv_nv_get_next_send_section(netvsc_dev *net_dev); #endif /* __HV_NET_VSC_H__ */ Modified: head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c ============================================================================== --- head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c Wed Jun 24 01:48:44 2015 (r284745) +++ head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c Wed Jun 24 06:01:29 2015 (r284746) @@ -83,6 +83,9 @@ __FBSDID("$FreeBSD$"); #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/if_ether.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip6.h> #include <vm/vm.h> #include <vm/vm_param.h> @@ -103,6 +106,8 @@ __FBSDID("$FreeBSD$"); #include <machine/intr_machdep.h> +#include <machine/in_cksum.h> + #include <dev/hyperv/include/hyperv.h> #include "hv_net_vsc.h" #include "hv_rndis.h" @@ -165,6 +170,53 @@ static int hn_ioctl(struct ifnet *ifp, static int hn_start_locked(struct ifnet *ifp); static void hn_start(struct ifnet *ifp); +/* + * NetVsc get message transport protocol type + */ +static uint32_t get_transport_proto_type(struct mbuf *m_head) +{ + uint32_t ret_val = TRANSPORT_TYPE_NOT_IP; + uint16_t ether_type = 0; + int ether_len = 0; + struct ether_vlan_header *eh; + struct ip *iph; + struct ip6_hdr *ip6; + + eh = mtod(m_head, struct ether_vlan_header*); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + ether_len = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + ether_type = eh->evl_proto; + } else { + ether_len = ETHER_HDR_LEN; + ether_type = eh->evl_encap_proto; + } *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"