Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hyperv/netvsc/if_hn.c
Show First 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | #endif | ||||
struct rndis_packet_msg *rndis_pkt; | struct rndis_packet_msg *rndis_pkt; | ||||
bus_dmamap_t rndis_pkt_dmap; | bus_dmamap_t rndis_pkt_dmap; | ||||
}; | }; | ||||
#define HN_TXD_FLAG_ONLIST 0x0001 | #define HN_TXD_FLAG_ONLIST 0x0001 | ||||
#define HN_TXD_FLAG_DMAMAP 0x0002 | #define HN_TXD_FLAG_DMAMAP 0x0002 | ||||
#define HN_TXD_FLAG_ONAGG 0x0004 | #define HN_TXD_FLAG_ONAGG 0x0004 | ||||
#define HN_NDIS_PKTINFO_SUBALLOC 0x01 | |||||
#define HN_NDIS_PKTINFO_1ST_FRAG 0x02 | |||||
#define HN_NDIS_PKTINFO_LAST_FRAG 0x04 | |||||
struct packet_info_id { | |||||
uint8_t ver; | |||||
uint8_t flag; | |||||
uint16_t pkt_id; | |||||
}; | |||||
#define NDIS_PKTINFOID_SZ sizeof(struct packet_info_id) | |||||
struct hn_rxinfo { | struct hn_rxinfo { | ||||
uint32_t vlan_info; | const uint32_t *vlan_info; | ||||
uint32_t csum_info; | const uint32_t *csum_info; | ||||
uint32_t hash_info; | const uint32_t *hash_info; | ||||
uint32_t hash_value; | const uint32_t *hash_value; | ||||
const struct packet_info_id *pktinfo_id; | |||||
}; | }; | ||||
struct hn_rxvf_setarg { | struct hn_rxvf_setarg { | ||||
struct hn_rx_ring *rxr; | struct hn_rx_ring *rxr; | ||||
struct ifnet *vf_ifp; | struct ifnet *vf_ifp; | ||||
}; | }; | ||||
#define HN_RXINFO_VLAN 0x0001 | #define HN_RXINFO_VLAN 0x0001 | ||||
#define HN_RXINFO_CSUM 0x0002 | #define HN_RXINFO_CSUM 0x0002 | ||||
#define HN_RXINFO_HASHINF 0x0004 | #define HN_RXINFO_HASHINF 0x0004 | ||||
#define HN_RXINFO_HASHVAL 0x0008 | #define HN_RXINFO_HASHVAL 0x0008 | ||||
#define HN_RXINFO_PKTINFO_ID 0x0010 | |||||
#define HN_RXINFO_ALL \ | #define HN_RXINFO_ALL \ | ||||
(HN_RXINFO_VLAN | \ | (HN_RXINFO_VLAN | \ | ||||
HN_RXINFO_CSUM | \ | HN_RXINFO_CSUM | \ | ||||
HN_RXINFO_HASHINF | \ | HN_RXINFO_HASHINF | \ | ||||
HN_RXINFO_HASHVAL) | HN_RXINFO_HASHVAL | \ | ||||
HN_RXINFO_PKTINFO_ID) | |||||
#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff | |||||
#define HN_NDIS_RXCSUM_INFO_INVALID 0 | |||||
#define HN_NDIS_HASH_INFO_INVALID 0 | |||||
static int hn_probe(device_t); | static int hn_probe(device_t); | ||||
static int hn_attach(device_t); | static int hn_attach(device_t); | ||||
static int hn_detach(device_t); | static int hn_detach(device_t); | ||||
static int hn_shutdown(device_t); | static int hn_shutdown(device_t); | ||||
static void hn_chan_callback(struct vmbus_channel *, | static void hn_chan_callback(struct vmbus_channel *, | ||||
void *); | void *); | ||||
static void hn_init(void *); | static void hn_init(void *); | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | |||||
static void hn_destroy_rx_data(struct hn_softc *); | static void hn_destroy_rx_data(struct hn_softc *); | ||||
static int hn_check_iplen(const struct mbuf *, int); | static int hn_check_iplen(const struct mbuf *, int); | ||||
static void hn_rxpkt_proto(const struct mbuf *, int *, int *); | static void hn_rxpkt_proto(const struct mbuf *, int *, int *); | ||||
static int hn_set_rxfilter(struct hn_softc *, uint32_t); | static int hn_set_rxfilter(struct hn_softc *, uint32_t); | ||||
static int hn_rxfilter_config(struct hn_softc *); | static int hn_rxfilter_config(struct hn_softc *); | ||||
static int hn_rss_reconfig(struct hn_softc *); | static int hn_rss_reconfig(struct hn_softc *); | ||||
static void hn_rss_ind_fixup(struct hn_softc *); | static void hn_rss_ind_fixup(struct hn_softc *); | ||||
static void hn_rss_mbuf_hash(struct hn_softc *, uint32_t); | static void hn_rss_mbuf_hash(struct hn_softc *, uint32_t); | ||||
static int hn_rxpkt(struct hn_rx_ring *, const void *, | static int hn_rxpkt(struct hn_rx_ring *); | ||||
int, const struct hn_rxinfo *); | |||||
static uint32_t hn_rss_type_fromndis(uint32_t); | static uint32_t hn_rss_type_fromndis(uint32_t); | ||||
static uint32_t hn_rss_type_tondis(uint32_t); | static uint32_t hn_rss_type_tondis(uint32_t); | ||||
static int hn_tx_ring_create(struct hn_softc *, int); | static int hn_tx_ring_create(struct hn_softc *, int); | ||||
static void hn_tx_ring_destroy(struct hn_tx_ring *); | static void hn_tx_ring_destroy(struct hn_tx_ring *); | ||||
static int hn_create_tx_data(struct hn_softc *, int); | static int hn_create_tx_data(struct hn_softc *, int); | ||||
static void hn_fixup_tx_data(struct hn_softc *); | static void hn_fixup_tx_data(struct hn_softc *); | ||||
static void hn_fixup_rx_data(struct hn_softc *); | static void hn_fixup_rx_data(struct hn_softc *); | ||||
▲ Show 20 Lines • Show All 2,939 Lines • ▼ Show 20 Lines | |||||
* 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, | ||||
* 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 the last mbuf in the chain or NULL if failed to | ||||
* allocate new mbuf. | |||||
*/ | */ | ||||
static int | static struct mbuf * | ||||
hv_m_append(struct mbuf *m0, int len, c_caddr_t cp) | hv_m_append(struct mbuf *m0, int len, c_caddr_t cp) | ||||
{ | { | ||||
struct mbuf *m, *n; | struct mbuf *m, *n; | ||||
int remainder, space; | int remainder, space; | ||||
for (m = m0; m->m_next != NULL; m = m->m_next) | for (m = m0; m->m_next != NULL; m = m->m_next) | ||||
; | ; | ||||
remainder = len; | remainder = len; | ||||
Show All 11 Lines | hv_m_append(struct mbuf *m0, int len, c_caddr_t cp) | ||||
} | } | ||||
while (remainder > 0) { | while (remainder > 0) { | ||||
/* | /* | ||||
* Allocate a new mbuf; could check space | * Allocate a new mbuf; could check space | ||||
* and allocate a cluster instead. | * and allocate a cluster instead. | ||||
*/ | */ | ||||
n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE); | n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE); | ||||
if (n == NULL) | if (n == NULL) | ||||
break; | return NULL; | ||||
n->m_len = min(MJUMPAGESIZE, remainder); | n->m_len = min(MJUMPAGESIZE, remainder); | ||||
bcopy(cp, mtod(n, caddr_t), n->m_len); | bcopy(cp, mtod(n, caddr_t), n->m_len); | ||||
cp += n->m_len; | cp += n->m_len; | ||||
remainder -= n->m_len; | remainder -= n->m_len; | ||||
m->m_next = n; | m->m_next = n; | ||||
m = n; | m = n; | ||||
} | } | ||||
if (m0->m_flags & M_PKTHDR) | |||||
m0->m_pkthdr.len += len - remainder; | |||||
return (remainder == 0); | return m; | ||||
} | } | ||||
#if defined(INET) || defined(INET6) | #if defined(INET) || defined(INET6) | ||||
static __inline int | static __inline int | ||||
hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m) | hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m) | ||||
{ | { | ||||
#if __FreeBSD_version >= 1100095 | #if __FreeBSD_version >= 1100095 | ||||
if (hn_lro_mbufq_depth) { | if (hn_lro_mbufq_depth) { | ||||
tcp_lro_queue_mbuf(lc, m); | tcp_lro_queue_mbuf(lc, m); | ||||
return 0; | return 0; | ||||
} | } | ||||
#endif | #endif | ||||
return tcp_lro_rx(lc, m, 0); | return tcp_lro_rx(lc, m, 0); | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, | hn_rxpkt(struct hn_rx_ring *rxr) | ||||
const struct hn_rxinfo *info) | |||||
{ | { | ||||
struct ifnet *ifp, *hn_ifp = rxr->hn_ifp; | struct ifnet *ifp, *hn_ifp = rxr->hn_ifp; | ||||
struct mbuf *m_new; | struct mbuf *m_new, *n; | ||||
int size, do_lro = 0, do_csum = 1, is_vf = 0; | int size, do_lro = 0, do_csum = 1, is_vf = 0; | ||||
int hash_type = M_HASHTYPE_NONE; | int hash_type = M_HASHTYPE_NONE; | ||||
int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE; | int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE; | ||||
int i; | |||||
ifp = hn_ifp; | ifp = hn_ifp; | ||||
if (rxr->hn_rxvf_ifp != NULL) { | if (rxr->hn_rxvf_ifp != NULL) { | ||||
/* | /* | ||||
* Non-transparent mode VF; pretend this packet is from | * Non-transparent mode VF; pretend this packet is from | ||||
* the VF. | * the VF. | ||||
*/ | */ | ||||
ifp = rxr->hn_rxvf_ifp; | ifp = rxr->hn_rxvf_ifp; | ||||
Show All 10 Lines | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | ||||
* function can be reached, immediately after the | * function can be reached, immediately after the | ||||
* RNDIS is initialized but before the ifnet is | * RNDIS is initialized but before the ifnet is | ||||
* setup on the hn_attach() path; drop the unexpected | * setup on the hn_attach() path; drop the unexpected | ||||
* packets. | * packets. | ||||
*/ | */ | ||||
return (0); | return (0); | ||||
} | } | ||||
if (__predict_false(dlen < ETHER_HDR_LEN)) { | if (__predict_false(rxr->rsc.pktlen < ETHER_HDR_LEN)) { | ||||
if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1); | if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (dlen <= MHLEN) { | if (rxr->rsc.cnt == 1 && rxr->rsc.pktlen <= MHLEN) { | ||||
m_new = m_gethdr(M_NOWAIT, MT_DATA); | m_new = m_gethdr(M_NOWAIT, MT_DATA); | ||||
if (m_new == NULL) { | if (m_new == NULL) { | ||||
if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1); | if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
memcpy(mtod(m_new, void *), data, dlen); | memcpy(mtod(m_new, void *), rxr->rsc.frag_data[0], | ||||
m_new->m_pkthdr.len = m_new->m_len = dlen; | rxr->rsc.frag_len[0]); | ||||
rxr->hn_small_pkts++; | m_new->m_pkthdr.len = m_new->m_len = rxr->rsc.frag_len[0]; | ||||
} else { | } else { | ||||
/* | /* | ||||
* Get an mbuf with a cluster. For packets 2K or less, | * Get an mbuf with a cluster. For packets 2K or less, | ||||
* get a standard 2K cluster. For anything larger, get a | * get a standard 2K cluster. For anything larger, get a | ||||
* 4K cluster. Any buffers larger than 4K can cause problems | * 4K cluster. Any buffers larger than 4K can cause problems | ||||
* if looped around to the Hyper-V TX channel, so avoid them. | * if looped around to the Hyper-V TX channel, so avoid them. | ||||
*/ | */ | ||||
size = MCLBYTES; | size = MCLBYTES; | ||||
if (dlen > MCLBYTES) { | if (rxr->rsc.pktlen > 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) { | ||||
if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1); | if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
hv_m_append(m_new, dlen, data); | n = m_new; | ||||
for (i = 0; i < rxr->rsc.cnt; i++) { | |||||
n = hv_m_append(n, rxr->rsc.frag_len[i], | |||||
rxr->rsc.frag_data[i]); | |||||
if (n == NULL) { | |||||
if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1); | |||||
return (0); | |||||
} else { | |||||
m_new->m_pkthdr.len += rxr->rsc.frag_len[i]; | |||||
} | } | ||||
} | |||||
} | |||||
if (rxr->rsc.pktlen <= MHLEN) | |||||
rxr->hn_small_pkts++; | |||||
m_new->m_pkthdr.rcvif = ifp; | m_new->m_pkthdr.rcvif = ifp; | ||||
if (__predict_false((hn_ifp->if_capenable & IFCAP_RXCSUM) == 0)) | if (__predict_false((hn_ifp->if_capenable & IFCAP_RXCSUM) == 0)) | ||||
do_csum = 0; | do_csum = 0; | ||||
/* receive side checksum offload */ | /* receive side checksum offload */ | ||||
if (info->csum_info != HN_NDIS_RXCSUM_INFO_INVALID) { | if (rxr->rsc.csum_info != NULL) { | ||||
/* IP csum offload */ | /* IP csum offload */ | ||||
if ((info->csum_info & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) { | if ((*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) { | ||||
m_new->m_pkthdr.csum_flags |= | m_new->m_pkthdr.csum_flags |= | ||||
(CSUM_IP_CHECKED | CSUM_IP_VALID); | (CSUM_IP_CHECKED | CSUM_IP_VALID); | ||||
rxr->hn_csum_ip++; | rxr->hn_csum_ip++; | ||||
} | } | ||||
/* TCP/UDP csum offload */ | /* TCP/UDP csum offload */ | ||||
if ((info->csum_info & (NDIS_RXCSUM_INFO_UDPCS_OK | | if ((*(rxr->rsc.csum_info) & (NDIS_RXCSUM_INFO_UDPCS_OK | | ||||
NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) { | NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) { | ||||
m_new->m_pkthdr.csum_flags |= | m_new->m_pkthdr.csum_flags |= | ||||
(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); | (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); | ||||
m_new->m_pkthdr.csum_data = 0xffff; | m_new->m_pkthdr.csum_data = 0xffff; | ||||
if (info->csum_info & NDIS_RXCSUM_INFO_TCPCS_OK) | if (*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_TCPCS_OK) | ||||
rxr->hn_csum_tcp++; | rxr->hn_csum_tcp++; | ||||
else | else | ||||
rxr->hn_csum_udp++; | rxr->hn_csum_udp++; | ||||
} | } | ||||
/* | /* | ||||
* XXX | * XXX | ||||
* As of this write (Oct 28th, 2016), host side will turn | * As of this write (Oct 28th, 2016), host side will turn | ||||
* on only TCPCS_OK and IPCS_OK even for UDP datagrams, so | * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so | ||||
* the do_lro setting here is actually _not_ accurate. We | * the do_lro setting here is actually _not_ accurate. We | ||||
* depend on the RSS hash type check to reset do_lro. | * depend on the RSS hash type check to reset do_lro. | ||||
*/ | */ | ||||
if ((info->csum_info & | if ((*(rxr->rsc.csum_info) & | ||||
(NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) == | (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) == | ||||
(NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) | (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) | ||||
do_lro = 1; | do_lro = 1; | ||||
} else { | } else { | ||||
hn_rxpkt_proto(m_new, &l3proto, &l4proto); | hn_rxpkt_proto(m_new, &l3proto, &l4proto); | ||||
if (l3proto == ETHERTYPE_IP) { | if (l3proto == ETHERTYPE_IP) { | ||||
if (l4proto == IPPROTO_TCP) { | if (l4proto == IPPROTO_TCP) { | ||||
if (do_csum && | if (do_csum && | ||||
Show All 20 Lines | if (l3proto == ETHERTYPE_IP) { | ||||
(rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) { | (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) { | ||||
rxr->hn_csum_trusted++; | rxr->hn_csum_trusted++; | ||||
m_new->m_pkthdr.csum_flags |= | m_new->m_pkthdr.csum_flags |= | ||||
(CSUM_IP_CHECKED | CSUM_IP_VALID); | (CSUM_IP_CHECKED | CSUM_IP_VALID); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (info->vlan_info != HN_NDIS_VLAN_INFO_INVALID) { | if (rxr->rsc.vlan_info != NULL) { | ||||
m_new->m_pkthdr.ether_vtag = EVL_MAKETAG( | m_new->m_pkthdr.ether_vtag = EVL_MAKETAG( | ||||
NDIS_VLAN_INFO_ID(info->vlan_info), | NDIS_VLAN_INFO_ID(*(rxr->rsc.vlan_info)), | ||||
NDIS_VLAN_INFO_PRI(info->vlan_info), | NDIS_VLAN_INFO_PRI(*(rxr->rsc.vlan_info)), | ||||
NDIS_VLAN_INFO_CFI(info->vlan_info)); | NDIS_VLAN_INFO_CFI(*(rxr->rsc.vlan_info))); | ||||
m_new->m_flags |= M_VLANTAG; | m_new->m_flags |= M_VLANTAG; | ||||
} | } | ||||
/* | /* | ||||
* If VF is activated (tranparent/non-transparent mode does not | * If VF is activated (tranparent/non-transparent mode does not | ||||
* matter here). | * matter here). | ||||
* | * | ||||
* - Disable LRO | * - Disable LRO | ||||
Show All 9 Lines | hn_rxpkt(struct hn_rx_ring *rxr) | ||||
if (is_vf) | if (is_vf) | ||||
do_lro = 0; | do_lro = 0; | ||||
/* | /* | ||||
* If VF is activated (tranparent/non-transparent mode does not | * If VF is activated (tranparent/non-transparent mode does not | ||||
* matter here), do _not_ mess with unsupported hash types or | * matter here), do _not_ mess with unsupported hash types or | ||||
* functions. | * functions. | ||||
*/ | */ | ||||
if (info->hash_info != HN_NDIS_HASH_INFO_INVALID) { | if (rxr->rsc.hash_info != NULL) { | ||||
rxr->hn_rss_pkts++; | rxr->hn_rss_pkts++; | ||||
m_new->m_pkthdr.flowid = info->hash_value; | m_new->m_pkthdr.flowid = *(rxr->rsc.hash_value); | ||||
if (!is_vf) | if (!is_vf) | ||||
hash_type = M_HASHTYPE_OPAQUE_HASH; | hash_type = M_HASHTYPE_OPAQUE_HASH; | ||||
if ((info->hash_info & NDIS_HASH_FUNCTION_MASK) == | if ((*(rxr->rsc.hash_info) & NDIS_HASH_FUNCTION_MASK) == | ||||
NDIS_HASH_FUNCTION_TOEPLITZ) { | NDIS_HASH_FUNCTION_TOEPLITZ) { | ||||
uint32_t type = (info->hash_info & NDIS_HASH_TYPE_MASK & | uint32_t type = (*(rxr->rsc.hash_info) & NDIS_HASH_TYPE_MASK & | ||||
rxr->hn_mbuf_hash); | rxr->hn_mbuf_hash); | ||||
/* | /* | ||||
* NOTE: | * NOTE: | ||||
* do_lro is resetted, if the hash types are not TCP | * do_lro is resetted, if the hash types are not TCP | ||||
* related. See the comment in the above csum_flags | * related. See the comment in the above csum_flags | ||||
* setup section. | * setup section. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 1,428 Lines • ▼ Show 20 Lines | if (sc->hn_rx_sysctl_tree != NULL) { | ||||
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | ||||
OID_AUTO, "packets", CTLFLAG_RW, | OID_AUTO, "packets", CTLFLAG_RW, | ||||
&rxr->hn_pkts, "# of packets received"); | &rxr->hn_pkts, "# of packets received"); | ||||
SYSCTL_ADD_ULONG(ctx, | SYSCTL_ADD_ULONG(ctx, | ||||
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | ||||
OID_AUTO, "rss_pkts", CTLFLAG_RW, | OID_AUTO, "rss_pkts", CTLFLAG_RW, | ||||
&rxr->hn_rss_pkts, | &rxr->hn_rss_pkts, | ||||
"# of packets w/ RSS info received"); | "# of packets w/ RSS info received"); | ||||
SYSCTL_ADD_ULONG(ctx, | |||||
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | |||||
OID_AUTO, "rsc_pkts", CTLFLAG_RW, | |||||
&rxr->hn_rsc_pkts, | |||||
"# of RSC packets received"); | |||||
SYSCTL_ADD_ULONG(ctx, | |||||
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | |||||
OID_AUTO, "rsc_drop", CTLFLAG_RW, | |||||
&rxr->hn_rsc_drop, | |||||
"# of RSC fragments dropped"); | |||||
SYSCTL_ADD_INT(ctx, | SYSCTL_ADD_INT(ctx, | ||||
SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), | ||||
OID_AUTO, "pktbuf_len", CTLFLAG_RD, | OID_AUTO, "pktbuf_len", CTLFLAG_RD, | ||||
&rxr->hn_pktbuf_len, 0, | &rxr->hn_pktbuf_len, 0, | ||||
"Temporary channel packet buffer length"); | "Temporary channel packet buffer length"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,008 Lines • ▼ Show 20 Lines | while (info_dlen != 0) { | ||||
if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) | if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) | if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) | ||||
return (EINVAL); | return (EINVAL); | ||||
dlen = pi->rm_size - pi->rm_pktinfooffset; | dlen = pi->rm_size - pi->rm_pktinfooffset; | ||||
data = pi->rm_data; | data = pi->rm_data; | ||||
if (pi->rm_internal == 1) { | |||||
switch (pi->rm_type) { | switch (pi->rm_type) { | ||||
case NDIS_PKTINFO_IT_PKTINFO_ID: | |||||
if (__predict_false(dlen < NDIS_PKTINFOID_SZ)) | |||||
return (EINVAL); | |||||
info->pktinfo_id = | |||||
(const struct packet_info_id *)data; | |||||
mask |= HN_RXINFO_PKTINFO_ID; | |||||
break; | |||||
default: | |||||
goto next; | |||||
} | |||||
} else { | |||||
switch (pi->rm_type) { | |||||
case NDIS_PKTINFO_TYPE_VLAN: | case NDIS_PKTINFO_TYPE_VLAN: | ||||
if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) | if (__predict_false(dlen | ||||
< NDIS_VLAN_INFO_SIZE)) | |||||
return (EINVAL); | return (EINVAL); | ||||
info->vlan_info = *((const uint32_t *)data); | info->vlan_info = (const uint32_t *)data; | ||||
mask |= HN_RXINFO_VLAN; | mask |= HN_RXINFO_VLAN; | ||||
break; | break; | ||||
case NDIS_PKTINFO_TYPE_CSUM: | case NDIS_PKTINFO_TYPE_CSUM: | ||||
if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) | if (__predict_false(dlen | ||||
< NDIS_RXCSUM_INFO_SIZE)) | |||||
return (EINVAL); | return (EINVAL); | ||||
info->csum_info = *((const uint32_t *)data); | info->csum_info = (const uint32_t *)data; | ||||
mask |= HN_RXINFO_CSUM; | mask |= HN_RXINFO_CSUM; | ||||
break; | break; | ||||
case HN_NDIS_PKTINFO_TYPE_HASHVAL: | case HN_NDIS_PKTINFO_TYPE_HASHVAL: | ||||
if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) | if (__predict_false(dlen | ||||
< HN_NDIS_HASH_VALUE_SIZE)) | |||||
return (EINVAL); | return (EINVAL); | ||||
info->hash_value = *((const uint32_t *)data); | info->hash_value = (const uint32_t *)data; | ||||
mask |= HN_RXINFO_HASHVAL; | mask |= HN_RXINFO_HASHVAL; | ||||
break; | break; | ||||
case HN_NDIS_PKTINFO_TYPE_HASHINF: | case HN_NDIS_PKTINFO_TYPE_HASHINF: | ||||
if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) | if (__predict_false(dlen | ||||
< HN_NDIS_HASH_INFO_SIZE)) | |||||
return (EINVAL); | return (EINVAL); | ||||
info->hash_info = *((const uint32_t *)data); | info->hash_info = (const uint32_t *)data; | ||||
mask |= HN_RXINFO_HASHINF; | mask |= HN_RXINFO_HASHINF; | ||||
break; | break; | ||||
default: | default: | ||||
goto next; | goto next; | ||||
} | } | ||||
} | |||||
if (mask == HN_RXINFO_ALL) { | if (mask == HN_RXINFO_ALL) { | ||||
/* All found; done */ | /* All found; done */ | ||||
break; | break; | ||||
} | } | ||||
next: | next: | ||||
pi = (const struct rndis_pktinfo *) | pi = (const struct rndis_pktinfo *) | ||||
((const uint8_t *)pi + pi->rm_size); | ((const uint8_t *)pi + pi->rm_size); | ||||
} | } | ||||
/* | /* | ||||
* Final fixup. | * Final fixup. | ||||
* - If there is no hash value, invalidate the hash info. | * - If there is no hash value, invalidate the hash info. | ||||
*/ | */ | ||||
if ((mask & HN_RXINFO_HASHVAL) == 0) | if ((mask & HN_RXINFO_HASHVAL) == 0) | ||||
info->hash_info = HN_NDIS_HASH_INFO_INVALID; | info->hash_info = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
static __inline bool | static __inline bool | ||||
hn_rndis_check_overlap(int off, int len, int check_off, int check_len) | hn_rndis_check_overlap(int off, int len, int check_off, int check_len) | ||||
{ | { | ||||
if (off < check_off) { | if (off < check_off) { | ||||
if (__predict_true(off + len <= check_off)) | if (__predict_true(off + len <= check_off)) | ||||
return (false); | return (false); | ||||
} else if (off > check_off) { | } else if (off > check_off) { | ||||
if (__predict_true(check_off + check_len <= off)) | if (__predict_true(check_off + check_len <= off)) | ||||
return (false); | return (false); | ||||
} | } | ||||
return (true); | return (true); | ||||
} | } | ||||
static __inline void | |||||
hn_rsc_add_data(struct hn_rx_ring *rxr, const void *data, | |||||
uint32_t len, struct hn_rxinfo *info) | |||||
{ | |||||
uint32_t cnt = rxr->rsc.cnt; | |||||
if (cnt) { | |||||
rxr->rsc.pktlen += len; | |||||
} else { | |||||
rxr->rsc.vlan_info = info->vlan_info; | |||||
rxr->rsc.csum_info = info->csum_info; | |||||
rxr->rsc.hash_info = info->hash_info; | |||||
rxr->rsc.hash_value = info->hash_value; | |||||
rxr->rsc.pktlen = len; | |||||
} | |||||
rxr->rsc.frag_data[cnt] = data; | |||||
rxr->rsc.frag_len[cnt] = len; | |||||
rxr->rsc.cnt++; | |||||
} | |||||
static void | static void | ||||
hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen) | hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen) | ||||
{ | { | ||||
const struct rndis_packet_msg *pkt; | const struct rndis_packet_msg *pkt; | ||||
struct hn_rxinfo info; | struct hn_rxinfo info; | ||||
int data_off, pktinfo_off, data_len, pktinfo_len; | int data_off, pktinfo_off, data_len, pktinfo_len; | ||||
bool rsc_more= false; | |||||
/* | /* | ||||
* Check length. | * Check length. | ||||
*/ | */ | ||||
if (__predict_false(dlen < sizeof(*pkt))) { | if (__predict_false(dlen < sizeof(*pkt))) { | ||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); | if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); | ||||
return; | return; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | if (pktinfo_len != 0 && | ||||
oob_off, oob_len, pktinfo_off, pktinfo_len); | oob_off, oob_len, pktinfo_off, pktinfo_len); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Check per-packet-info coverage and find useful per-packet-info. | * Check per-packet-info coverage and find useful per-packet-info. | ||||
*/ | */ | ||||
info.vlan_info = HN_NDIS_VLAN_INFO_INVALID; | info.vlan_info = NULL; | ||||
info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID; | info.csum_info = NULL; | ||||
info.hash_info = HN_NDIS_HASH_INFO_INVALID; | info.hash_info = NULL; | ||||
info.pktinfo_id = NULL; | |||||
if (__predict_true(pktinfo_len != 0)) { | if (__predict_true(pktinfo_len != 0)) { | ||||
bool overlap; | bool overlap; | ||||
int error; | int error; | ||||
if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { | if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { | ||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " | if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " | ||||
"pktinfo overflow, msglen %u, " | "pktinfo overflow, msglen %u, " | ||||
"pktinfo abs %d len %d\n", | "pktinfo abs %d len %d\n", | ||||
Show All 27 Lines | #undef IS_OFFSET_INVALID | ||||
} | } | ||||
if (__predict_false(data_off + data_len > pkt->rm_len)) { | if (__predict_false(data_off + data_len > pkt->rm_len)) { | ||||
if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " | if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " | ||||
"data overflow, msglen %u, data abs %d len %d\n", | "data overflow, msglen %u, data abs %d len %d\n", | ||||
pkt->rm_len, data_off, data_len); | pkt->rm_len, data_off, data_len); | ||||
return; | return; | ||||
} | } | ||||
hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info); | |||||
/* Identify RSC fragments, drop invalid packets */ | |||||
if ((info.pktinfo_id != NULL) && | |||||
(info.pktinfo_id->flag & HN_NDIS_PKTINFO_SUBALLOC)) { | |||||
if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_1ST_FRAG) { | |||||
rxr->rsc.cnt = 0; | |||||
rxr->hn_rsc_pkts++; | |||||
} else if (rxr->rsc.cnt == 0) | |||||
goto drop; | |||||
rsc_more = true; | |||||
if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_LAST_FRAG) | |||||
rsc_more = false; | |||||
if (rsc_more && rxr->rsc.is_last) | |||||
goto drop; | |||||
} else { | |||||
rxr->rsc.cnt = 0; | |||||
} | } | ||||
if (__predict_false(rxr->rsc.cnt >= HN_NVS_RSC_MAX)) | |||||
goto drop; | |||||
/* Store data in per rx ring structure */ | |||||
hn_rsc_add_data(rxr,((const uint8_t *)pkt) + data_off, | |||||
data_len, &info); | |||||
if (rsc_more) | |||||
return; | |||||
hn_rxpkt(rxr); | |||||
rxr->rsc.cnt = 0; | |||||
return; | |||||
drop: | |||||
rxr->hn_rsc_drop++; | |||||
return; | |||||
} | |||||
static __inline void | static __inline void | ||||
hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen) | hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen) | ||||
{ | { | ||||
const struct rndis_msghdr *hdr; | const struct rndis_msghdr *hdr; | ||||
if (__predict_false(dlen < sizeof(*hdr))) { | if (__predict_false(dlen < sizeof(*hdr))) { | ||||
if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); | if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | for (i = 0; i < count; ++i) { | ||||
ofs = pkt->cp_rxbuf[i].rb_ofs; | ofs = pkt->cp_rxbuf[i].rb_ofs; | ||||
len = pkt->cp_rxbuf[i].rb_len; | len = pkt->cp_rxbuf[i].rb_len; | ||||
if (__predict_false(ofs + len > HN_RXBUF_SIZE)) { | if (__predict_false(ofs + len > HN_RXBUF_SIZE)) { | ||||
if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, " | if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, " | ||||
"ofs %d, len %d\n", i, ofs, len); | "ofs %d, len %d\n", i, ofs, len); | ||||
continue; | continue; | ||||
} | } | ||||
rxr->rsc.is_last = (i == (count - 1)); | |||||
hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len); | hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len); | ||||
} | } | ||||
/* | /* | ||||
* Ack the consumed RXBUF associated w/ this channel packet, | * Ack the consumed RXBUF associated w/ this channel packet, | ||||
* so that this RXBUF can be recycled by the hypervisor. | * so that this RXBUF can be recycled by the hypervisor. | ||||
*/ | */ | ||||
hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid); | hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid); | ||||
▲ Show 20 Lines • Show All 190 Lines • Show Last 20 Lines |