Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
Show First 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/if_vlan_var.h> | #include <net/if_vlan_var.h> | ||||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||||
#include <netinet/if_ether.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.h> | ||||
#include <vm/vm_param.h> | #include <vm/vm_param.h> | ||||
#include <vm/vm_kern.h> | #include <vm/vm_kern.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <machine/frame.h> | #include <machine/frame.h> | ||||
#include <machine/vmparam.h> | #include <machine/vmparam.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <machine/atomic.h> | #include <machine/atomic.h> | ||||
#include <machine/intr_machdep.h> | #include <machine/intr_machdep.h> | ||||
#include <machine/in_cksum.h> | |||||
#include <dev/hyperv/include/hyperv.h> | #include <dev/hyperv/include/hyperv.h> | ||||
#include "hv_net_vsc.h" | #include "hv_net_vsc.h" | ||||
#include "hv_rndis.h" | #include "hv_rndis.h" | ||||
#include "hv_rndis_filter.h" | #include "hv_rndis_filter.h" | ||||
/* Short for Hyper-V network interface */ | /* Short for Hyper-V network interface */ | ||||
#define NETVSC_DEVNAME "hn" | #define NETVSC_DEVNAME "hn" | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void hn_stop(hn_softc_t *sc); | static void hn_stop(hn_softc_t *sc); | ||||
static void hn_ifinit_locked(hn_softc_t *sc); | static void hn_ifinit_locked(hn_softc_t *sc); | ||||
static void hn_ifinit(void *xsc); | static void hn_ifinit(void *xsc); | ||||
static int hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); | static int hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); | ||||
static int hn_start_locked(struct ifnet *ifp); | static int hn_start_locked(struct ifnet *ifp); | ||||
static void hn_start(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: | |||||
break; | |||||
royger: IMHO I would set ret_val = TRANSPORT_TYPE_NOT_IP here so the flow is clearer. | |||||
} | |||||
return ret_val; | |||||
} | |||||
/* | /* | ||||
* NetVsc driver initialization | * NetVsc driver initialization | ||||
* Note: Filter init is no longer required | * Note: Filter init is no longer required | ||||
*/ | */ | ||||
static int | static int | ||||
netvsc_drv_init(void) | netvsc_drv_init(void) | ||||
{ | { | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | netvsc_attach(device_t dev) | ||||
IFQ_SET_MAXLEN(&ifp->if_snd, 512); | IFQ_SET_MAXLEN(&ifp->if_snd, 512); | ||||
ifp->if_snd.ifq_drv_maxlen = 511; | ifp->if_snd.ifq_drv_maxlen = 511; | ||||
IFQ_SET_READY(&ifp->if_snd); | IFQ_SET_READY(&ifp->if_snd); | ||||
/* | /* | ||||
* Tell upper layers that we support full VLAN capability. | * Tell upper layers that we support full VLAN capability. | ||||
*/ | */ | ||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ifp->if_hdrlen = sizeof(struct ether_vlan_header); | ||||
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; | ifp->if_capabilities |= | ||||
ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; | 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); | ret = hv_rf_on_device_add(device_ctx, &device_info); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
if_free(ifp); | if_free(ifp); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
if (device_info.link_state == 0) { | if (device_info.link_state == 0) { | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Start a transmit of one or more packets | * Start a transmit of one or more packets | ||||
*/ | */ | ||||
static int | static int | ||||
hn_start_locked(struct ifnet *ifp) | hn_start_locked(struct ifnet *ifp) | ||||
{ | { | ||||
hn_softc_t *sc = ifp->if_softc; | hn_softc_t *sc = ifp->if_softc; | ||||
struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); | struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev); | ||||
netvsc_dev *net_dev = sc->net_dev; | |||||
uint8_t *buf; | uint8_t *buf; | ||||
netvsc_packet *packet; | netvsc_packet *packet; | ||||
struct mbuf *m_head, *m; | struct mbuf *m_head, *m; | ||||
struct mbuf *mc_head = NULL; | 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; | |||||
Done Inline ActionsDon't add this alignment here, the reset of the local variables are aligned using a space. royger: Don't add this alignment here, the reset of the local variables are aligned using a space. | |||||
rndis_tcp_ip_csum_info *csum_info; | |||||
rndis_tcp_tso_info *tso_info; | |||||
int ether_len; | |||||
int i; | int i; | ||||
int num_frags; | int num_frags; | ||||
int len; | int len; | ||||
int xlen; | |||||
int rppi_size; | |||||
int retries = 0; | 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)) { | while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) { | ||||
IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head); | IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head); | ||||
if (m_head == NULL) { | if (m_head == NULL) { | ||||
break; | break; | ||||
} | } | ||||
len = 0; | len = 0; | ||||
num_frags = 0; | num_frags = 0; | ||||
xlen = 0; | |||||
/* Walk the mbuf list computing total length and num frags */ | /* Walk the mbuf list computing total length and num frags */ | ||||
for (m = m_head; m != NULL; m = m->m_next) { | for (m = m_head; m != NULL; m = m->m_next) { | ||||
if (m->m_len != 0) { | if (m->m_len != 0) { | ||||
num_frags++; | num_frags++; | ||||
len += m->m_len; | len += m->m_len; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Reserve the number of pages requested. Currently, | * Reserve the number of pages requested. Currently, | ||||
* one page is reserved for the message in the RNDIS | * one page is reserved for the message in the RNDIS | ||||
* filter packet | * filter packet | ||||
*/ | */ | ||||
num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS; | num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS; | ||||
/* If exceeds # page_buffers in netvsc_packet */ | /* If exceeds # page_buffers in netvsc_packet */ | ||||
if (num_frags > NETVSC_PACKET_MAXPAGE) { | if (num_frags > NETVSC_PACKET_MAXPAGE) { | ||||
m_freem(m); | printf("exceed max page buffers,%d,%d\n", | ||||
num_frags, NETVSC_PACKET_MAXPAGE); | |||||
m_freem(m_head); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
return (EINVAL); | 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 | * Allocate a buffer with space for a netvsc packet plus a | ||||
* number of reserved areas. First comes a (currently 16 | * number of reserved areas. First comes a (currently 16 | ||||
* bytes, currently unused) reserved data area. Second is | * bytes, currently unused) reserved data area. Second is | ||||
* the netvsc_packet, which includes (currently 4) page | * the netvsc_packet. Third is an area reserved for an | ||||
* buffers. Third (optional) is a rndis_per_packet_info | * rndis_filter_packet struct. Fourth (optional) is a | ||||
* struct, but only if a VLAN tag should be inserted into the | * rndis_per_packet_info struct. | ||||
* Ethernet frame by the Hyper-V infrastructure. Fourth is | |||||
* an area reserved for an rndis_filter_packet struct. | |||||
* Changed malloc to M_NOWAIT to avoid sleep under spin lock. | * Changed malloc to M_NOWAIT to avoid sleep under spin lock. | ||||
* No longer reserving extra space for page buffers, as they | * No longer reserving extra space for page buffers, as they | ||||
* are already part of the netvsc_packet. | * are already part of the netvsc_packet. | ||||
*/ | */ | ||||
buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF + | buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF + | ||||
sizeof(netvsc_packet) + rppi_size + | sizeof(netvsc_packet) + | ||||
sizeof(rndis_filter_packet), | sizeof(rndis_msg) + | ||||
RNDIS_VLAN_PPI_SIZE + | |||||
RNDIS_TSO_PPI_SIZE + | |||||
RNDIS_CSUM_PPI_SIZE, | |||||
M_DEVBUF, M_ZERO | M_NOWAIT); | M_DEVBUF, M_ZERO | M_NOWAIT); | ||||
if (buf == NULL) { | if (buf == NULL) { | ||||
m_freem(m); | printf("hn:malloc packet failed\n"); | ||||
m_freem(m_head); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
Done Inline Actionsdevice_printf royger: device_printf | |||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF); | packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF); | ||||
*(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF; | *(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF; | ||||
packet->is_data_pkt = TRUE; | |||||
/* Set up the rndis header */ | |||||
packet->page_buf_count = num_frags; | |||||
/* Initialize it from the mbuf */ | |||||
packet->tot_data_buf_len = len; | |||||
/* | /* | ||||
* extension points to the area reserved for the | * extension points to the area reserved for the | ||||
* rndis_filter_packet, which is placed just after | * rndis_filter_packet, which is placed just after | ||||
* the netvsc_packet (and rppi struct, if present; | * the netvsc_packet (and rppi struct, if present; | ||||
* length is updated later). | * length is updated later). | ||||
*/ | */ | ||||
packet->extension = packet + 1; | packet->rndis_mesg = packet + 1; | ||||
rndis_mesg = (rndis_msg *)packet->rndis_mesg; | |||||
rndis_mesg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; | |||||
/* Set up the rndis header */ | rndis_pkt = &rndis_mesg->msg.packet; | ||||
packet->page_buf_count = num_frags; | 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); | |||||
/* Initialize it from the mbuf */ | rndis_msg_size = RNDIS_MESSAGE_SIZE(rndis_packet); | ||||
packet->tot_data_buf_len = len; | |||||
/* | /* | ||||
* If the Hyper-V infrastructure needs to embed a VLAN tag, | * If the Hyper-V infrastructure needs to embed a VLAN tag, | ||||
* initialize netvsc_packet and rppi struct values as needed. | * initialize netvsc_packet and rppi struct values as needed. | ||||
*/ | */ | ||||
if (rppi_size) { | if (m_head->m_flags & M_VLANTAG) { | ||||
/* Lower layers need the VLAN TCI */ | /* | ||||
* 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; | 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 = | |||||
hv_get_phys_addr(rndis_mesg) >> PAGE_SHIFT; | |||||
packet->page_buffers[0].offset = | |||||
(unsigned long)rndis_mesg & (PAGE_SIZE - 1); | |||||
packet->page_buffers[0].length = rndis_msg_size; | |||||
/* | |||||
* Fill the page buffers with mbuf info starting at index | * Fill the page buffers with mbuf info starting at index | ||||
* HV_RF_NUM_TX_RESERVED_PAGE_BUFS. | * HV_RF_NUM_TX_RESERVED_PAGE_BUFS. | ||||
*/ | */ | ||||
i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS; | i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS; | ||||
for (m = m_head; m != NULL; m = m->m_next) { | for (m = m_head; m != NULL; m = m->m_next) { | ||||
if (m->m_len) { | if (m->m_len) { | ||||
vm_offset_t paddr = | vm_offset_t paddr = | ||||
vtophys(mtod(m, vm_offset_t)); | vtophys(mtod(m, vm_offset_t)); | ||||
packet->page_buffers[i].pfn = | packet->page_buffers[i].pfn = | ||||
paddr >> PAGE_SHIFT; | paddr >> PAGE_SHIFT; | ||||
packet->page_buffers[i].offset = | packet->page_buffers[i].offset = | ||||
paddr & (PAGE_SIZE - 1); | paddr & (PAGE_SIZE - 1); | ||||
packet->page_buffers[i].length = m->m_len; | packet->page_buffers[i].length = m->m_len; | ||||
i++; | i++; | ||||
} | } | ||||
} | } | ||||
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 | * If bpf, copy the mbuf chain. This is less expensive than | ||||
* it appears; the mbuf clusters are not copied, only their | * it appears; the mbuf clusters are not copied, only their | ||||
* reference counts are incremented. | * reference counts are incremented. | ||||
* Needed to avoid a race condition where the completion | * Needed to avoid a race condition where the completion | ||||
* callback is invoked, freeing the mbuf chain, before the | * callback is invoked, freeing the mbuf chain, before the | ||||
* bpf_mtap code has a chance to run. | * bpf_mtap code has a chance to run. | ||||
*/ | */ | ||||
if (ifp->if_bpf) { | if (ifp->if_bpf) { | ||||
mc_head = m_copypacket(m_head, M_NOWAIT); | mc_head = m_copypacket(m_head, M_NOWAIT); | ||||
} | } | ||||
retry_send: | retry_send: | ||||
/* Set the completion routine */ | /* Set the completion routine */ | ||||
packet->compl.send.on_send_completion = netvsc_xmit_completion; | packet->compl.send.on_send_completion = netvsc_xmit_completion; | ||||
packet->compl.send.send_completion_context = packet; | packet->compl.send.send_completion_context = packet; | ||||
packet->compl.send.send_completion_tid = (uint64_t)(uintptr_t)m_head; | packet->compl.send.send_completion_tid = (uint64_t)(uintptr_t)m_head; | ||||
/* Removed critical_enter(), does not appear necessary */ | /* 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 (ret == 0) { | ||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | ||||
/* if bpf && mc_head, call bpf_mtap code */ | /* if bpf && mc_head, call bpf_mtap code */ | ||||
if (mc_head) { | if (mc_head) { | ||||
ETHER_BPF_MTAP(ifp, mc_head); | ETHER_BPF_MTAP(ifp, mc_head); | ||||
} | } | ||||
} else { | } else { | ||||
retries++; | retries++; | ||||
Show All 11 Lines | if (ret == 0) { | ||||
*/ | */ | ||||
packet->compl.send.send_completion_tid = 0; | packet->compl.send.send_completion_tid = 0; | ||||
/* | /* | ||||
* Release the resources since we will not get any | * Release the resources since we will not get any | ||||
* send completion | * send completion | ||||
*/ | */ | ||||
netvsc_xmit_completion(packet); | netvsc_xmit_completion(packet); | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
} | } | ||||
/* if bpf && mc_head, free the mbuf chain copy */ | /* if bpf && mc_head, free the mbuf chain copy */ | ||||
if (mc_head) { | if (mc_head) { | ||||
m_freem(mc_head); | m_freem(mc_head); | ||||
} | } | ||||
} | } | ||||
Show All 18 Lines | if (status == 1) { | ||||
sc->hn_carrier = 0; | sc->hn_carrier = 0; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Append the specified data to the indicated mbuf chain, | * Append the specified data to the indicated mbuf chain, | ||||
* Extend the mbuf chain if the new data does not fit in | * Extend the mbuf chain if the new data does not fit in | ||||
* existing space. | * existing space. | ||||
* | * | ||||
Done Inline Actionsatop(hv_get_phys_addr(rndis_mesg)) royger: ```
atop(hv_get_phys_addr(rndis_mesg))
``` | |||||
* This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c. | * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c. | ||||
* There should be an equivalent in the kernel mbuf code, | * There should be an equivalent in the kernel mbuf code, | ||||
Done Inline ActionsUse PAGE_MASK instead of PAGE_SIZE - 1 royger: Use PAGE_MASK instead of PAGE_SIZE - 1 | |||||
* but there does not appear to be one yet. | * but there does not appear to be one yet. | ||||
* | * | ||||
* Differs from m_append() in that additional mbufs are | * Differs from m_append() in that additional mbufs are | ||||
* allocated with cluster size MJUMPAGESIZE, and filled | * allocated with cluster size MJUMPAGESIZE, and filled | ||||
* accordingly. | * accordingly. | ||||
* | * | ||||
* Return 1 if able to complete the job; otherwise 0. | * Return 1 if able to complete the job; otherwise 0. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Called when we receive a data packet from the "wire" on the | * Called when we receive a data packet from the "wire" on the | ||||
* specified device | * specified device | ||||
* | * | ||||
* Note: This is no longer used as a callback | * Note: This is no longer used as a callback | ||||
*/ | */ | ||||
int | 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 | |||||
) | |||||
Done Inline ActionsThis is not FreeBSD coding style. royger: This is not FreeBSD coding style. | |||||
{ | { | ||||
hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device); | hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device); | ||||
struct mbuf *m_new; | struct mbuf *m_new; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int size; | int size; | ||||
int i; | |||||
if (sc == NULL) { | if (sc == NULL) { | ||||
return (0); /* TODO: KYS how can this be! */ | return (0); /* TODO: KYS how can this be! */ | ||||
} | } | ||||
ifp = sc->hn_ifp; | ifp = sc->hn_ifp; | ||||
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { | if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { | ||||
Show All 17 Lines | ) | ||||
if (packet->tot_data_buf_len > MCLBYTES) { | if (packet->tot_data_buf_len > MCLBYTES) { | ||||
/* 4096 */ | /* 4096 */ | ||||
size = MJUMPAGESIZE; | size = MJUMPAGESIZE; | ||||
} | } | ||||
m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size); | m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size); | ||||
if (m_new == NULL) | if (m_new == NULL) { | ||||
printf("alloc mbuf failed!!!\n"); | |||||
Done Inline Actionsdevice_printf and no exclamation marks. royger: device_printf and no exclamation marks. | |||||
return (0); | return (0); | ||||
} | |||||
/* | hv_m_append(m_new, packet->tot_data_buf_len, | ||||
* Remove trailing junk from RX data buffer. | packet->data); | ||||
* 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; | |||||
/* | m_new->m_pkthdr.rcvif = ifp; | ||||
* 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, | /* receive side checksum offload */ | ||||
vaddr + packet->page_buffers[i].offset); | 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); | |||||
} | } | ||||
m_new->m_pkthdr.rcvif = ifp; | /* 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) && | 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_pkthdr.ether_vtag = packet->vlan_tci; | ||||
m_new->m_flags |= M_VLANTAG; | m_new->m_flags |= M_VLANTAG; | ||||
} | } | ||||
/* | /* | ||||
* Note: Moved RX completion back to hv_nv_on_receive() so all | * Note: Moved RX completion back to hv_nv_on_receive() so all | ||||
* messages (not just data messages) will trigger a response. | * messages (not just data messages) will trigger a response. | ||||
*/ | */ | ||||
Show All 29 Lines | hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ||||
netvsc_device_info device_info; | netvsc_device_info device_info; | ||||
struct hv_device *hn_dev; | struct hv_device *hn_dev; | ||||
int mask, error = 0; | int mask, error = 0; | ||||
int retry_cnt = 500; | int retry_cnt = 500; | ||||
switch(cmd) { | switch(cmd) { | ||||
case SIOCSIFADDR: | case SIOCSIFADDR: | ||||
case SIOCGIFADDR: | |||||
#ifdef INET | #ifdef INET | ||||
NV_LOCK(sc); | |||||
if (ifa->ifa_addr->sa_family == AF_INET) { | if (ifa->ifa_addr->sa_family == AF_INET) { | ||||
ifp->if_flags |= IFF_UP; | ifp->if_flags |= IFF_UP; | ||||
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) | if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { | ||||
hn_ifinit(sc); | ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); | ||||
arp_ifinit(ifp, ifa); | if_link_state_change(ifp, LINK_STATE_DOWN); | ||||
} else | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
if_link_state_change(ifp, LINK_STATE_UP); | |||||
} | |||||
NV_UNLOCK(sc); | |||||
} else { | |||||
NV_UNLOCK(sc); | |||||
#endif | #endif | ||||
error = ether_ioctl(ifp, cmd, data); | error = ether_ioctl(ifp, cmd, data); | ||||
#ifdef INET | |||||
} | |||||
#endif | |||||
break; | break; | ||||
case SIOCSIFMTU: | case SIOCSIFMTU: | ||||
hn_dev = vmbus_get_devctx(sc->hn_dev); | hn_dev = vmbus_get_devctx(sc->hn_dev); | ||||
/* Check MTU value change */ | /* Check MTU value change */ | ||||
if (ifp->if_mtu == ifr->ifr_mtu) | if (ifp->if_mtu == ifr->ifr_mtu) | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | #endif | ||||
NV_LOCK(sc); | NV_LOCK(sc); | ||||
sc->temp_unusable = FALSE; | sc->temp_unusable = FALSE; | ||||
NV_UNLOCK(sc); | NV_UNLOCK(sc); | ||||
sc->hn_if_flags = ifp->if_flags; | sc->hn_if_flags = ifp->if_flags; | ||||
error = 0; | error = 0; | ||||
break; | break; | ||||
case SIOCSIFCAP: | case SIOCSIFCAP: | ||||
mask = ifr->ifr_reqcap ^ ifp->if_capenable; | mask = ifr->ifr_reqcap ^ ifp->if_capenable; | ||||
if (mask & IFCAP_HWCSUM) { | if (mask & IFCAP_TXCSUM) { | ||||
if (IFCAP_HWCSUM & ifp->if_capenable) { | if (IFCAP_TXCSUM & ifp->if_capenable) { | ||||
ifp->if_capenable &= ~IFCAP_HWCSUM; | ifp->if_capenable &= ~IFCAP_TXCSUM; | ||||
ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP); | |||||
} else { | } 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; | error = 0; | ||||
break; | break; | ||||
case SIOCADDMULTI: | case SIOCADDMULTI: | ||||
case SIOCDELMULTI: | case SIOCDELMULTI: | ||||
#ifdef notyet | #ifdef notyet | ||||
/* Fixme: Multicast mode? */ | /* Fixme: Multicast mode? */ | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
NV_LOCK(sc); | NV_LOCK(sc); | ||||
▲ Show 20 Lines • Show All 150 Lines • Show Last 20 Lines |
IMHO I would set ret_val = TRANSPORT_TYPE_NOT_IP here so the flow is clearer.