Changeset View
Standalone View
sys/dev/hyperv/netvsc/hv_net_vsc.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
* Forward declarations | * Forward declarations | ||||
*/ | */ | ||||
static void hv_nv_on_channel_callback(void *context); | static void hv_nv_on_channel_callback(void *context); | ||||
static int hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device); | static int hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device); | ||||
static int hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device); | 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_send_buffer(netvsc_dev *net_dev); | ||||
static int hv_nv_destroy_rx_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 int hv_nv_connect_to_vsp(struct hv_device *device); | ||||
static void hv_nv_on_send_completion(struct hv_device *device, | static void hv_nv_on_send_completion(netvsc_dev *net_dev, | ||||
struct hv_device *device, | |||||
hv_vm_packet_descriptor *pkt); | hv_vm_packet_descriptor *pkt); | ||||
static void hv_nv_on_receive(struct hv_device *device, | static void hv_nv_on_receive(netvsc_dev *net_dev, | ||||
struct hv_device *device, | |||||
hv_vm_packet_descriptor *pkt); | hv_vm_packet_descriptor *pkt); | ||||
royger: This should be aligned using 4 spaces after a line break. | |||||
static void hv_nv_send_receive_completion(struct hv_device *device, | |||||
uint64_t tid); | |||||
/* | /* | ||||
* | * | ||||
*/ | */ | ||||
static inline netvsc_dev * | static inline netvsc_dev * | ||||
hv_nv_alloc_net_device(struct hv_device *device) | hv_nv_alloc_net_device(struct hv_device *device) | ||||
{ | { | ||||
netvsc_dev *net_dev; | netvsc_dev *net_dev; | ||||
hn_softc_t *sc = device_get_softc(device->device); | hn_softc_t *sc = device_get_softc(device->device); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | hv_nv_get_inbound_net_device(struct hv_device *device) | ||||
*/ | */ | ||||
if (net_dev->destroy && net_dev->num_outstanding_sends == 0) { | if (net_dev->destroy && net_dev->num_outstanding_sends == 0) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
return (net_dev); | return (net_dev); | ||||
} | } | ||||
extern int | |||||
Done Inline Actionsextern in function definition??? royger: extern in function definition??? | |||||
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; | |||||
Done Inline ActionsParentheses around return value. royger: Parentheses around return value. | |||||
} | |||||
if (synch_test_and_set_bit(idx, &bitsmap[i])) { | |||||
Done Inline ActionsThis is defined inside of the Xen headers, you might want to use atomic_testandset_long in the machine/atomic.h header instead. royger: This is defined inside of the Xen headers, you might want to use atomic_testandset_long in the… | |||||
Not Done Inline ActionsIs "bitsmap" shared between the VM and another entity on the system (another VM or the hypervisor)? If that's the case please ignore my previous comment and keep using the synch_* functions, or else it won't work properly when used on a kernel without SMP support. royger: Is "bitsmap" shared between the VM and another entity on the system (another VM or the… | |||||
Not Done Inline ActionsThanks so much Roger! I didn't realize it. You are absolutely right. It just uses MPLOCK which could cause problem in UP VMs. I will change it back to use synch_* as it is likely this bitmap is being shared by hypervisor and VMs. whu: Thanks so much Roger! I didn't realize it. You are absolutely right. It just uses MPLOCK which… | |||||
continue; | |||||
} | |||||
Done Inline ActionsNo need for braces in single line statements. royger: No need for braces in single line statements. | |||||
ret = i * BITS_PER_LONG + idx; | |||||
break; | |||||
} | |||||
return ret; | |||||
Done Inline ActionsParentheses around return value. royger: Parentheses around return value. | |||||
} | |||||
/* | /* | ||||
* Net VSC initialize receive buffer with net VSP | * Net VSC initialize receive buffer with net VSP | ||||
* | * | ||||
* Net VSP: Network virtual services client, also known as the | * Net VSP: Network virtual services client, also known as the | ||||
* Hyper-V extensible switch and the synthetic data path. | * Hyper-V extensible switch and the synthetic data path. | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device) | hv_nv_init_rx_buffer_with_net_vsp(struct hv_device *device) | ||||
{ | { | ||||
netvsc_dev *net_dev; | netvsc_dev *net_dev; | ||||
nvsp_msg *init_pkt; | nvsp_msg *init_pkt; | ||||
int ret = 0; | int ret = 0; | ||||
net_dev = hv_nv_get_outbound_net_device(device); | net_dev = hv_nv_get_outbound_net_device(device); | ||||
if (!net_dev) { | if (!net_dev) { | ||||
return (ENODEV); | 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_DEVBUF, | ||||
M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); | M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); | ||||
if (net_dev->rx_buf == NULL) { | if (net_dev->rx_buf == NULL) { | ||||
Done Inline Actionscontigmalloc assumes M_WAITOK unless M_NOWAIT is specified, so you can drop the check against NULL. royger: contigmalloc assumes M_WAITOK unless M_NOWAIT is specified, so you can drop the check against… | |||||
ret = ENOMEM; | ret = ENOMEM; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* | /* | ||||
* Establish the GPADL handle for this buffer on this channel. | * Establish the GPADL handle for this buffer on this channel. | ||||
* Note: This call uses the vmbus connection rather than the | * Note: This call uses the vmbus connection rather than the | ||||
* channel to establish the gpadl handle. | * channel to establish the gpadl handle. | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | hv_nv_init_send_buffer_with_net_vsp(struct hv_device *device) | ||||
} | } | ||||
/* | /* | ||||
* Establish the gpadl handle for this buffer on this channel. | * Establish the gpadl handle for this buffer on this channel. | ||||
* Note: This call uses the vmbus connection rather than the | * Note: This call uses the vmbus connection rather than the | ||||
* channel to establish the gpadl handle. | * channel to establish the gpadl handle. | ||||
*/ | */ | ||||
ret = hv_vmbus_channel_establish_gpadl(device->channel, | 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, | ||||
Done Inline ActionsStray change, or is it an alignment fix? Sorry but phabric is quite crappy with tabs vs spaces. royger: Stray change, or is it an alignment fix? Sorry but phabric is quite crappy with tabs vs spaces. | |||||
&net_dev->send_buf_gpadl_handle); | &net_dev->send_buf_gpadl_handle); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* Notify the NetVsp of the gpadl handle */ | /* Notify the NetVsp of the gpadl handle */ | ||||
init_pkt = &net_dev->channel_init_packet; | init_pkt = &net_dev->channel_init_packet; | ||||
memset(init_pkt, 0, sizeof(nvsp_msg)); | memset(init_pkt, 0, sizeof(nvsp_msg)); | ||||
init_pkt->hdr.msg_type = nvsp_msg_1_type_send_send_buf; | init_pkt->hdr.msg_type = nvsp_msg_1_type_send_send_buf; | ||||
init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle = | init_pkt->msgs.vers_1_msgs.send_rx_buf.gpadl_handle = | ||||
net_dev->send_buf_gpadl_handle; | net_dev->send_buf_gpadl_handle; | ||||
init_pkt->msgs.vers_1_msgs.send_rx_buf.id = | init_pkt->msgs.vers_1_msgs.send_rx_buf.id = | ||||
NETVSC_SEND_BUFFER_ID; | NETVSC_SEND_BUFFER_ID; | ||||
/* Send the gpadl notification request */ | /* Send the gpadl notification request */ | ||||
ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, | ret = hv_vmbus_channel_send_packet(device->channel, init_pkt, | ||||
sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt, | sizeof(nvsp_msg), (uint64_t)(uintptr_t)init_pkt, | ||||
Done Inline Actions2 casts? Why not use uint64_t directly? royger: 2 casts? Why not use uint64_t directly? | |||||
HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, | HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, | ||||
HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
sema_wait(&net_dev->channel_init_sema); | sema_wait(&net_dev->channel_init_sema); | ||||
/* Check the response */ | /* Check the response */ | ||||
if (init_pkt->msgs.vers_1_msgs.send_send_buf_complete.status | if (init_pkt->msgs.vers_1_msgs.send_send_buf_complete.status | ||||
!= nvsp_status_success) { | != nvsp_status_success) { | ||||
ret = EINVAL; | ret = EINVAL; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
net_dev->send_section_size = | net_dev->send_section_size = | ||||
init_pkt->msgs.vers_1_msgs.send_send_buf_complete.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 = | |||||
(net_dev->send_section_count + BITS_PER_LONG - 1) / | |||||
BITS_PER_LONG; | |||||
Done Inline Actionsnet_dev->bitsmap_words = howmany(net_dev->send_section_count, BITS_PER_LONG); royger: ```
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_DEVBUF, | |||||
Done Inline ActionsI see that you use M_DEVBUF in the driver, could you create a new tag to be used by the net driver? M_NETVSC sounds like a suitable name. royger: I see that you use M_DEVBUF in the driver, could you create a new tag to be used by the net… | |||||
Done Inline ActionsHmm.... why do we need to create a new one while the existing M_DEVBUF could serve the purpose? whu: Hmm.... why do we need to create a new one while the existing M_DEVBUF could serve the purpose? | |||||
Done Inline ActionsSo that when you use vmstat -m it's not listed as a generic "devbuf" allocation. It's quite helpful when debugging memory leaks or track memory usage of each driver. royger: So that when you use vmstat -m it's not listed as a generic "devbuf" allocation. It's quite… | |||||
M_NOWAIT | M_ZERO); | |||||
if (NULL == net_dev->send_section_bitsmap) { | |||||
ret = ENOMEM; | |||||
goto cleanup; | |||||
} | |||||
goto exit; | goto exit; | ||||
cleanup: | cleanup: | ||||
hv_nv_destroy_send_buffer(net_dev); | hv_nv_destroy_send_buffer(net_dev); | ||||
exit: | exit: | ||||
return (ret); | return (ret); | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | hv_nv_destroy_send_buffer(netvsc_dev *net_dev) | ||||
} | } | ||||
if (net_dev->send_buf) { | if (net_dev->send_buf) { | ||||
/* Free up the receive buffer */ | /* 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_DEVBUF); | ||||
net_dev->send_buf = NULL; | net_dev->send_buf = NULL; | ||||
} | } | ||||
if (net_dev->send_section_bitsmap) { | |||||
free(net_dev->send_section_bitsmap, M_DEVBUF); | |||||
} | |||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Attempt to negotiate the caller-specified NVSP version | * Attempt to negotiate the caller-specified NVSP version | ||||
* | * | ||||
* For NVSP v2, Server 2008 R2 does not set | * For NVSP v2, Server 2008 R2 does not set | ||||
* init_pkt->msgs.init_msgs.init_compl.negotiated_prot_vers | * init_pkt->msgs.init_msgs.init_compl.negotiated_prot_vers | ||||
* to the negotiated version, so we cannot rely on that. | * to the negotiated version, so we cannot rely on that. | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_negotiate_nvsp_protocol(struct hv_device *device, netvsc_dev *net_dev, | hv_nv_negotiate_nvsp_protocol(struct hv_device *device, netvsc_dev *net_dev, | ||||
uint32_t nvsp_ver) | uint32_t nvsp_ver) | ||||
Done Inline ActionsAfter a line break align using 4 spaces. royger: After a line break align using 4 spaces. | |||||
{ | { | ||||
nvsp_msg *init_pkt; | nvsp_msg *init_pkt; | ||||
int ret; | int ret; | ||||
init_pkt = &net_dev->channel_init_packet; | init_pkt = &net_dev->channel_init_packet; | ||||
memset(init_pkt, 0, sizeof(nvsp_msg)); | memset(init_pkt, 0, sizeof(nvsp_msg)); | ||||
init_pkt->hdr.msg_type = nvsp_msg_type_init; | init_pkt->hdr.msg_type = nvsp_msg_type_init; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Net VSC connect to VSP | * Net VSC connect to VSP | ||||
*/ | */ | ||||
static int | static int | ||||
hv_nv_connect_to_vsp(struct hv_device *device) | hv_nv_connect_to_vsp(struct hv_device *device) | ||||
{ | { | ||||
netvsc_dev *net_dev; | netvsc_dev *net_dev; | ||||
nvsp_msg *init_pkt; | nvsp_msg *init_pkt; | ||||
uint32_t nvsp_vers; | |||||
uint32_t ndis_version; | 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 = 4; | |||||
Not Done Inline ActionsIf this always has to be the number of items in the static array initialized above I would rather use: int protocol_number = nitems(protocol_list); royger: If this always has to be the number of items in the static array initialized above I would… | |||||
int ret = 0; | int ret = 0; | ||||
device_t dev = device->device; | device_t dev = device->device; | ||||
hn_softc_t *sc = device_get_softc(dev); | hn_softc_t *sc = device_get_softc(dev); | ||||
struct ifnet *ifp = sc->hn_ifp; | struct ifnet *ifp = sc->hn_ifp; | ||||
net_dev = hv_nv_get_outbound_net_device(device); | net_dev = hv_nv_get_outbound_net_device(device); | ||||
if (!net_dev) { | if (!net_dev) { | ||||
return (ENODEV); | return (ENODEV); | ||||
} | } | ||||
/* | /* | ||||
* Negotiate the NVSP version. Try NVSP v2 first. | * Negotiate the NVSP version. Try the latest NVSP first. | ||||
*/ | */ | ||||
nvsp_vers = NVSP_PROTOCOL_VERSION_2; | for (i = protocol_number - 1; i >= 0; i--) | ||||
ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); | if (hv_nv_negotiate_nvsp_protocol(device, net_dev, | ||||
if (ret != 0) { | protocol_list[i]) == 0) { | ||||
/* NVSP v2 failed, try NVSP v1 */ | net_dev->nvsp_version = protocol_list[i]; | ||||
nvsp_vers = NVSP_PROTOCOL_VERSION_1; | if (bootverbose) | ||||
ret = hv_nv_negotiate_nvsp_protocol(device, net_dev, nvsp_vers); | printf("Netvsc: got version 0x%x\n", | ||||
Done Inline Actionsdevice_printf? royger: device_printf? | |||||
if (ret != 0) { | net_dev->nvsp_version); | ||||
/* NVSP v1 failed, return bad status */ | break; | ||||
Done Inline ActionsAdding braces in the for case would make this code easier to read (not that they are really needed). royger: Adding braces in the for case would make this code easier to read (not that they are really… | |||||
return (ret); | |||||
} | } | ||||
if (i < 0) { | |||||
if (bootverbose) | |||||
printf("Netvsc: failed to negotiate a valid " | |||||
Done Inline ActionsCan you use device_printf here? royger: Can you use device_printf here? | |||||
"protocol.\n"); | |||||
return (EPROTO); | |||||
} | } | ||||
net_dev->nvsp_version = nvsp_vers; | |||||
/* | /* | ||||
* Set the MTU if supported by this NVSP protocol version | * Set the MTU if supported by this NVSP protocol version | ||||
* This needs to be right after the NVSP init message per Haiyang | * 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); | ret = hv_nv_send_ndis_config(device, ifp->if_mtu); | ||||
/* | /* | ||||
* Send the NDIS version | * Send the NDIS version | ||||
*/ | */ | ||||
init_pkt = &net_dev->channel_init_packet; | init_pkt = &net_dev->channel_init_packet; | ||||
memset(init_pkt, 0, sizeof(nvsp_msg)); | memset(init_pkt, 0, sizeof(nvsp_msg)); | ||||
/* | if (net_dev->nvsp_version <= NVSP_PROTOCOL_VERSION_4) { | ||||
* Updated to version 5.1, minimum, for VLAN per Haiyang | ndis_version = NDIS_VERSION_6_1; | ||||
*/ | } else { | ||||
ndis_version = NDIS_VERSION; | ndis_version = NDIS_VERSION_6_30; | ||||
} | |||||
init_pkt->hdr.msg_type = nvsp_msg_1_type_send_ndis_vers; | init_pkt->hdr.msg_type = nvsp_msg_1_type_send_ndis_vers; | ||||
init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_major_vers = | init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_major_vers = | ||||
(ndis_version & 0xFFFF0000) >> 16; | (ndis_version & 0xFFFF0000) >> 16; | ||||
init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_minor_vers = | init_pkt->msgs.vers_1_msgs.send_ndis_vers.ndis_minor_vers = | ||||
ndis_version & 0xFFFF; | ndis_version & 0xFFFF; | ||||
/* Send the init request */ | /* Send the init request */ | ||||
Show All 35 Lines | |||||
* Net VSC on device add | * Net VSC on device add | ||||
* | * | ||||
* Callback when the device belonging to this driver is added | * Callback when the device belonging to this driver is added | ||||
*/ | */ | ||||
netvsc_dev * | netvsc_dev * | ||||
hv_nv_on_device_add(struct hv_device *device, void *additional_info) | hv_nv_on_device_add(struct hv_device *device, void *additional_info) | ||||
{ | { | ||||
netvsc_dev *net_dev; | netvsc_dev *net_dev; | ||||
netvsc_packet *packet; | int ret = 0; | ||||
netvsc_packet *next_packet; | |||||
int i, ret = 0; | |||||
net_dev = hv_nv_alloc_net_device(device); | net_dev = hv_nv_alloc_net_device(device); | ||||
if (!net_dev) | if (!net_dev) | ||||
goto cleanup; | goto cleanup; | ||||
/* Initialize the NetVSC channel extension */ | /* Initialize the NetVSC channel extension */ | ||||
net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; | 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; | 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"); | sema_init(&net_dev->channel_init_sema, 0, "netdev_sema"); | ||||
/* | /* | ||||
* Open the channel | * Open the channel | ||||
*/ | */ | ||||
ret = hv_vmbus_channel_open(device->channel, | ret = hv_vmbus_channel_open(device->channel, | ||||
NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE, | NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE, | ||||
NULL, 0, hv_nv_on_channel_callback, device); | NULL, 0, hv_nv_on_channel_callback, device); | ||||
Show All 16 Lines | |||||
cleanup: | cleanup: | ||||
/* | /* | ||||
* Free the packet buffers on the netvsc device packet queue. | * Free the packet buffers on the netvsc device packet queue. | ||||
* Release other resources. | * Release other resources. | ||||
*/ | */ | ||||
if (net_dev) { | if (net_dev) { | ||||
sema_destroy(&net_dev->channel_init_sema); | 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_DEVBUF); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC on device remove | * Net VSC on device remove | ||||
*/ | */ | ||||
int | int | ||||
hv_nv_on_device_remove(struct hv_device *device, boolean_t destroy_channel) | 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); | hn_softc_t *sc = device_get_softc(device->device); | ||||
netvsc_dev *net_dev = sc->net_dev;; | netvsc_dev *net_dev = sc->net_dev;; | ||||
/* Stop outbound traffic ie sends and receives completions */ | /* Stop outbound traffic ie sends and receives completions */ | ||||
mtx_lock(&device->channel->inbound_lock); | mtx_lock(&device->channel->inbound_lock); | ||||
net_dev->destroy = TRUE; | net_dev->destroy = TRUE; | ||||
mtx_unlock(&device->channel->inbound_lock); | mtx_unlock(&device->channel->inbound_lock); | ||||
Show All 10 Lines | hv_nv_on_device_remove(struct hv_device *device, boolean_t destroy_channel) | ||||
if (!destroy_channel) { | if (!destroy_channel) { | ||||
device->channel->state = | device->channel->state = | ||||
HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE; | HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE; | ||||
} | } | ||||
hv_vmbus_channel_close(device->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); | sema_destroy(&net_dev->channel_init_sema); | ||||
free(net_dev, M_DEVBUF); | free(net_dev, M_DEVBUF); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Net VSC on send completion | * Net VSC on send completion | ||||
*/ | */ | ||||
static void | static void | ||||
hv_nv_on_send_completion(struct hv_device *device, hv_vm_packet_descriptor *pkt) | hv_nv_on_send_completion(netvsc_dev *net_dev, | ||||
struct hv_device *device, | |||||
hv_vm_packet_descriptor *pkt) | |||||
Done Inline ActionsAlign using 4 spaces after a line break. royger: Align using 4 spaces after a line break. | |||||
{ | { | ||||
netvsc_dev *net_dev; | |||||
nvsp_msg *nvsp_msg_pkt; | nvsp_msg *nvsp_msg_pkt; | ||||
netvsc_packet *net_vsc_pkt; | netvsc_packet *net_vsc_pkt; | ||||
net_dev = hv_nv_get_inbound_net_device(device); | |||||
if (!net_dev) { | |||||
return; | |||||
} | |||||
nvsp_msg_pkt = | nvsp_msg_pkt = | ||||
(nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3)); | (nvsp_msg *)((unsigned long)pkt + (pkt->data_offset8 << 3)); | ||||
if (nvsp_msg_pkt->hdr.msg_type == nvsp_msg_type_init_complete | if (nvsp_msg_pkt->hdr.msg_type == nvsp_msg_type_init_complete | ||||
|| nvsp_msg_pkt->hdr.msg_type | || nvsp_msg_pkt->hdr.msg_type | ||||
== nvsp_msg_1_type_send_rx_buf_complete | == nvsp_msg_1_type_send_rx_buf_complete | ||||
|| nvsp_msg_pkt->hdr.msg_type | || nvsp_msg_pkt->hdr.msg_type | ||||
== nvsp_msg_1_type_send_send_buf_complete) { | == nvsp_msg_1_type_send_send_buf_complete) { | ||||
/* Copy the response back */ | /* Copy the response back */ | ||||
memcpy(&net_dev->channel_init_packet, nvsp_msg_pkt, | memcpy(&net_dev->channel_init_packet, nvsp_msg_pkt, | ||||
sizeof(nvsp_msg)); | sizeof(nvsp_msg)); | ||||
sema_post(&net_dev->channel_init_sema); | sema_post(&net_dev->channel_init_sema); | ||||
} else if (nvsp_msg_pkt->hdr.msg_type == | } 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 */ | /* Get the send context */ | ||||
net_vsc_pkt = | net_vsc_pkt = | ||||
(netvsc_packet *)(unsigned long)pkt->transaction_id; | (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, | |||||
Not Done Inline ActionsAgain this is defined in Xen headers. You should be able to find a suitable equivalent in machine/atomic.h. I plan to remove those Xen specific bitops and replace them with the FreeBSD native ones in Xen code. royger: Again this is defined in Xen headers. You should be able to find a suitable equivalent in… | |||||
Not Done Inline ActionsI don't find similar one in atomic.h using 'btcl' instruction. whu: I don't find similar one in atomic.h using 'btcl' instruction. | |||||
Not Done Inline ActionsRight, this is missing from the native FreeBSD atomic ops. I've also realized that on UP kernels (kernels compiled without SMP) the native FreeBSD atomic ops drop the "lock" prefix, which means they cannot be safely used inside of a VM if the memory is shared between another VM or the hypervisor. I guess it's fine to use the synch_* ones for now. I will try to come up with a patch to properly remove them when I have some time... royger: Right, this is missing from the native FreeBSD atomic ops. I've also realized that on UP… | |||||
net_dev->send_section_bitsmap); | |||||
} | |||||
/* Notify the layer above us */ | /* Notify the layer above us */ | ||||
net_vsc_pkt->compl.send.on_send_completion( | net_vsc_pkt->compl.send.on_send_completion( | ||||
net_vsc_pkt->compl.send.send_completion_context); | net_vsc_pkt->compl.send.send_completion_context); | ||||
} | |||||
atomic_subtract_int(&net_dev->num_outstanding_sends, 1); | atomic_subtract_int(&net_dev->num_outstanding_sends, 1); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Net VSC on send | * Net VSC on send | ||||
* Sends a packet on the specified Hyper-V device. | * Sends a packet on the specified Hyper-V device. | ||||
* Returns 0 on success, non-zero on failure. | * Returns 0 on success, non-zero on failure. | ||||
Show All 13 Lines | hv_nv_on_send(struct hv_device *device, netvsc_packet *pkt) | ||||
if (pkt->is_data_pkt) { | if (pkt->is_data_pkt) { | ||||
/* 0 is RMC_DATA */ | /* 0 is RMC_DATA */ | ||||
send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 0; | send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 0; | ||||
} else { | } else { | ||||
/* 1 is RMC_CONTROL */ | /* 1 is RMC_CONTROL */ | ||||
send_msg.msgs.vers_1_msgs.send_rndis_pkt.chan_type = 1; | 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 = | send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_idx = | ||||
0xFFFFFFFF; | pkt->send_buf_section_idx; | ||||
send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size = 0; | send_msg.msgs.vers_1_msgs.send_rndis_pkt.send_buf_section_size = | ||||
pkt->send_buf_section_size; | |||||
if (pkt->page_buf_count) { | if (pkt->page_buf_count) { | ||||
ret = hv_vmbus_channel_send_packet_pagebuffer(device->channel, | ret = hv_vmbus_channel_send_packet_pagebuffer(device->channel, | ||||
pkt->page_buffers, pkt->page_buf_count, | pkt->page_buffers, pkt->page_buf_count, | ||||
&send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt); | &send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt); | ||||
} else { | } else { | ||||
ret = hv_vmbus_channel_send_packet(device->channel, | ret = hv_vmbus_channel_send_packet(device->channel, | ||||
&send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt, | &send_msg, sizeof(nvsp_msg), (uint64_t)(uintptr_t)pkt, | ||||
Show All 9 Lines | |||||
} | } | ||||
/* | /* | ||||
* Net VSC on receive | * Net VSC on receive | ||||
* | * | ||||
* In the FreeBSD Hyper-V virtual world, this function deals exclusively | * In the FreeBSD Hyper-V virtual world, this function deals exclusively | ||||
* with virtual addresses. | * with virtual addresses. | ||||
*/ | */ | ||||
static void | static void | ||||
hv_nv_on_receive(struct hv_device *device, hv_vm_packet_descriptor *pkt) | hv_nv_on_receive | ||||
( | |||||
netvsc_dev *net_dev, | |||||
struct hv_device *device, | |||||
hv_vm_packet_descriptor *pkt | |||||
) | |||||
Done Inline ActionsThis is not FreeBSD coding style, please fix it. royger: This is not FreeBSD coding style, please fix it. | |||||
{ | { | ||||
netvsc_dev *net_dev; | |||||
hv_vm_transfer_page_packet_header *vm_xfer_page_pkt; | hv_vm_transfer_page_packet_header *vm_xfer_page_pkt; | ||||
nvsp_msg *nvsp_msg_pkt; | nvsp_msg *nvsp_msg_pkt; | ||||
netvsc_packet *net_vsc_pkt = NULL; | netvsc_packet vsc_pkt; | ||||
unsigned long start; | netvsc_packet *net_vsc_pkt = &vsc_pkt; | ||||
xfer_page_packet *xfer_page_pkt = NULL; | |||||
STAILQ_HEAD(PKT_LIST, netvsc_packet_) mylist_head = | |||||
STAILQ_HEAD_INITIALIZER(mylist_head); | |||||
int count = 0; | int count = 0; | ||||
int i = 0; | int i = 0; | ||||
int status = nvsp_status_success; | |||||
net_dev = hv_nv_get_inbound_net_device(device); | |||||
if (!net_dev) | |||||
return; | |||||
/* | /* | ||||
* All inbound packets other than send completion should be | * All inbound packets other than send completion should be | ||||
* xfer page packet. | * 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) { | ||||
printf("packet type %d is invalid!\n", pkt->type); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
return; | return; | ||||
} | |||||
nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt | nvsp_msg_pkt = (nvsp_msg *)((unsigned long)pkt | ||||
+ (pkt->data_offset8 << 3)); | + (pkt->data_offset8 << 3)); | ||||
/* Make sure this is a valid nvsp packet */ | /* 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) { | ||||
printf("packet hdr type %d is invalid!\n", pkt->type); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
return; | return; | ||||
} | |||||
vm_xfer_page_pkt = (hv_vm_transfer_page_packet_header *)pkt; | vm_xfer_page_pkt = (hv_vm_transfer_page_packet_header *)pkt; | ||||
if (vm_xfer_page_pkt->transfer_page_set_id | if (vm_xfer_page_pkt->transfer_page_set_id != | ||||
!= NETVSC_RECEIVE_BUFFER_ID) { | NETVSC_RECEIVE_BUFFER_ID) { | ||||
printf("transfer_page_set_id %d is invalid!\n", | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
vm_xfer_page_pkt->transfer_page_set_id); | |||||
return; | return; | ||||
} | } | ||||
STAILQ_INIT(&mylist_head); | count = vm_xfer_page_pkt->range_count; | ||||
/* | |||||
* 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; | |||||
/* 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; | 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; | |||||
/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ | |||||
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); | |||||
net_vsc_pkt->tot_data_buf_len = | net_vsc_pkt->tot_data_buf_len = | ||||
vm_xfer_page_pkt->ranges[i].byte_count; | vm_xfer_page_pkt->ranges[i].byte_count; | ||||
net_vsc_pkt->page_buf_count = 1; | |||||
net_vsc_pkt->page_buffers[0].length = | hv_rf_on_receive(net_dev, device, net_vsc_pkt); | ||||
vm_xfer_page_pkt->ranges[i].byte_count; | if (net_vsc_pkt->status != nvsp_status_success) { | ||||
status = nvsp_status_failure; | |||||
} | |||||
} | |||||
/* The virtual address of the packet in the receive buffer */ | |||||
start = ((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); | |||||
/* | |||||
* Moved completion call back here so that all received | * Moved completion call back here so that all received | ||||
* messages (not just data messages) will trigger a response | * messages (not just data messages) will trigger a response | ||||
* message back to the host. | * message back to the host. | ||||
*/ | */ | ||||
hv_nv_on_receive_completion(net_vsc_pkt); | 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 | void | ||||
hv_nv_send_receive_completion(struct hv_device *device, uint64_t tid) | hv_nv_on_receive_completion(struct hv_device *device, | ||||
uint64_t tid, | |||||
uint32_t status) | |||||
Done Inline Actions4 spaces after line break, and please place both in the same line. royger: 4 spaces after line break, and please place both in the same line. | |||||
{ | { | ||||
nvsp_msg rx_comp_msg; | nvsp_msg rx_comp_msg; | ||||
int retries = 0; | int retries = 0; | ||||
int ret = 0; | int ret = 0; | ||||
rx_comp_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt_complete; | rx_comp_msg.hdr.msg_type = nvsp_msg_1_type_send_rndis_pkt_complete; | ||||
/* Pass in the status */ | /* Pass in the status */ | ||||
rx_comp_msg.msgs.vers_1_msgs.send_rndis_pkt_complete.status = | rx_comp_msg.msgs.vers_1_msgs.send_rndis_pkt_complete.status = | ||||
nvsp_status_success; | status; | ||||
retry_send_cmplt: | retry_send_cmplt: | ||||
/* Send the completion */ | /* Send the completion */ | ||||
ret = hv_vmbus_channel_send_packet(device->channel, &rx_comp_msg, | ret = hv_vmbus_channel_send_packet(device->channel, &rx_comp_msg, | ||||
sizeof(nvsp_msg), tid, HV_VMBUS_PACKET_TYPE_COMPLETION, 0); | sizeof(nvsp_msg), tid, HV_VMBUS_PACKET_TYPE_COMPLETION, 0); | ||||
if (ret == 0) { | if (ret == 0) { | ||||
/* success */ | /* success */ | ||||
/* no-op */ | /* no-op */ | ||||
} else if (ret == EAGAIN) { | } else if (ret == EAGAIN) { | ||||
/* no more room... wait a bit and attempt to retry 3 times */ | /* no more room... wait a bit and attempt to retry 3 times */ | ||||
retries++; | retries++; | ||||
if (retries < 4) { | if (retries < 4) { | ||||
DELAY(100); | DELAY(100); | ||||
goto retry_send_cmplt; | goto 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 | * Net VSC on channel callback | ||||
*/ | */ | ||||
static void | static void | ||||
hv_nv_on_channel_callback(void *context) | hv_nv_on_channel_callback(void *context) | ||||
{ | { | ||||
/* Fixme: Magic number */ | |||||
const int net_pkt_size = 2048; | |||||
struct hv_device *device = (struct hv_device *)context; | struct hv_device *device = (struct hv_device *)context; | ||||
netvsc_dev *net_dev; | netvsc_dev *net_dev; | ||||
uint32_t bytes_rxed; | uint32_t bytes_rxed; | ||||
uint64_t request_id; | uint64_t request_id; | ||||
uint8_t *packet; | |||||
hv_vm_packet_descriptor *desc; | hv_vm_packet_descriptor *desc; | ||||
uint8_t *buffer; | uint8_t *buffer; | ||||
int bufferlen = net_pkt_size; | int bufferlen = NETVSC_PACKET_SIZE; | ||||
int ret = 0; | int ret = 0; | ||||
packet = malloc(net_pkt_size * sizeof(uint8_t), M_DEVBUF, M_NOWAIT); | |||||
if (!packet) | |||||
return; | |||||
buffer = packet; | |||||
net_dev = hv_nv_get_inbound_net_device(device); | net_dev = hv_nv_get_inbound_net_device(device); | ||||
if (net_dev == NULL) | if (net_dev == NULL) | ||||
goto out; | return; | ||||
buffer = net_dev->callback_buf; | |||||
do { | do { | ||||
ret = hv_vmbus_channel_recv_packet_raw(device->channel, | ret = hv_vmbus_channel_recv_packet_raw(device->channel, | ||||
buffer, bufferlen, &bytes_rxed, &request_id); | buffer, bufferlen, &bytes_rxed, &request_id); | ||||
if (ret == 0) { | if (ret == 0) { | ||||
if (bytes_rxed > 0) { | if (bytes_rxed > 0) { | ||||
desc = (hv_vm_packet_descriptor *)buffer; | desc = (hv_vm_packet_descriptor *)buffer; | ||||
switch (desc->type) { | switch (desc->type) { | ||||
case HV_VMBUS_PACKET_TYPE_COMPLETION: | case HV_VMBUS_PACKET_TYPE_COMPLETION: | ||||
hv_nv_on_send_completion(device, desc); | hv_nv_on_send_completion(net_dev, device, desc); | ||||
break; | break; | ||||
case HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES: | case HV_VMBUS_PACKET_TYPE_DATA_USING_TRANSFER_PAGES: | ||||
hv_nv_on_receive(device, desc); | hv_nv_on_receive(net_dev, device, desc); | ||||
break; | break; | ||||
default: | default: | ||||
printf("hv_cb recv unknow packet\n"); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
break; | break; | ||||
} | } | ||||
} else { | } else { | ||||
break; | break; | ||||
} | } | ||||
} else if (ret == ENOBUFS) { | } else if (ret == ENOBUFS) { | ||||
/* Handle large packet */ | /* Handle large packet */ | ||||
if (bufferlen > NETVSC_PACKET_SIZE){ | |||||
free(buffer, M_DEVBUF); | free(buffer, M_DEVBUF); | ||||
buffer = NULL; | |||||
} | |||||
/* alloc new buffer */ | |||||
buffer = malloc(bytes_rxed, M_DEVBUF, M_NOWAIT); | buffer = malloc(bytes_rxed, M_DEVBUF, M_NOWAIT); | ||||
if (buffer == NULL) { | if (buffer == NULL) { | ||||
printf("hv_cb malloc buffer failed, len=%u\n", bytes_rxed); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
bufferlen = 0; | |||||
break; | break; | ||||
} | } | ||||
bufferlen = bytes_rxed; | bufferlen = bytes_rxed; | ||||
} | } | ||||
} while (1); | } while (1); | ||||
Not Done Inline ActionsIMHO you should consider removing this unbound loop, using this kind of constructions can lead to infinite loops in kernel code. Can't you use ret as a condition here and avoid the breaks in the inner code? royger: IMHO you should consider removing this unbound loop, using this kind of constructions can lead… | |||||
Not Done Inline ActionsIn the cases of either (ret == 0 && bytes_rxed > 0) or (ret == ENOBUFS), we still want to keep reading the ring buffer until there is nothing left. The only case to break out of the while loop is (ret ==0 && bytes_rxed == 0). If you don't mind, I would like to maintain its current code logic to make it in consistence to its Windows and Linux counterparts. whu: In the cases of either (ret == 0 && bytes_rxed > 0) or (ret == ENOBUFS), we still want to keep… | |||||
Not Done Inline ActionsAs you wish, this was just a suggestion. At the end of day you are the one that's going to maintain the code :). royger: As you wish, this was just a suggestion. At the end of day you are the one that's going to… | |||||
out: | if (bufferlen > NETVSC_PACKET_SIZE) | ||||
free(buffer, M_DEVBUF); | free(buffer, M_DEVBUF); | ||||
} | } | ||||
This should be aligned using 4 spaces after a line break.