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
This commit is contained in:
parent
cce1064454
commit
71f5e47790
@ -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) \
|
||||
|
@ -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_net_vsp(struct hv_device *device);
|
||||
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 *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_device *device)
|
||||
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 hv_device *device)
|
||||
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 hv_device *device)
|
||||
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(struct hv_device *device)
|
||||
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(struct hv_device *device)
|
||||
* 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(struct hv_device *device)
|
||||
/* 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(struct hv_device *device)
|
||||
|
||||
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_dev)
|
||||
|
||||
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 *net_dev)
|
||||
|
||||
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 *net_dev)
|
||||
*/
|
||||
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 *device)
|
||||
{
|
||||
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 *device)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *device)
|
||||
|
||||
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 *device, void *additional_info)
|
||||
|
||||
/* 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 @@ hv_nv_on_device_add(struct hv_device *device, void *additional_info)
|
||||
*/
|
||||
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 @@ 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)
|
||||
{
|
||||
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 *device, boolean_t destroy_channel)
|
||||
|
||||
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 *device, boolean_t destroy_channel)
|
||||
/*
|
||||
* 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_device *device, hv_vm_packet_descriptor *pkt)
|
||||
== 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, netvsc_packet *pkt)
|
||||
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, netvsc_packet *pkt)
|
||||
* 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);
|
||||
net_vsc_pkt->tot_data_buf_len =
|
||||
vm_xfer_page_pkt->ranges[i].byte_count;
|
||||
|
||||
/* 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);
|
||||
|
||||
/*
|
||||
* 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_device *device, uint64_t tid)
|
||||
|
||||
/* 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 */
|
||||
@ -1031,82 +958,27 @@ hv_nv_send_receive_completion(struct hv_device *device, uint64_t tid)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
@ -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_chimney_event_ {
|
||||
|
||||
|
||||
#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;
|
||||
#define NETVSC_VLAN_PRIO_MASK 0xe000
|
||||
#define NETVSC_VLAN_PRIO_SHIFT 13
|
||||
#define NETVSC_VLAN_VID_MASK 0x0fff
|
||||
|
||||
uint32_t count;
|
||||
} xfer_page_packet;
|
||||
#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__ */
|
||||
|
||||
|
@ -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, u_long cmd, caddr_t data);
|
||||
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;
|
||||
}
|
||||
|
||||
switch (ntohs(ether_type)) {
|
||||
case ETHERTYPE_IPV6:
|
||||
ip6 = (struct ip6_hdr *)(m_head->m_data + ether_len);
|
||||
|
||||
if (IPPROTO_TCP == ip6->ip6_nxt) {
|
||||
ret_val = TRANSPORT_TYPE_IPV6_TCP;
|
||||
} else if (IPPROTO_UDP == ip6->ip6_nxt) {
|
||||
ret_val = TRANSPORT_TYPE_IPV6_UDP;
|
||||
}
|
||||
break;
|
||||
case ETHERTYPE_IP:
|
||||
iph = (struct ip *)(m_head->m_data + ether_len);
|
||||
|
||||
if (IPPROTO_TCP == iph->ip_p) {
|
||||
ret_val = TRANSPORT_TYPE_IPV4_TCP;
|
||||
} else if (IPPROTO_UDP == iph->ip_p) {
|
||||
ret_val = TRANSPORT_TYPE_IPV4_UDP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret_val = TRANSPORT_TYPE_NOT_IP;
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* NetVsc driver initialization
|
||||
@ -276,8 +328,11 @@ netvsc_attach(device_t dev)
|
||||
* Tell upper layers that we support full VLAN capability.
|
||||
*/
|
||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header);
|
||||
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
|
||||
ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
|
||||
ifp->if_capabilities |=
|
||||
IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO;
|
||||
ifp->if_capenable |=
|
||||
IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM | IFCAP_TSO;
|
||||
ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
|
||||
|
||||
ret = hv_rf_on_device_add(device_ctx, &device_info);
|
||||
if (ret != 0) {
|
||||
@ -347,7 +402,7 @@ netvsc_xmit_completion(void *context)
|
||||
mb = (struct mbuf *)(uintptr_t)packet->compl.send.send_completion_tid;
|
||||
buf = ((uint8_t *)packet) - HV_NV_PACKET_OFFSET_IN_BUF;
|
||||
|
||||
free(buf, M_DEVBUF);
|
||||
free(buf, M_NETVSC);
|
||||
|
||||
if (mb != NULL) {
|
||||
m_freem(mb);
|
||||
@ -362,17 +417,29 @@ hn_start_locked(struct ifnet *ifp)
|
||||
{
|
||||
hn_softc_t *sc = ifp->if_softc;
|
||||
struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
|
||||
netvsc_dev *net_dev = sc->net_dev;
|
||||
device_t dev = device_ctx->device;
|
||||
uint8_t *buf;
|
||||
netvsc_packet *packet;
|
||||
struct mbuf *m_head, *m;
|
||||
struct mbuf *mc_head = NULL;
|
||||
struct ether_vlan_header *eh;
|
||||
rndis_msg *rndis_mesg;
|
||||
rndis_packet *rndis_pkt;
|
||||
rndis_per_packet_info *rppi;
|
||||
ndis_8021q_info *rppi_vlan_info;
|
||||
rndis_tcp_ip_csum_info *csum_info;
|
||||
rndis_tcp_tso_info *tso_info;
|
||||
int ether_len;
|
||||
int i;
|
||||
int num_frags;
|
||||
int len;
|
||||
int xlen;
|
||||
int rppi_size;
|
||||
int retries = 0;
|
||||
int ret = 0;
|
||||
int ret = 0;
|
||||
uint32_t rndis_msg_size = 0;
|
||||
uint32_t trans_proto_type;
|
||||
uint32_t send_buf_section_idx =
|
||||
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
|
||||
|
||||
while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) {
|
||||
IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head);
|
||||
@ -382,7 +449,6 @@ hn_start_locked(struct ifnet *ifp)
|
||||
|
||||
len = 0;
|
||||
num_frags = 0;
|
||||
xlen = 0;
|
||||
|
||||
/* Walk the mbuf list computing total length and num frags */
|
||||
for (m = m_head; m != NULL; m = m->m_next) {
|
||||
@ -401,50 +467,42 @@ hn_start_locked(struct ifnet *ifp)
|
||||
|
||||
/* If exceeds # page_buffers in netvsc_packet */
|
||||
if (num_frags > NETVSC_PACKET_MAXPAGE) {
|
||||
m_freem(m);
|
||||
|
||||
device_printf(dev, "exceed max page buffers,%d,%d\n",
|
||||
num_frags, NETVSC_PACKET_MAXPAGE);
|
||||
m_freem(m_head);
|
||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
rppi_size = 0;
|
||||
if (m_head->m_flags & M_VLANTAG) {
|
||||
rppi_size = sizeof(rndis_per_packet_info) +
|
||||
sizeof(ndis_8021q_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a buffer with space for a netvsc packet plus a
|
||||
* number of reserved areas. First comes a (currently 16
|
||||
* bytes, currently unused) reserved data area. Second is
|
||||
* the netvsc_packet, which includes (currently 4) page
|
||||
* buffers. Third (optional) is a rndis_per_packet_info
|
||||
* struct, but only if a VLAN tag should be inserted into the
|
||||
* Ethernet frame by the Hyper-V infrastructure. Fourth is
|
||||
* an area reserved for an rndis_filter_packet struct.
|
||||
* the netvsc_packet. Third is an area reserved for an
|
||||
* rndis_filter_packet struct. Fourth (optional) is a
|
||||
* rndis_per_packet_info struct.
|
||||
* Changed malloc to M_NOWAIT to avoid sleep under spin lock.
|
||||
* No longer reserving extra space for page buffers, as they
|
||||
* are already part of the netvsc_packet.
|
||||
*/
|
||||
buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF +
|
||||
sizeof(netvsc_packet) + rppi_size +
|
||||
sizeof(rndis_filter_packet),
|
||||
M_DEVBUF, M_ZERO | M_NOWAIT);
|
||||
sizeof(netvsc_packet) +
|
||||
sizeof(rndis_msg) +
|
||||
RNDIS_VLAN_PPI_SIZE +
|
||||
RNDIS_TSO_PPI_SIZE +
|
||||
RNDIS_CSUM_PPI_SIZE,
|
||||
M_NETVSC, M_ZERO | M_NOWAIT);
|
||||
if (buf == NULL) {
|
||||
m_freem(m);
|
||||
|
||||
device_printf(dev, "hn:malloc packet failed\n");
|
||||
m_freem(m_head);
|
||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF);
|
||||
*(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF;
|
||||
|
||||
/*
|
||||
* extension points to the area reserved for the
|
||||
* rndis_filter_packet, which is placed just after
|
||||
* the netvsc_packet (and rppi struct, if present;
|
||||
* length is updated later).
|
||||
*/
|
||||
packet->extension = packet + 1;
|
||||
packet->is_data_pkt = TRUE;
|
||||
|
||||
/* Set up the rndis header */
|
||||
packet->page_buf_count = num_frags;
|
||||
@ -452,15 +510,173 @@ hn_start_locked(struct ifnet *ifp)
|
||||
/* Initialize it from the mbuf */
|
||||
packet->tot_data_buf_len = len;
|
||||
|
||||
/*
|
||||
* extension points to the area reserved for the
|
||||
* rndis_filter_packet, which is placed just after
|
||||
* the netvsc_packet (and rppi struct, if present;
|
||||
* length is updated later).
|
||||
*/
|
||||
packet->rndis_mesg = packet + 1;
|
||||
rndis_mesg = (rndis_msg *)packet->rndis_mesg;
|
||||
rndis_mesg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
|
||||
|
||||
rndis_pkt = &rndis_mesg->msg.packet;
|
||||
rndis_pkt->data_offset = sizeof(rndis_packet);
|
||||
rndis_pkt->data_length = packet->tot_data_buf_len;
|
||||
rndis_pkt->per_pkt_info_offset = sizeof(rndis_packet);
|
||||
|
||||
rndis_msg_size = RNDIS_MESSAGE_SIZE(rndis_packet);
|
||||
|
||||
/*
|
||||
* If the Hyper-V infrastructure needs to embed a VLAN tag,
|
||||
* initialize netvsc_packet and rppi struct values as needed.
|
||||
*/
|
||||
if (rppi_size) {
|
||||
/* Lower layers need the VLAN TCI */
|
||||
if (m_head->m_flags & M_VLANTAG) {
|
||||
/*
|
||||
* set up some additional fields so the Hyper-V infrastructure will stuff the VLAN tag
|
||||
* into the frame.
|
||||
*/
|
||||
packet->vlan_tci = m_head->m_pkthdr.ether_vtag;
|
||||
|
||||
rndis_msg_size += RNDIS_VLAN_PPI_SIZE;
|
||||
|
||||
rppi = hv_set_rppi_data(rndis_mesg, RNDIS_VLAN_PPI_SIZE,
|
||||
ieee_8021q_info);
|
||||
|
||||
/* VLAN info immediately follows rppi struct */
|
||||
rppi_vlan_info = (ndis_8021q_info *)((char*)rppi +
|
||||
rppi->per_packet_info_offset);
|
||||
/* FreeBSD does not support CFI or priority */
|
||||
rppi_vlan_info->u1.s1.vlan_id =
|
||||
packet->vlan_tci & 0xfff;
|
||||
}
|
||||
|
||||
if (0 == m_head->m_pkthdr.csum_flags) {
|
||||
goto pre_send;
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
ether_len = ETHER_HDR_LEN;
|
||||
}
|
||||
|
||||
trans_proto_type = get_transport_proto_type(m_head);
|
||||
if (TRANSPORT_TYPE_NOT_IP == trans_proto_type) {
|
||||
goto pre_send;
|
||||
}
|
||||
|
||||
/*
|
||||
* TSO packet needless to setup the send side checksum
|
||||
* offload.
|
||||
*/
|
||||
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
|
||||
goto do_tso;
|
||||
}
|
||||
|
||||
/* setup checksum offload */
|
||||
rndis_msg_size += RNDIS_CSUM_PPI_SIZE;
|
||||
rppi = hv_set_rppi_data(rndis_mesg, RNDIS_CSUM_PPI_SIZE,
|
||||
tcpip_chksum_info);
|
||||
csum_info = (rndis_tcp_ip_csum_info *)((char*)rppi +
|
||||
rppi->per_packet_info_offset);
|
||||
|
||||
if (trans_proto_type & (TYPE_IPV4 << 16)) {
|
||||
csum_info->xmit.is_ipv4 = 1;
|
||||
} else {
|
||||
csum_info->xmit.is_ipv6 = 1;
|
||||
}
|
||||
|
||||
if (trans_proto_type & TYPE_TCP) {
|
||||
csum_info->xmit.tcp_csum = 1;
|
||||
csum_info->xmit.tcp_header_offset = 0;
|
||||
} else if (trans_proto_type & TYPE_UDP) {
|
||||
csum_info->xmit.udp_csum = 1;
|
||||
}
|
||||
|
||||
goto pre_send;
|
||||
|
||||
do_tso:
|
||||
/* setup TCP segmentation offload */
|
||||
rndis_msg_size += RNDIS_TSO_PPI_SIZE;
|
||||
rppi = hv_set_rppi_data(rndis_mesg, RNDIS_TSO_PPI_SIZE,
|
||||
tcp_large_send_info);
|
||||
|
||||
tso_info = (rndis_tcp_tso_info *)((char *)rppi +
|
||||
rppi->per_packet_info_offset);
|
||||
tso_info->lso_v2_xmit.type =
|
||||
RNDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE;
|
||||
|
||||
if (trans_proto_type & (TYPE_IPV4 << 16)) {
|
||||
struct ip *ip =
|
||||
(struct ip *)(m_head->m_data + ether_len);
|
||||
unsigned long iph_len = ip->ip_hl << 2;
|
||||
struct tcphdr *th =
|
||||
(struct tcphdr *)((caddr_t)ip + iph_len);
|
||||
|
||||
tso_info->lso_v2_xmit.ip_version =
|
||||
RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV4;
|
||||
ip->ip_len = 0;
|
||||
ip->ip_sum = 0;
|
||||
|
||||
th->th_sum = in_pseudo(ip->ip_src.s_addr,
|
||||
ip->ip_dst.s_addr,
|
||||
htons(IPPROTO_TCP));
|
||||
} else {
|
||||
struct ip6_hdr *ip6 =
|
||||
(struct ip6_hdr *)(m_head->m_data + ether_len);
|
||||
struct tcphdr *th = (struct tcphdr *)(ip6 + 1);
|
||||
|
||||
tso_info->lso_v2_xmit.ip_version =
|
||||
RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV6;
|
||||
ip6->ip6_plen = 0;
|
||||
th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
|
||||
}
|
||||
tso_info->lso_v2_xmit.tcp_header_offset = 0;
|
||||
tso_info->lso_v2_xmit.mss = m_head->m_pkthdr.tso_segsz;
|
||||
|
||||
pre_send:
|
||||
rndis_mesg->msg_len = packet->tot_data_buf_len + rndis_msg_size;
|
||||
packet->tot_data_buf_len = rndis_mesg->msg_len;
|
||||
|
||||
/* send packet with send buffer */
|
||||
if (packet->tot_data_buf_len < net_dev->send_section_size) {
|
||||
send_buf_section_idx =
|
||||
hv_nv_get_next_send_section(net_dev);
|
||||
if (send_buf_section_idx !=
|
||||
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX) {
|
||||
char *dest = ((char *)net_dev->send_buf +
|
||||
send_buf_section_idx *
|
||||
net_dev->send_section_size);
|
||||
|
||||
memcpy(dest, rndis_mesg, rndis_msg_size);
|
||||
dest += rndis_msg_size;
|
||||
for (m = m_head; m != NULL; m = m->m_next) {
|
||||
if (m->m_len) {
|
||||
memcpy(dest,
|
||||
(void *)mtod(m, vm_offset_t),
|
||||
m->m_len);
|
||||
dest += m->m_len;
|
||||
}
|
||||
}
|
||||
|
||||
packet->send_buf_section_idx =
|
||||
send_buf_section_idx;
|
||||
packet->send_buf_section_size =
|
||||
packet->tot_data_buf_len;
|
||||
packet->page_buf_count = 0;
|
||||
goto do_send;
|
||||
}
|
||||
}
|
||||
|
||||
/* send packet with page buffer */
|
||||
packet->page_buffers[0].pfn =
|
||||
atop(hv_get_phys_addr(rndis_mesg));
|
||||
packet->page_buffers[0].offset =
|
||||
(unsigned long)rndis_mesg & PAGE_MASK;
|
||||
packet->page_buffers[0].length = rndis_msg_size;
|
||||
|
||||
/*
|
||||
* Fill the page buffers with mbuf info starting at index
|
||||
* HV_RF_NUM_TX_RESERVED_PAGE_BUFS.
|
||||
@ -479,6 +695,12 @@ hn_start_locked(struct ifnet *ifp)
|
||||
}
|
||||
}
|
||||
|
||||
packet->send_buf_section_idx =
|
||||
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
|
||||
packet->send_buf_section_size = 0;
|
||||
|
||||
do_send:
|
||||
|
||||
/*
|
||||
* If bpf, copy the mbuf chain. This is less expensive than
|
||||
* it appears; the mbuf clusters are not copied, only their
|
||||
@ -497,8 +719,7 @@ hn_start_locked(struct ifnet *ifp)
|
||||
packet->compl.send.send_completion_tid = (uint64_t)(uintptr_t)m_head;
|
||||
|
||||
/* Removed critical_enter(), does not appear necessary */
|
||||
ret = hv_rf_on_send(device_ctx, packet);
|
||||
|
||||
ret = hv_nv_on_send(device_ctx, packet);
|
||||
if (ret == 0) {
|
||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
|
||||
/* if bpf && mc_head, call bpf_mtap code */
|
||||
@ -526,6 +747,7 @@ hn_start_locked(struct ifnet *ifp)
|
||||
* send completion
|
||||
*/
|
||||
netvsc_xmit_completion(packet);
|
||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
|
||||
}
|
||||
|
||||
/* if bpf && mc_head, free the mbuf chain copy */
|
||||
@ -621,13 +843,14 @@ hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
|
||||
* Note: This is no longer used as a callback
|
||||
*/
|
||||
int
|
||||
netvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet)
|
||||
netvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet,
|
||||
rndis_tcp_ip_csum_info *csum_info)
|
||||
{
|
||||
hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device);
|
||||
struct mbuf *m_new;
|
||||
struct ifnet *ifp;
|
||||
device_t dev = device_ctx->device;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
if (sc == NULL) {
|
||||
return (0); /* TODO: KYS how can this be! */
|
||||
@ -661,36 +884,35 @@ netvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet)
|
||||
|
||||
m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
|
||||
|
||||
if (m_new == NULL)
|
||||
if (m_new == NULL) {
|
||||
device_printf(dev, "alloc mbuf failed.\n");
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Remove trailing junk from RX data buffer.
|
||||
* Fixme: This will not work for multiple Hyper-V RX buffers.
|
||||
* Fortunately, the channel gathers all RX data into one buffer.
|
||||
*
|
||||
* L2 frame length, with L2 header, not including CRC
|
||||
*/
|
||||
packet->page_buffers[0].length = packet->tot_data_buf_len;
|
||||
|
||||
/*
|
||||
* Copy the received packet to one or more mbufs.
|
||||
* The copy is required since the memory pointed to by netvsc_packet
|
||||
* cannot be deallocated
|
||||
*/
|
||||
for (i=0; i < packet->page_buf_count; i++) {
|
||||
/* Shift virtual page number to form virtual page address */
|
||||
uint8_t *vaddr = (uint8_t *)(uintptr_t)
|
||||
(packet->page_buffers[i].pfn << PAGE_SHIFT);
|
||||
|
||||
hv_m_append(m_new, packet->page_buffers[i].length,
|
||||
vaddr + packet->page_buffers[i].offset);
|
||||
}
|
||||
|
||||
hv_m_append(m_new, packet->tot_data_buf_len,
|
||||
packet->data);
|
||||
|
||||
m_new->m_pkthdr.rcvif = ifp;
|
||||
|
||||
/* receive side checksum offload */
|
||||
m_new->m_pkthdr.csum_flags = 0;
|
||||
if (NULL != csum_info) {
|
||||
/* IP csum offload */
|
||||
if (csum_info->receive.ip_csum_succeeded) {
|
||||
m_new->m_pkthdr.csum_flags |=
|
||||
(CSUM_IP_CHECKED | CSUM_IP_VALID);
|
||||
}
|
||||
|
||||
/* TCP csum offload */
|
||||
if (csum_info->receive.tcp_csum_succeeded) {
|
||||
m_new->m_pkthdr.csum_flags |=
|
||||
(CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
|
||||
m_new->m_pkthdr.csum_data = 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
if ((packet->vlan_tci != 0) &&
|
||||
(ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) {
|
||||
(ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) {
|
||||
m_new->m_pkthdr.ether_vtag = packet->vlan_tci;
|
||||
m_new->m_flags |= M_VLANTAG;
|
||||
}
|
||||
@ -858,13 +1080,34 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
break;
|
||||
case SIOCSIFCAP:
|
||||
mask = ifr->ifr_reqcap ^ ifp->if_capenable;
|
||||
if (mask & IFCAP_HWCSUM) {
|
||||
if (IFCAP_HWCSUM & ifp->if_capenable) {
|
||||
ifp->if_capenable &= ~IFCAP_HWCSUM;
|
||||
if (mask & IFCAP_TXCSUM) {
|
||||
if (IFCAP_TXCSUM & ifp->if_capenable) {
|
||||
ifp->if_capenable &= ~IFCAP_TXCSUM;
|
||||
ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
|
||||
} else {
|
||||
ifp->if_capenable |= IFCAP_HWCSUM;
|
||||
ifp->if_capenable |= IFCAP_TXCSUM;
|
||||
ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & IFCAP_RXCSUM) {
|
||||
if (IFCAP_RXCSUM & ifp->if_capenable) {
|
||||
ifp->if_capenable &= ~IFCAP_RXCSUM;
|
||||
} else {
|
||||
ifp->if_capenable |= IFCAP_RXCSUM;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & IFCAP_TSO4) {
|
||||
ifp->if_capenable ^= IFCAP_TSO4;
|
||||
ifp->if_hwassist ^= CSUM_IP_TSO;
|
||||
}
|
||||
|
||||
if (mask & IFCAP_TSO6) {
|
||||
ifp->if_capenable ^= IFCAP_TSO6;
|
||||
ifp->if_hwassist ^= CSUM_IP6_TSO;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
break;
|
||||
case SIOCADDMULTI:
|
||||
|
@ -24,6 +24,8 @@
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __HV_RNDIS_H__
|
||||
@ -36,6 +38,9 @@
|
||||
#define NDIS_VERSION_5_0 0x00050000
|
||||
#define NDIS_VERSION_5_1 0x00050001
|
||||
#define NDIS_VERSION_6_0 0x00060000
|
||||
#define NDIS_VERSION_6_1 0x00060001
|
||||
#define NDIS_VERSION_6_30 0x0006001e
|
||||
|
||||
#define NDIS_VERSION (NDIS_VERSION_5_1)
|
||||
|
||||
/*
|
||||
@ -347,6 +352,36 @@
|
||||
#define RNDIS_MAJOR_VERSION 0x00000001
|
||||
#define RNDIS_MINOR_VERSION 0x00000000
|
||||
|
||||
|
||||
/*
|
||||
* Remote NDIS offload parameters
|
||||
*/
|
||||
#define RNDIS_OBJECT_TYPE_DEFAULT 0x80
|
||||
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_REVISION_3 3
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_NO_CHANGE 0
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED 1
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED 2
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED 2
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_RSC_DISABLED 1
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_RSC_ENABLED 2
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED 1
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED 2
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3
|
||||
#define RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4
|
||||
|
||||
#define RNDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE 1
|
||||
#define RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0
|
||||
#define RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1
|
||||
|
||||
|
||||
#define RNDIS_OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B /* query only */
|
||||
#define RNDIS_OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C /* set only */
|
||||
#define RNDIS_OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D/* query only */
|
||||
#define RNDIS_OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG 0xFC01020E /* query only */
|
||||
#define RNDIS_OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020F /* query */
|
||||
#define RNDIS_OID_OFFLOAD_ENCAPSULATION 0x0101010A /* set/query */
|
||||
|
||||
/*
|
||||
* NdisInitialize message
|
||||
*/
|
||||
@ -585,6 +620,108 @@ typedef struct ndis_8021q_info_ {
|
||||
} u1;
|
||||
} ndis_8021q_info;
|
||||
|
||||
struct rndis_object_header {
|
||||
uint8_t type;
|
||||
uint8_t revision;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
typedef struct rndis_offload_params_ {
|
||||
struct rndis_object_header header;
|
||||
uint8_t ipv4_csum;
|
||||
uint8_t tcp_ipv4_csum;
|
||||
uint8_t udp_ipv4_csum;
|
||||
uint8_t tcp_ipv6_csum;
|
||||
uint8_t udp_ipv6_csum;
|
||||
uint8_t lso_v1;
|
||||
uint8_t ip_sec_v1;
|
||||
uint8_t lso_v2_ipv4;
|
||||
uint8_t lso_v2_ipv6;
|
||||
uint8_t tcp_connection_ipv4;
|
||||
uint8_t tcp_connection_ipv6;
|
||||
uint32_t flags;
|
||||
uint8_t ip_sec_v2;
|
||||
uint8_t ip_sec_v2_ipv4;
|
||||
struct {
|
||||
uint8_t rsc_ipv4;
|
||||
uint8_t rsc_ipv6;
|
||||
};
|
||||
struct {
|
||||
uint8_t encapsulated_packet_task_offload;
|
||||
uint8_t encapsulation_types;
|
||||
};
|
||||
|
||||
} rndis_offload_params;
|
||||
|
||||
|
||||
typedef struct rndis_tcp_ip_csum_info_ {
|
||||
union {
|
||||
struct {
|
||||
uint32_t is_ipv4:1;
|
||||
uint32_t is_ipv6:1;
|
||||
uint32_t tcp_csum:1;
|
||||
uint32_t udp_csum:1;
|
||||
uint32_t ip_header_csum:1;
|
||||
uint32_t reserved:11;
|
||||
uint32_t tcp_header_offset:10;
|
||||
} xmit;
|
||||
struct {
|
||||
uint32_t tcp_csum_failed:1;
|
||||
uint32_t udp_csum_failed:1;
|
||||
uint32_t ip_csum_failed:1;
|
||||
uint32_t tcp_csum_succeeded:1;
|
||||
uint32_t udp_csum_succeeded:1;
|
||||
uint32_t ip_csum_succeeded:1;
|
||||
uint32_t loopback:1;
|
||||
uint32_t tcp_csum_value_invalid:1;
|
||||
uint32_t ip_csum_value_invalid:1;
|
||||
} receive;
|
||||
uint32_t value;
|
||||
};
|
||||
} rndis_tcp_ip_csum_info;
|
||||
|
||||
typedef struct rndis_tcp_tso_info_ {
|
||||
union {
|
||||
struct {
|
||||
uint32_t unused:30;
|
||||
uint32_t type:1;
|
||||
uint32_t reserved2:1;
|
||||
} xmit;
|
||||
struct {
|
||||
uint32_t mss:20;
|
||||
uint32_t tcp_header_offset:10;
|
||||
uint32_t type:1;
|
||||
uint32_t reserved2:1;
|
||||
} lso_v1_xmit;
|
||||
struct {
|
||||
uint32_t tcp_payload:30;
|
||||
uint32_t type:1;
|
||||
uint32_t reserved2:1;
|
||||
} lso_v1_xmit_complete;
|
||||
struct {
|
||||
uint32_t mss:20;
|
||||
uint32_t tcp_header_offset:10;
|
||||
uint32_t type:1;
|
||||
uint32_t ip_version:1;
|
||||
} lso_v2_xmit;
|
||||
struct {
|
||||
uint32_t reserved:30;
|
||||
uint32_t type:1;
|
||||
uint32_t reserved2:1;
|
||||
} lso_v2_xmit_complete;
|
||||
uint32_t value;
|
||||
};
|
||||
} rndis_tcp_tso_info;
|
||||
|
||||
#define RNDIS_VLAN_PPI_SIZE (sizeof(rndis_per_packet_info) + \
|
||||
sizeof(ndis_8021q_info))
|
||||
|
||||
#define RNDIS_CSUM_PPI_SIZE (sizeof(rndis_per_packet_info) + \
|
||||
sizeof(rndis_tcp_ip_csum_info))
|
||||
|
||||
#define RNDIS_TSO_PPI_SIZE (sizeof(rndis_per_packet_info) + \
|
||||
sizeof(rndis_tcp_tso_info))
|
||||
|
||||
/*
|
||||
* Format of Information buffer passed in a SetRequest for the OID
|
||||
* OID_GEN_RNDIS_CONFIG_PARAMETER.
|
||||
@ -906,6 +1043,18 @@ typedef struct rndismp_rx_bufs_info_ {
|
||||
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
|
||||
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
|
||||
|
||||
/*
|
||||
* Externs
|
||||
*/
|
||||
int netvsc_recv(struct hv_device *device_ctx,
|
||||
netvsc_packet *packet,
|
||||
rndis_tcp_ip_csum_info *csum_info);
|
||||
|
||||
void* hv_set_rppi_data(rndis_msg *rndis_mesg,
|
||||
uint32_t rppi_size,
|
||||
int pkt_type);
|
||||
|
||||
void* hv_get_ppi_data(rndis_packet *rpkt, uint32_t type);
|
||||
|
||||
#endif /* __HV_RNDIS_H__ */
|
||||
|
||||
|
@ -67,9 +67,64 @@ static int hv_rf_set_packet_filter(rndis_device *device, uint32_t new_filter);
|
||||
static int hv_rf_init_device(rndis_device *device);
|
||||
static int hv_rf_open_device(rndis_device *device);
|
||||
static int hv_rf_close_device(rndis_device *device);
|
||||
static void hv_rf_on_send_completion(void *context);
|
||||
static void hv_rf_on_send_request_completion(void *context);
|
||||
static void hv_rf_on_send_request_halt_completion(void *context);
|
||||
int
|
||||
hv_rf_send_offload_request(struct hv_device *device,
|
||||
rndis_offload_params *offloads);
|
||||
/*
|
||||
* Set the Per-Packet-Info with the specified type
|
||||
*/
|
||||
void *
|
||||
hv_set_rppi_data(rndis_msg *rndis_mesg, uint32_t rppi_size,
|
||||
int pkt_type)
|
||||
{
|
||||
rndis_packet *rndis_pkt;
|
||||
rndis_per_packet_info *rppi;
|
||||
|
||||
rndis_pkt = &rndis_mesg->msg.packet;
|
||||
rndis_pkt->data_offset += rppi_size;
|
||||
|
||||
rppi = (rndis_per_packet_info *)((char *)rndis_pkt +
|
||||
rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_length);
|
||||
|
||||
rppi->size = rppi_size;
|
||||
rppi->type = pkt_type;
|
||||
rppi->per_packet_info_offset = sizeof(rndis_per_packet_info);
|
||||
|
||||
rndis_pkt->per_pkt_info_length += rppi_size;
|
||||
|
||||
return (rppi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the Per-Packet-Info with the specified type
|
||||
* return NULL if not found.
|
||||
*/
|
||||
void *
|
||||
hv_get_ppi_data(rndis_packet *rpkt, uint32_t type)
|
||||
{
|
||||
rndis_per_packet_info *ppi;
|
||||
int len;
|
||||
|
||||
if (rpkt->per_pkt_info_offset == 0)
|
||||
return (NULL);
|
||||
|
||||
ppi = (rndis_per_packet_info *)((unsigned long)rpkt +
|
||||
rpkt->per_pkt_info_offset);
|
||||
len = rpkt->per_pkt_info_length;
|
||||
|
||||
while (len > 0) {
|
||||
if (ppi->type == type)
|
||||
return (void *)((unsigned long)ppi +
|
||||
ppi->per_packet_info_offset);
|
||||
|
||||
len -= ppi->size;
|
||||
ppi = (rndis_per_packet_info *)((unsigned long)ppi + ppi->size);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@ -80,7 +135,7 @@ hv_get_rndis_device(void)
|
||||
{
|
||||
rndis_device *device;
|
||||
|
||||
device = malloc(sizeof(rndis_device), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
device = malloc(sizeof(rndis_device), M_NETVSC, M_NOWAIT | M_ZERO);
|
||||
if (device == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
@ -102,7 +157,7 @@ static inline void
|
||||
hv_put_rndis_device(rndis_device *device)
|
||||
{
|
||||
mtx_destroy(&device->req_lock);
|
||||
free(device, M_DEVBUF);
|
||||
free(device, M_NETVSC);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -116,7 +171,7 @@ hv_rndis_request(rndis_device *device, uint32_t message_type,
|
||||
rndis_msg *rndis_mesg;
|
||||
rndis_set_request *set;
|
||||
|
||||
request = malloc(sizeof(rndis_request), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
request = malloc(sizeof(rndis_request), M_NETVSC, M_NOWAIT | M_ZERO);
|
||||
if (request == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
@ -161,7 +216,7 @@ hv_put_rndis_request(rndis_device *device, rndis_request *request)
|
||||
mtx_unlock_spin(&device->req_lock);
|
||||
|
||||
sema_destroy(&request->wait_sema);
|
||||
free(request, M_DEVBUF);
|
||||
free(request, M_NETVSC);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -169,7 +224,7 @@ hv_put_rndis_request(rndis_device *device, rndis_request *request)
|
||||
*/
|
||||
static int
|
||||
hv_rf_send_request(rndis_device *device, rndis_request *request,
|
||||
uint32_t message_type)
|
||||
uint32_t message_type)
|
||||
{
|
||||
int ret;
|
||||
netvsc_packet *packet;
|
||||
@ -196,6 +251,9 @@ hv_rf_send_request(rndis_device *device, rndis_request *request,
|
||||
hv_rf_on_send_request_halt_completion;
|
||||
}
|
||||
packet->compl.send.send_completion_tid = (unsigned long)device;
|
||||
packet->send_buf_section_idx =
|
||||
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
|
||||
packet->send_buf_section_size = 0;
|
||||
|
||||
ret = hv_nv_on_send(device->net_dev->dev, packet);
|
||||
|
||||
@ -248,6 +306,84 @@ hv_rf_receive_response(rndis_device *device, rndis_msg *response)
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
hv_rf_send_offload_request(struct hv_device *device,
|
||||
rndis_offload_params *offloads)
|
||||
{
|
||||
rndis_request *request;
|
||||
rndis_set_request *set;
|
||||
rndis_offload_params *offload_req;
|
||||
rndis_set_complete *set_complete;
|
||||
rndis_device *rndis_dev;
|
||||
hn_softc_t *sc = device_get_softc(device->device);
|
||||
device_t dev = device->device;
|
||||
netvsc_dev *net_dev = sc->net_dev;
|
||||
uint32_t vsp_version = net_dev->nvsp_version;
|
||||
uint32_t extlen = sizeof(rndis_offload_params);
|
||||
int ret;
|
||||
|
||||
if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
|
||||
extlen = VERSION_4_OFFLOAD_SIZE;
|
||||
/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
|
||||
* UDP checksum offload.
|
||||
*/
|
||||
offloads->udp_ipv4_csum = 0;
|
||||
offloads->udp_ipv6_csum = 0;
|
||||
}
|
||||
|
||||
rndis_dev = net_dev->extension;
|
||||
|
||||
request = hv_rndis_request(rndis_dev, REMOTE_NDIS_SET_MSG,
|
||||
RNDIS_MESSAGE_SIZE(rndis_set_request) + extlen);
|
||||
if (!request)
|
||||
return (ENOMEM);
|
||||
|
||||
set = &request->request_msg.msg.set_request;
|
||||
set->oid = RNDIS_OID_TCP_OFFLOAD_PARAMETERS;
|
||||
set->info_buffer_length = extlen;
|
||||
set->info_buffer_offset = sizeof(rndis_set_request);
|
||||
set->device_vc_handle = 0;
|
||||
|
||||
offload_req = (rndis_offload_params *)((unsigned long)set +
|
||||
set->info_buffer_offset);
|
||||
*offload_req = *offloads;
|
||||
offload_req->header.type = RNDIS_OBJECT_TYPE_DEFAULT;
|
||||
offload_req->header.revision = RNDIS_OFFLOAD_PARAMETERS_REVISION_3;
|
||||
offload_req->header.size = extlen;
|
||||
|
||||
ret = hv_rf_send_request(rndis_dev, request, REMOTE_NDIS_SET_MSG);
|
||||
if (ret != 0) {
|
||||
device_printf(dev, "hv send offload request failed, ret=%d!\n",
|
||||
ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = sema_timedwait(&request->wait_sema, 500);
|
||||
if (ret != 0) {
|
||||
device_printf(dev, "hv send offload request timeout\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
set_complete = &request->response_msg.msg.set_complete;
|
||||
if (set_complete->status == RNDIS_STATUS_SUCCESS) {
|
||||
device_printf(dev, "hv send offload request succeeded\n");
|
||||
ret = 0;
|
||||
} else {
|
||||
if (set_complete->status == STATUS_NOT_SUPPORTED) {
|
||||
device_printf(dev, "HV Not support offload\n");
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = set_complete->status;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (request)
|
||||
hv_put_rndis_request(rndis_dev, request);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter receive indicate status
|
||||
*/
|
||||
@ -256,12 +392,18 @@ hv_rf_receive_indicate_status(rndis_device *device, rndis_msg *response)
|
||||
{
|
||||
rndis_indicate_status *indicate = &response->msg.indicate_status;
|
||||
|
||||
if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) {
|
||||
switch(indicate->status) {
|
||||
case RNDIS_STATUS_MEDIA_CONNECT:
|
||||
netvsc_linkstatus_callback(device->net_dev->dev, 1);
|
||||
} else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) {
|
||||
break;
|
||||
case RNDIS_STATUS_MEDIA_DISCONNECT:
|
||||
netvsc_linkstatus_callback(device->net_dev->dev, 0);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
/* TODO: */
|
||||
device_printf(device->net_dev->dev->device,
|
||||
"unknown status %d received\n", indicate->status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,9 +414,10 @@ static void
|
||||
hv_rf_receive_data(rndis_device *device, rndis_msg *message, netvsc_packet *pkt)
|
||||
{
|
||||
rndis_packet *rndis_pkt;
|
||||
rndis_per_packet_info *rppi;
|
||||
ndis_8021q_info *rppi_vlan_info;
|
||||
ndis_8021q_info *rppi_vlan_info;
|
||||
uint32_t data_offset;
|
||||
rndis_tcp_ip_csum_info *csum_info = NULL;
|
||||
device_t dev = device->net_dev->dev->device;
|
||||
|
||||
rndis_pkt = &message->msg.packet;
|
||||
|
||||
@ -286,88 +429,57 @@ hv_rf_receive_data(rndis_device *device, rndis_msg *message, netvsc_packet *pkt)
|
||||
/* Remove rndis header, then pass data packet up the stack */
|
||||
data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
|
||||
|
||||
/* L2 frame length, with L2 header, not including CRC */
|
||||
pkt->tot_data_buf_len = rndis_pkt->data_length;
|
||||
pkt->page_buffers[0].offset += data_offset;
|
||||
/* Buffer length now L2 frame length plus trailing junk */
|
||||
pkt->page_buffers[0].length -= data_offset;
|
||||
|
||||
pkt->is_data_pkt = TRUE;
|
||||
|
||||
pkt->vlan_tci = 0;
|
||||
|
||||
/*
|
||||
* Read the VLAN ID if supplied by the Hyper-V infrastructure.
|
||||
* Let higher-level driver code decide if it wants to use it.
|
||||
* Ignore CFI, priority for now as FreeBSD does not support these.
|
||||
*/
|
||||
if (rndis_pkt->per_pkt_info_offset != 0) {
|
||||
/* rppi struct exists; compute its address */
|
||||
rppi = (rndis_per_packet_info *)((uint8_t *)rndis_pkt +
|
||||
rndis_pkt->per_pkt_info_offset);
|
||||
/* if VLAN ppi struct, get the VLAN ID */
|
||||
if (rppi->type == ieee_8021q_info) {
|
||||
rppi_vlan_info = (ndis_8021q_info *)((uint8_t *)rppi
|
||||
+ rppi->per_packet_info_offset);
|
||||
pkt->vlan_tci = rppi_vlan_info->u1.s1.vlan_id;
|
||||
}
|
||||
pkt->tot_data_buf_len -= data_offset;
|
||||
if (pkt->tot_data_buf_len < rndis_pkt->data_length) {
|
||||
pkt->status = nvsp_status_failure;
|
||||
device_printf(dev,
|
||||
"total length %u is less than data length %u\n",
|
||||
pkt->tot_data_buf_len, rndis_pkt->data_length);
|
||||
return;
|
||||
}
|
||||
|
||||
netvsc_recv(device->net_dev->dev, pkt);
|
||||
pkt->tot_data_buf_len = rndis_pkt->data_length;
|
||||
pkt->data = (void *)((unsigned long)pkt->data + data_offset);
|
||||
|
||||
rppi_vlan_info = hv_get_ppi_data(rndis_pkt, ieee_8021q_info);
|
||||
if (rppi_vlan_info) {
|
||||
pkt->vlan_tci = rppi_vlan_info->u1.s1.vlan_id;
|
||||
} else {
|
||||
pkt->vlan_tci = 0;
|
||||
}
|
||||
|
||||
csum_info = hv_get_ppi_data(rndis_pkt, tcpip_chksum_info);
|
||||
netvsc_recv(device->net_dev->dev, pkt, csum_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter on receive
|
||||
*/
|
||||
int
|
||||
hv_rf_on_receive(struct hv_device *device, netvsc_packet *pkt)
|
||||
hv_rf_on_receive(netvsc_dev *net_dev, struct hv_device *device, netvsc_packet *pkt)
|
||||
{
|
||||
hn_softc_t *sc = device_get_softc(device->device);
|
||||
netvsc_dev *net_dev = sc->net_dev;
|
||||
rndis_device *rndis_dev;
|
||||
rndis_msg rndis_mesg;
|
||||
rndis_msg *rndis_hdr;
|
||||
|
||||
/* Make sure the rndis device state is initialized */
|
||||
if (net_dev->extension == NULL)
|
||||
if (net_dev->extension == NULL) {
|
||||
pkt->status = nvsp_status_failure;
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
rndis_dev = (rndis_device *)net_dev->extension;
|
||||
if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED)
|
||||
if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
|
||||
pkt->status = nvsp_status_failure;
|
||||
return (EINVAL);
|
||||
|
||||
/* Shift virtual page number to form virtual page address */
|
||||
rndis_hdr = (rndis_msg *)(uintptr_t)(pkt->page_buffers[0].pfn << PAGE_SHIFT);
|
||||
|
||||
rndis_hdr = (void *)((unsigned long)rndis_hdr
|
||||
+ pkt->page_buffers[0].offset);
|
||||
|
||||
/*
|
||||
* Make sure we got a valid rndis message
|
||||
* Fixme: There seems to be a bug in set completion msg where
|
||||
* its msg_len is 16 bytes but the byte_count field in the
|
||||
* xfer page range shows 52 bytes
|
||||
*/
|
||||
#if 0
|
||||
if (pkt->tot_data_buf_len != rndis_hdr->msg_len) {
|
||||
DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u "
|
||||
"bytes got %u)... dropping this message!",
|
||||
rndis_hdr->msg_len, pkt->tot_data_buf_len);
|
||||
DPRINT_EXIT(NETVSC);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
memcpy(&rndis_mesg, rndis_hdr,
|
||||
(rndis_hdr->msg_len > sizeof(rndis_msg)) ?
|
||||
sizeof(rndis_msg) : rndis_hdr->msg_len);
|
||||
rndis_hdr = pkt->data;
|
||||
|
||||
switch (rndis_mesg.ndis_msg_type) {
|
||||
switch (rndis_hdr->ndis_msg_type) {
|
||||
|
||||
/* data message */
|
||||
case REMOTE_NDIS_PACKET_MSG:
|
||||
hv_rf_receive_data(rndis_dev, &rndis_mesg, pkt);
|
||||
hv_rf_receive_data(rndis_dev, rndis_hdr, pkt);
|
||||
break;
|
||||
/* completion messages */
|
||||
case REMOTE_NDIS_INITIALIZE_CMPLT:
|
||||
@ -375,15 +487,15 @@ hv_rf_on_receive(struct hv_device *device, netvsc_packet *pkt)
|
||||
case REMOTE_NDIS_SET_CMPLT:
|
||||
case REMOTE_NDIS_RESET_CMPLT:
|
||||
case REMOTE_NDIS_KEEPALIVE_CMPLT:
|
||||
hv_rf_receive_response(rndis_dev, &rndis_mesg);
|
||||
hv_rf_receive_response(rndis_dev, rndis_hdr);
|
||||
break;
|
||||
/* notification message */
|
||||
case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
||||
hv_rf_receive_indicate_status(rndis_dev, &rndis_mesg);
|
||||
hv_rf_receive_indicate_status(rndis_dev, rndis_hdr);
|
||||
break;
|
||||
default:
|
||||
printf("hv_rf_on_receive(): Unknown msg_type 0x%x\n",
|
||||
rndis_mesg.ndis_msg_type);
|
||||
rndis_hdr->ndis_msg_type);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -711,7 +823,9 @@ hv_rf_on_device_add(struct hv_device *device, void *additl_info)
|
||||
int ret;
|
||||
netvsc_dev *net_dev;
|
||||
rndis_device *rndis_dev;
|
||||
rndis_offload_params offloads;
|
||||
netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
|
||||
device_t dev = device->device;
|
||||
|
||||
rndis_dev = hv_get_rndis_device();
|
||||
if (rndis_dev == NULL) {
|
||||
@ -752,6 +866,22 @@ hv_rf_on_device_add(struct hv_device *device, void *additl_info)
|
||||
if (ret != 0) {
|
||||
/* TODO: shut down rndis device and the channel */
|
||||
}
|
||||
|
||||
/* config csum offload and send request to host */
|
||||
memset(&offloads, 0, sizeof(offloads));
|
||||
offloads.ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
|
||||
offloads.tcp_ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
|
||||
offloads.udp_ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
|
||||
offloads.tcp_ipv6_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
|
||||
offloads.udp_ipv6_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
|
||||
offloads.lso_v2_ipv4 = RNDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
|
||||
|
||||
ret = hv_rf_send_offload_request(device, &offloads);
|
||||
if (ret != 0) {
|
||||
/* TODO: shut down rndis device and the channel */
|
||||
device_printf(dev,
|
||||
"hv_rf_send_offload_request failed, ret=%d\n", ret);
|
||||
}
|
||||
|
||||
memcpy(dev_info->mac_addr, rndis_dev->hw_mac_addr, HW_MACADDR_LEN);
|
||||
|
||||
@ -809,103 +939,6 @@ hv_rf_on_close(struct hv_device *device)
|
||||
return (hv_rf_close_device((rndis_device *)net_dev->extension));
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter on send
|
||||
*/
|
||||
int
|
||||
hv_rf_on_send(struct hv_device *device, netvsc_packet *pkt)
|
||||
{
|
||||
rndis_filter_packet *filter_pkt;
|
||||
rndis_msg *rndis_mesg;
|
||||
rndis_packet *rndis_pkt;
|
||||
rndis_per_packet_info *rppi;
|
||||
ndis_8021q_info *rppi_vlan_info;
|
||||
uint32_t rndis_msg_size;
|
||||
int ret = 0;
|
||||
|
||||
/* Add the rndis header */
|
||||
filter_pkt = (rndis_filter_packet *)pkt->extension;
|
||||
|
||||
memset(filter_pkt, 0, sizeof(rndis_filter_packet));
|
||||
|
||||
rndis_mesg = &filter_pkt->message;
|
||||
rndis_msg_size = RNDIS_MESSAGE_SIZE(rndis_packet);
|
||||
|
||||
if (pkt->vlan_tci != 0) {
|
||||
rndis_msg_size += sizeof(rndis_per_packet_info) +
|
||||
sizeof(ndis_8021q_info);
|
||||
}
|
||||
|
||||
rndis_mesg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
|
||||
rndis_mesg->msg_len = pkt->tot_data_buf_len + rndis_msg_size;
|
||||
|
||||
rndis_pkt = &rndis_mesg->msg.packet;
|
||||
rndis_pkt->data_offset = sizeof(rndis_packet);
|
||||
rndis_pkt->data_length = pkt->tot_data_buf_len;
|
||||
|
||||
pkt->is_data_pkt = TRUE;
|
||||
pkt->page_buffers[0].pfn = hv_get_phys_addr(rndis_mesg) >> PAGE_SHIFT;
|
||||
pkt->page_buffers[0].offset =
|
||||
(unsigned long)rndis_mesg & (PAGE_SIZE - 1);
|
||||
pkt->page_buffers[0].length = rndis_msg_size;
|
||||
|
||||
/* Save the packet context */
|
||||
filter_pkt->completion_context =
|
||||
pkt->compl.send.send_completion_context;
|
||||
|
||||
/* Use ours */
|
||||
pkt->compl.send.on_send_completion = hv_rf_on_send_completion;
|
||||
pkt->compl.send.send_completion_context = filter_pkt;
|
||||
|
||||
/*
|
||||
* If there is a VLAN tag, we need to set up some additional
|
||||
* fields so the Hyper-V infrastructure will stuff the VLAN tag
|
||||
* into the frame.
|
||||
*/
|
||||
if (pkt->vlan_tci != 0) {
|
||||
/* Move data offset past end of rppi + VLAN structs */
|
||||
rndis_pkt->data_offset += sizeof(rndis_per_packet_info) +
|
||||
sizeof(ndis_8021q_info);
|
||||
|
||||
/* must be set when we have rppi, VLAN info */
|
||||
rndis_pkt->per_pkt_info_offset = sizeof(rndis_packet);
|
||||
rndis_pkt->per_pkt_info_length = sizeof(rndis_per_packet_info) +
|
||||
sizeof(ndis_8021q_info);
|
||||
|
||||
/* rppi immediately follows rndis_pkt */
|
||||
rppi = (rndis_per_packet_info *)(rndis_pkt + 1);
|
||||
rppi->size = sizeof(rndis_per_packet_info) +
|
||||
sizeof(ndis_8021q_info);
|
||||
rppi->type = ieee_8021q_info;
|
||||
rppi->per_packet_info_offset = sizeof(rndis_per_packet_info);
|
||||
|
||||
/* VLAN info immediately follows rppi struct */
|
||||
rppi_vlan_info = (ndis_8021q_info *)(rppi + 1);
|
||||
/* FreeBSD does not support CFI or priority */
|
||||
rppi_vlan_info->u1.s1.vlan_id = pkt->vlan_tci & 0xfff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke netvsc send. If return status is bad, the caller now
|
||||
* resets the context pointers before retrying.
|
||||
*/
|
||||
ret = hv_nv_on_send(device, pkt);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter on send completion callback
|
||||
*/
|
||||
static void
|
||||
hv_rf_on_send_completion(void *context)
|
||||
{
|
||||
rndis_filter_packet *filter_pkt = (rndis_filter_packet *)context;
|
||||
|
||||
/* Pass it back to the original handler */
|
||||
netvsc_xmit_completion(filter_pkt->completion_context);
|
||||
}
|
||||
|
||||
/*
|
||||
* RNDIS filter on send request completion callback
|
||||
*/
|
||||
|
@ -24,6 +24,8 @@
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __HV_RNDIS_FILTER_H__
|
||||
@ -90,27 +92,16 @@ typedef struct rndis_device_ {
|
||||
uint8_t hw_mac_addr[HW_MACADDR_LEN];
|
||||
} rndis_device;
|
||||
|
||||
typedef struct rndis_filter_packet_ {
|
||||
void *completion_context;
|
||||
/* No longer used */
|
||||
pfn_on_send_rx_completion on_completion;
|
||||
|
||||
rndis_msg message;
|
||||
} rndis_filter_packet;
|
||||
|
||||
|
||||
/*
|
||||
* Externs
|
||||
*/
|
||||
|
||||
extern int hv_rf_on_receive(struct hv_device *device, netvsc_packet *pkt);
|
||||
extern int hv_rf_on_device_add(struct hv_device *device, void *additl_info);
|
||||
extern int hv_rf_on_device_remove(struct hv_device *device,
|
||||
boolean_t destroy_channel);
|
||||
extern int hv_rf_on_open(struct hv_device *device);
|
||||
extern int hv_rf_on_close(struct hv_device *device);
|
||||
extern int hv_rf_on_send(struct hv_device *device, netvsc_packet *pkt);
|
||||
|
||||
int hv_rf_on_receive(netvsc_dev *net_dev,
|
||||
struct hv_device *device, netvsc_packet *pkt);
|
||||
int hv_rf_on_device_add(struct hv_device *device, void *additl_info);
|
||||
int hv_rf_on_device_remove(struct hv_device *device, boolean_t destroy_channel);
|
||||
int hv_rf_on_open(struct hv_device *device);
|
||||
int hv_rf_on_close(struct hv_device *device);
|
||||
|
||||
#endif /* __HV_RNDIS_FILTER_H__ */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user