diff options
author | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2016-09-23 14:36:09 +0000 |
---|---|---|
committer | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2016-09-23 14:36:09 +0000 |
commit | 6e4310521bc661549d826525ae7cc52756d20276 (patch) | |
tree | 7dfb404107685ab37bf66362f758e80fb7fc086f /sys | |
parent | c9acef3ee3168170dcc57b9fd89f55abab09b0a4 (diff) |
Rework Per Packet Info handling
Per Packet Info is an NDIS meta information embedded in the RNDIS
message to communicate various offloading and scheduling operations
with the backend. Due to various versions of NDIS supported by
Hyper-V some of the offloading features may or may not be available.
This commit brings support for IP and protocol checksum offloading
as well as initial code for hardware VLAN tagging and jumbo frames
where supported.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pv/if_hvn.c | 144 |
1 files changed, 105 insertions, 39 deletions
diff --git a/sys/dev/pv/if_hvn.c b/sys/dev/pv/if_hvn.c index ff5cbb12f00..801ac5b15e4 100644 --- a/sys/dev/pv/if_hvn.c +++ b/sys/dev/pv/if_hvn.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2010-2012 Citrix Inc. * Copyright (c) 2012 NetApp Inc. * Copyright (c) 2016 Mike Belopuhov <mike@esdenera.com> @@ -85,10 +85,6 @@ #define HVN_RNDIS_CTLREQS 4 #define HVN_RNDIS_CMPBUFSZ 512 -#define HVN_RNDIS_MSG_LEN \ - (sizeof(struct rndis_packet_msg) + NDIS_VLAN_INFO_SIZE + \ - NDIS_RXCSUM_INFO_SIZE) - struct rndis_cmd { uint32_t rc_id; struct hvn_nvs_rndis rc_msg; @@ -113,10 +109,15 @@ TAILQ_HEAD(rndis_queue, rndis_cmd); * Tx ring */ #define HVN_TX_DESC 128 -#define HVN_TX_FRAGS 31 +#define HVN_TX_FRAGS 15 /* 31 is the max */ #define HVN_TX_FRAG_SIZE PAGE_SIZE #define HVN_TX_PKT_SIZE 16384 +#define HVN_RNDIS_PKT_LEN \ + (sizeof(struct rndis_packet_msg) + \ + sizeof(struct rndis_pktinfo) + NDIS_VLAN_INFO_SIZE + \ + sizeof(struct rndis_pktinfo) + NDIS_TXCSUM_INFO_SIZE) + struct hvn_tx_desc { uint32_t txd_id; int txd_ready; @@ -490,13 +491,36 @@ hvn_start(struct ifnet *ifp) } } +static inline char * +hvn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize, + size_t datalen, uint32_t type) +{ + struct rndis_pktinfo *pi; + size_t pi_size = sizeof(*pi) + datalen; + char *cp; + + KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= + pktsize); + + cp = (char *)pkt + pkt->rm_pktinfooffset + pkt->rm_pktinfolen; + pi = (struct rndis_pktinfo *)cp; + pi->rm_size = pi_size; + pi->rm_type = type; + pi->rm_pktinfooffset = sizeof(*pi); + pkt->rm_pktinfolen += pi_size; + pkt->rm_dataoffset += pi_size; + pkt->rm_len += pi_size; + return ((char *)pi->rm_data); +} + int hvn_encap(struct hvn_softc *sc, struct mbuf *m, struct hvn_tx_desc **txd0) { struct hvn_tx_desc *txd; + struct rndis_packet_msg *pkt; bus_dma_segment_t *seg; - size_t rlen; - int i; + size_t pktlen; + int i, rv; /* XXX use queues? */ txd = &sc->sc_tx_desc[sc->sc_tx_next]; @@ -506,29 +530,67 @@ hvn_encap(struct hvn_softc *sc, struct mbuf *m, struct hvn_tx_desc **txd0) txd = &sc->sc_tx_desc[sc->sc_tx_next]; } - memset(txd->txd_req, 0, sizeof(*txd->txd_req)); - txd->txd_req->rm_type = REMOTE_NDIS_PACKET_MSG; - txd->txd_req->rm_dataoffset = RNDIS_DATA_OFFSET; - txd->txd_req->rm_datalen = m->m_pkthdr.len; - txd->txd_req->rm_pktinfooffset = RNDIS_DATA_OFFSET; - rlen = sizeof(struct rndis_packet_msg); - - if (bus_dmamap_load_mbuf(sc->sc_dmat, txd->txd_dmap, m, BUS_DMA_READ | - BUS_DMA_NOWAIT)) { + pkt = txd->txd_req; + memset(pkt, 0, sizeof(*pkt)); + pkt->rm_type = REMOTE_NDIS_PACKET_MSG; + pkt->rm_len = sizeof(*pkt) + m->m_pkthdr.len; + pkt->rm_dataoffset = RNDIS_DATA_OFFSET; + pkt->rm_datalen = m->m_pkthdr.len; + pkt->rm_pktinfooffset = sizeof(*pkt); /* adjusted below */ + pkt->rm_pktinfolen = 0; + + rv = bus_dmamap_load_mbuf(sc->sc_dmat, txd->txd_dmap, m, BUS_DMA_READ | + BUS_DMA_NOWAIT); + switch (rv) { + case 0: + break; + case EFBIG: + if (m_defrag(m, M_NOWAIT) == 0 && + bus_dmamap_load_mbuf(sc->sc_dmat, txd->txd_dmap, m, + BUS_DMA_READ | BUS_DMA_NOWAIT) == 0) + break; + default: DPRINTF("%s: failed to load mbuf\n", sc->sc_dev.dv_xname); return (-1); } txd->txd_buf = m; - /* Per-packet info adjusts rlen */ +#if NVLAN > 0 + if (m->m_flags & M_VLANTAG) { + uint32_t vlan; + char *cp; + + vlan = NDIS_VLAN_INFO(EVL_VLANOFTAG(m->m_pkthdr.ether_vtag), + EVL_PRIOFTAG(m->m_pkthdr.ether_vtag)); + cp = hvn_rndis_pktinfo_append(pkt, HVN_RNDIS_PKT_LEN, + NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN); + memcpy(cp, &vlan, NDIS_VLAN_INFO_SIZE); + } +#endif - /* Final length value for the RNDIS header and data */ - txd->txd_req->rm_len = txd->txd_req->rm_datalen + rlen; + if (m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT | M_UDP_CSUM_OUT | + M_TCP_CSUM_OUT)) { + uint32_t csum = NDIS_TXCSUM_INFO_IPV4; + char *cp; + + if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT) + csum |= NDIS_TXCSUM_INFO_IPCS; + if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) + csum |= NDIS_TXCSUM_INFO_TCPCS; + if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) + csum |= NDIS_TXCSUM_INFO_UDPCS; + cp = hvn_rndis_pktinfo_append(pkt, HVN_RNDIS_PKT_LEN, + NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM); + memcpy(cp, &csum, NDIS_TXCSUM_INFO_SIZE); + } + + pktlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen; + pkt->rm_pktinfooffset -= RNDIS_HEADER_OFFSET; /* Attach an RNDIS message into the first slot */ txd->txd_sgl[0].gpa_page = txd->txd_gpa.gpa_page; txd->txd_sgl[0].gpa_ofs = txd->txd_gpa.gpa_ofs; - txd->txd_sgl[0].gpa_len = rlen; + txd->txd_sgl[0].gpa_len = pktlen; txd->txd_nsge = txd->txd_dmap->dm_nsegs + 1; for (i = 0; i < txd->txd_dmap->dm_nsegs; i++) { @@ -682,7 +744,7 @@ hvn_tx_ring_create(struct hvn_softc *sc) int i, rsegs; paddr_t pa; - msgsize = roundup(HVN_RNDIS_MSG_LEN, 128); + msgsize = roundup(HVN_RNDIS_PKT_LEN, 128); /* Allocate memory to store RNDIS messages */ if (bus_dmamem_alloc(sc->sc_dmat, msgsize * HVN_TX_DESC, PAGE_SIZE, 0, @@ -765,7 +827,7 @@ hvn_tx_ring_destroy(struct hvn_softc *sc) bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_rmap); } if (sc->sc_tx_msgs) { - size_t msgsize = roundup(HVN_RNDIS_MSG_LEN, 128); + size_t msgsize = roundup(HVN_RNDIS_PKT_LEN, 128); bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_tx_msgs, msgsize * HVN_TX_DESC); @@ -1366,8 +1428,7 @@ hvn_devget(struct hvn_softc *sc, caddr_t buf, uint32_t len) } void -hvn_rxeof(struct hvn_softc *sc, caddr_t buf, uint32_t len, - struct mbuf_list *ml) +hvn_rxeof(struct hvn_softc *sc, caddr_t buf, uint32_t len, struct mbuf_list *ml) { struct ifnet *ifp = &sc->sc_ac.ac_if; struct rndis_packet_msg *pkt; @@ -1378,7 +1439,7 @@ hvn_rxeof(struct hvn_softc *sc, caddr_t buf, uint32_t len, if (!(ifp->if_flags & IFF_RUNNING)) return; - if (len < RNDIS_HEADER_OFFSET + sizeof(*pkt)) { + if (len < sizeof(*pkt)) { printf("%s: data packet too short: %u\n", sc->sc_dev.dv_xname, len); return; @@ -1398,24 +1459,24 @@ hvn_rxeof(struct hvn_softc *sc, caddr_t buf, uint32_t len, return; } + if (pkt->rm_pktinfooffset + pkt->rm_pktinfolen > len) { + printf("%s: pktinfo is out of bounds: %u@%u vs %u\n", + sc->sc_dev.dv_xname, pkt->rm_pktinfolen, + pkt->rm_pktinfooffset, len); + goto done; + } pi = (struct rndis_pktinfo *)((caddr_t)pkt + RNDIS_HEADER_OFFSET + pkt->rm_pktinfooffset); while (pkt->rm_pktinfolen > 0) { - if (pkt->rm_pktinfooffset + pkt->rm_pktinfolen > len) { - printf("%s: PI out of bounds: %u@%u\n", - sc->sc_dev.dv_xname, pkt->rm_pktinfolen, - pkt->rm_pktinfooffset); - break; - } if (pi->rm_size > pkt->rm_pktinfolen) { - printf("%s: invalid PI size: %u/%u\n", + printf("%s: invalid pktinfo size: %u/%u\n", sc->sc_dev.dv_xname, pi->rm_size, pkt->rm_pktinfolen); break; } switch (pi->rm_type) { case NDIS_PKTINFO_TYPE_CSUM: - memcpy(&csum, (caddr_t)pi + pi->rm_size, sizeof(csum)); + memcpy(&csum, pi->rm_data, sizeof(csum)); if (csum & NDIS_RXCSUM_INFO_IPCS_OK) m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; if (csum & NDIS_RXCSUM_INFO_TCPCS_OK) @@ -1424,20 +1485,25 @@ hvn_rxeof(struct hvn_softc *sc, caddr_t buf, uint32_t len, m->m_pkthdr.csum_flags |= M_UDP_CSUM_IN_OK; break; case NDIS_PKTINFO_TYPE_VLAN: - memcpy(&vlan, (caddr_t)pi + pi->rm_size, sizeof(vlan)); + memcpy(&vlan, pi->rm_data, sizeof(vlan)); #if NVLAN > 0 - m->m_pkthdr.ether_vtag = vlan & 0xffff; - m->m_flags |= M_VLANTAG; + if (vlan != 0xffffffff) { + m->m_pkthdr.ether_vtag = + NDIS_VLAN_INFO_ID(vlan) | + (NDIS_VLAN_INFO_PRI(vlan) << EVL_PRIO_BITS); + m->m_flags |= M_VLANTAG; + } #endif break; default: - DPRINTF("%s: unhandled PI %u\n", sc->sc_dev.dv_xname, - pi->rm_type); + DPRINTF("%s: unhandled pktinfo type %u\n", + sc->sc_dev.dv_xname, pi->rm_type); } pkt->rm_pktinfolen -= pi->rm_size; pi = (struct rndis_pktinfo *)((caddr_t)pi + pi->rm_size); } + done: ml_enqueue(ml, m); } |