Date: Fri, 11 Nov 2016 06:23:01 +0000 (UTC) From: Sepherosa Ziehau <sephe@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r308506 - stable/10/sys/dev/hyperv/netvsc Message-ID: <201611110623.uAB6N1aI062429@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sephe Date: Fri Nov 11 06:23:01 2016 New Revision: 308506 URL: https://svnweb.freebsd.org/changeset/base/308506 Log: MFC 308013-308017 308013 hyperv/hn: Nuke unnecessary indirection. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8355 308014 hyperv/hn: Reorganize RX path; mainly pull non-control code path up Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8356 308015 hyperv/hn: Pull data path code up. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8357 308016 hyperv/hn: Cleanup RNDIS related files. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8358 308017 hyperv/hn: Change header guardian; in preparation for the upcoming rename. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8359 Modified: stable/10/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c stable/10/sys/dev/hyperv/netvsc/hv_rndis_filter.c stable/10/sys/dev/hyperv/netvsc/hv_rndis_filter.h stable/10/sys/dev/hyperv/netvsc/if_hnvar.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c ============================================================================== --- stable/10/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c Fri Nov 11 05:53:58 2016 (r308505) +++ stable/10/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c Fri Nov 11 06:23:01 2016 (r308506) @@ -168,6 +168,16 @@ __FBSDID("$FreeBSD$"); #define HN_EARLY_TXEOF_THRESH 8 +#define HN_RXINFO_VLAN 0x0001 +#define HN_RXINFO_CSUM 0x0002 +#define HN_RXINFO_HASHINF 0x0004 +#define HN_RXINFO_HASHVAL 0x0008 +#define HN_RXINFO_ALL \ + (HN_RXINFO_VLAN | \ + HN_RXINFO_CSUM | \ + HN_RXINFO_HASHINF | \ + HN_RXINFO_HASHVAL) + struct hn_txdesc { #ifndef HN_USE_TXDESC_BUFRING SLIST_ENTRY(hn_txdesc) link; @@ -190,6 +200,17 @@ struct hn_txdesc { #define HN_TXD_FLAG_ONLIST 0x1 #define HN_TXD_FLAG_DMAMAP 0x2 +#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff +#define HN_NDIS_RXCSUM_INFO_INVALID 0 +#define HN_NDIS_HASH_INFO_INVALID 0 + +struct hn_rxinfo { + uint32_t vlan_info; + uint32_t csum_info; + uint32_t hash_info; + uint32_t hash_value; +}; + #define HN_LRO_LENLIM_MULTIRX_DEF (12 * ETHERMTU) #define HN_LRO_LENLIM_DEF (25 * ETHERMTU) /* YYY 2*MTU is a bit rough, but should be good enough. */ @@ -360,6 +381,7 @@ static void hn_chan_detach(struct hn_sof static int hn_attach_subchans(struct hn_softc *); static void hn_detach_allchans(struct hn_softc *); static void hn_chan_callback(struct vmbus_channel *chan, void *xrxr); +static void hn_chan_rollup(struct hn_rx_ring *, struct hn_tx_ring *); static void hn_set_ring_inuse(struct hn_softc *, int); static int hn_synth_attach(struct hn_softc *, int); static void hn_synth_detach(struct hn_softc *); @@ -378,12 +400,18 @@ static void hn_link_status(struct hn_sof static int hn_sendpkt_rndis_sglist(struct hn_tx_ring *, struct hn_txdesc *); static int hn_sendpkt_rndis_chim(struct hn_tx_ring *, struct hn_txdesc *); static int hn_set_rxfilter(struct hn_softc *); +static void hn_link_status_update(struct hn_softc *); +static void hn_network_change(struct hn_softc *); + +static int hn_rndis_rxinfo(const void *, int, struct hn_rxinfo *); +static void hn_rndis_rx_data(struct hn_rx_ring *, const void *, int); +static void hn_rndis_rx_status(struct hn_softc *, const void *, int); static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt); static void hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkt); -static void hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr, +static void hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr); static void hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid); @@ -1021,7 +1049,7 @@ hn_netchg_status_taskfunc(void *xsc, int hn_link_status(sc); } -void +static void hn_link_status_update(struct hn_softc *sc) { @@ -1029,7 +1057,7 @@ hn_link_status_update(struct hn_softc *s taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task); } -void +static void hn_network_change(struct hn_softc *sc) { @@ -1204,7 +1232,7 @@ hn_tx_done(struct hn_nvs_sendctx *sndc, } } -void +static void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr) { #if defined(INET) || defined(INET6) @@ -1238,6 +1266,42 @@ hn_rndis_pktmsg_offset(uint32_t ofs) return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset)); } +static __inline void * +hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize, + size_t pi_dlen, uint32_t pi_type) +{ + const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen); + struct rndis_pktinfo *pi; + + KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0, + ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen)); + + /* + * Per-packet-info does not move; it only grows. + * + * NOTE: + * rm_pktinfooffset in this phase counts from the beginning + * of rndis_packet_msg. + */ + KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize, + ("%u pktinfo overflows RNDIS packet msg", pi_type)); + pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset + + pkt->rm_pktinfolen); + pkt->rm_pktinfolen += pi_size; + + pi->rm_size = pi_size; + pi->rm_type = pi_type; + pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET; + + /* Data immediately follow per-packet-info. */ + pkt->rm_dataoffset += pi_size; + + /* Update RNDIS packet msg length */ + pkt->rm_len += pi_size; + + return (pi->rm_data); +} + /* * NOTE: * If this function fails, then both txd and m_head0 will be freed. @@ -1636,15 +1700,9 @@ hn_lro_rx(struct lro_ctrl *lc, struct mb } #endif -/* - * Called when we receive a data packet from the "wire" on the - * specified device - * - * Note: This is no longer used as a callback - */ -int +static int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, - const struct hn_recvinfo *info) + const struct hn_rxinfo *info) { struct ifnet *ifp = rxr->hn_ifp; struct mbuf *m_new; @@ -4044,6 +4102,325 @@ hn_resume(struct hn_softc *sc) hn_resume_mgmt(sc); } +static void +hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen) +{ + const struct rndis_status_msg *msg; + int ofs; + + if (dlen < sizeof(*msg)) { + if_printf(sc->hn_ifp, "invalid RNDIS status\n"); + return; + } + msg = data; + + switch (msg->rm_status) { + case RNDIS_STATUS_MEDIA_CONNECT: + case RNDIS_STATUS_MEDIA_DISCONNECT: + hn_link_status_update(sc); + break; + + case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: + /* Not really useful; ignore. */ + break; + + case RNDIS_STATUS_NETWORK_CHANGE: + ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset); + if (dlen < ofs + msg->rm_stbuflen || + msg->rm_stbuflen < sizeof(uint32_t)) { + if_printf(sc->hn_ifp, "network changed\n"); + } else { + uint32_t change; + + memcpy(&change, ((const uint8_t *)msg) + ofs, + sizeof(change)); + if_printf(sc->hn_ifp, "network changed, change %u\n", + change); + } + hn_network_change(sc); + break; + + default: + if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n", + msg->rm_status); + break; + } +} + +static int +hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info) +{ + const struct rndis_pktinfo *pi = info_data; + uint32_t mask = 0; + + while (info_dlen != 0) { + const void *data; + uint32_t dlen; + + if (__predict_false(info_dlen < sizeof(*pi))) + return (EINVAL); + if (__predict_false(info_dlen < pi->rm_size)) + return (EINVAL); + info_dlen -= pi->rm_size; + + if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) + return (EINVAL); + if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) + return (EINVAL); + dlen = pi->rm_size - pi->rm_pktinfooffset; + data = pi->rm_data; + + switch (pi->rm_type) { + case NDIS_PKTINFO_TYPE_VLAN: + if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) + return (EINVAL); + info->vlan_info = *((const uint32_t *)data); + mask |= HN_RXINFO_VLAN; + break; + + case NDIS_PKTINFO_TYPE_CSUM: + if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) + return (EINVAL); + info->csum_info = *((const uint32_t *)data); + mask |= HN_RXINFO_CSUM; + break; + + case HN_NDIS_PKTINFO_TYPE_HASHVAL: + if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) + return (EINVAL); + info->hash_value = *((const uint32_t *)data); + mask |= HN_RXINFO_HASHVAL; + break; + + case HN_NDIS_PKTINFO_TYPE_HASHINF: + if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) + return (EINVAL); + info->hash_info = *((const uint32_t *)data); + mask |= HN_RXINFO_HASHINF; + break; + + default: + goto next; + } + + if (mask == HN_RXINFO_ALL) { + /* All found; done */ + break; + } +next: + pi = (const struct rndis_pktinfo *) + ((const uint8_t *)pi + pi->rm_size); + } + + /* + * Final fixup. + * - If there is no hash value, invalidate the hash info. + */ + if ((mask & HN_RXINFO_HASHVAL) == 0) + info->hash_info = HN_NDIS_HASH_INFO_INVALID; + return (0); +} + +static __inline bool +hn_rndis_check_overlap(int off, int len, int check_off, int check_len) +{ + + if (off < check_off) { + if (__predict_true(off + len <= check_off)) + return (false); + } else if (off > check_off) { + if (__predict_true(check_off + check_len <= off)) + return (false); + } + return (true); +} + +static void +hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen) +{ + const struct rndis_packet_msg *pkt; + struct hn_rxinfo info; + int data_off, pktinfo_off, data_len, pktinfo_len; + + /* + * Check length. + */ + if (__predict_false(dlen < sizeof(*pkt))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); + return; + } + pkt = data; + + if (__predict_false(dlen < pkt->rm_len)) { + if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, " + "dlen %d, msglen %u\n", dlen, pkt->rm_len); + return; + } + if (__predict_false(pkt->rm_len < + pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, " + "msglen %u, data %u, oob %u, pktinfo %u\n", + pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen, + pkt->rm_pktinfolen); + return; + } + if (__predict_false(pkt->rm_datalen == 0)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n"); + return; + } + + /* + * Check offests. + */ +#define IS_OFFSET_INVALID(ofs) \ + ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \ + ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK)) + + /* XXX Hyper-V does not meet data offset alignment requirement */ + if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "data offset %u\n", pkt->rm_dataoffset); + return; + } + if (__predict_false(pkt->rm_oobdataoffset > 0 && + IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob offset %u\n", pkt->rm_oobdataoffset); + return; + } + if (__predict_true(pkt->rm_pktinfooffset > 0) && + __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo offset %u\n", pkt->rm_pktinfooffset); + return; + } + +#undef IS_OFFSET_INVALID + + data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset); + data_len = pkt->rm_datalen; + pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset); + pktinfo_len = pkt->rm_pktinfolen; + + /* + * Check OOB coverage. + */ + if (__predict_false(pkt->rm_oobdatalen != 0)) { + int oob_off, oob_len; + + if_printf(rxr->hn_ifp, "got oobdata\n"); + oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset); + oob_len = pkt->rm_oobdatalen; + + if (__predict_false(oob_off + oob_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overflow, msglen %u, oob abs %d len %d\n", + pkt->rm_len, oob_off, oob_len); + return; + } + + /* + * Check against data. + */ + if (hn_rndis_check_overlap(oob_off, oob_len, + data_off, data_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overlaps data, oob abs %d len %d, " + "data abs %d len %d\n", + oob_off, oob_len, data_off, data_len); + return; + } + + /* + * Check against pktinfo. + */ + if (pktinfo_len != 0 && + hn_rndis_check_overlap(oob_off, oob_len, + pktinfo_off, pktinfo_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overlaps pktinfo, oob abs %d len %d, " + "pktinfo abs %d len %d\n", + oob_off, oob_len, pktinfo_off, pktinfo_len); + return; + } + } + + /* + * Check per-packet-info coverage and find useful per-packet-info. + */ + info.vlan_info = HN_NDIS_VLAN_INFO_INVALID; + info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID; + info.hash_info = HN_NDIS_HASH_INFO_INVALID; + if (__predict_true(pktinfo_len != 0)) { + bool overlap; + int error; + + if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo overflow, msglen %u, " + "pktinfo abs %d len %d\n", + pkt->rm_len, pktinfo_off, pktinfo_len); + return; + } + + /* + * Check packet info coverage. + */ + overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len, + data_off, data_len); + if (__predict_false(overlap)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo overlap data, pktinfo abs %d len %d, " + "data abs %d len %d\n", + pktinfo_off, pktinfo_len, data_off, data_len); + return; + } + + /* + * Find useful per-packet-info. + */ + error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off, + pktinfo_len, &info); + if (__predict_false(error)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg " + "pktinfo\n"); + return; + } + } + + if (__predict_false(data_off + data_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "data overflow, msglen %u, data abs %d len %d\n", + pkt->rm_len, data_off, data_len); + return; + } + hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info); +} + +static __inline void +hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen) +{ + const struct rndis_msghdr *hdr; + + if (__predict_false(dlen < sizeof(*hdr))) { + if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); + return; + } + hdr = data; + + if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) { + /* Hot data path. */ + hn_rndis_rx_data(rxr, data, dlen); + /* Done! */ + return; + } + + if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG) + hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen); + else + hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen); +} + static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt) { @@ -4079,8 +4456,8 @@ hn_nvs_handle_comp(struct hn_softc *sc, } static void -hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr, - struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr) +hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan, + const struct vmbus_chanpkt_hdr *pkthdr) { const struct vmbus_chanpkt_rxbuf *pkt; const struct hn_nvs_hdr *nvs_hdr; @@ -4130,9 +4507,9 @@ hn_nvs_handle_rxbuf(struct hn_softc *sc, "ofs %d, len %d\n", i, ofs, len); continue; } - hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len); + hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len); } - + /* * Moved completion call back here so that all received * messages (not just data messages) will trigger a response @@ -4196,7 +4573,7 @@ hn_chan_callback(struct vmbus_channel *c hn_nvs_handle_comp(sc, chan, pkt); break; case VMBUS_CHANPKT_TYPE_RXBUF: - hn_nvs_handle_rxbuf(sc, rxr, chan, pkt); + hn_nvs_handle_rxbuf(rxr, chan, pkt); break; case VMBUS_CHANPKT_TYPE_INBAND: hn_nvs_handle_notify(sc, pkt); @@ -4233,7 +4610,7 @@ hn_chan_callback(struct vmbus_channel *c if (bufferlen > HN_PKTBUF_LEN) free(buffer, M_DEVBUF); - hv_rf_channel_rollup(rxr, rxr->hn_txr); + hn_chan_rollup(rxr, rxr->hn_txr); } static void Modified: stable/10/sys/dev/hyperv/netvsc/hv_rndis_filter.c ============================================================================== --- stable/10/sys/dev/hyperv/netvsc/hv_rndis_filter.c Fri Nov 11 05:53:58 2016 (r308505) +++ stable/10/sys/dev/hyperv/netvsc/hv_rndis_filter.c Fri Nov 11 06:23:01 2016 (r308506) @@ -29,12 +29,12 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_inet6.h" +#include "opt_inet.h" + #include <sys/param.h> -#include <sys/kernel.h> -#include <sys/mbuf.h> #include <sys/socket.h> -#include <sys/lock.h> -#include <sys/mutex.h> +#include <sys/systm.h> #include <sys/taskqueue.h> #include <machine/atomic.h> @@ -61,16 +61,6 @@ __FBSDID("$FreeBSD$"); #include <dev/hyperv/netvsc/hn_nvs.h> #include <dev/hyperv/netvsc/hv_rndis_filter.h> -#define HV_RF_RECVINFO_VLAN 0x1 -#define HV_RF_RECVINFO_CSUM 0x2 -#define HV_RF_RECVINFO_HASHINF 0x4 -#define HV_RF_RECVINFO_HASHVAL 0x8 -#define HV_RF_RECVINFO_ALL \ - (HV_RF_RECVINFO_VLAN | \ - HV_RF_RECVINFO_CSUM | \ - HV_RF_RECVINFO_HASHINF | \ - HV_RF_RECVINFO_HASHVAL) - #define HN_RNDIS_RID_COMPAT_MASK 0xffff #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK @@ -88,24 +78,23 @@ __FBSDID("$FreeBSD$"); #define HN_NDIS_LSOV2_CAP_IP6 \ (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT) -/* - * Forward declarations - */ -static void hv_rf_receive_indicate_status(struct hn_softc *sc, - const void *data, int dlen); -static void hv_rf_receive_data(struct hn_rx_ring *rxr, - const void *data, int dlen); - -static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, - const void *idata, size_t idlen, void *odata, size_t *odlen0); -static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid, - const void *idata, size_t idlen, void *odata, size_t *odlen0, - size_t min_odlen); -static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, - size_t dlen); -static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu); -static int hn_rndis_query_hwcaps(struct hn_softc *sc, - struct ndis_offload *caps); +static const void *hn_rndis_xact_exec1(struct hn_softc *, + struct vmbus_xact *, size_t, + struct hn_nvs_sendctx *, size_t *); +static const void *hn_rndis_xact_execute(struct hn_softc *, + struct vmbus_xact *, uint32_t, size_t, size_t *, + uint32_t); +static int hn_rndis_query(struct hn_softc *, uint32_t, + const void *, size_t, void *, size_t *); +static int hn_rndis_query2(struct hn_softc *, uint32_t, + const void *, size_t, void *, size_t *, size_t); +static int hn_rndis_set(struct hn_softc *, uint32_t, + const void *, size_t); +static int hn_rndis_init(struct hn_softc *); +static int hn_rndis_halt(struct hn_softc *); +static int hn_rndis_conf_offload(struct hn_softc *, int); +static int hn_rndis_query_hwcaps(struct hn_softc *, + struct ndis_offload *); static __inline uint32_t hn_rndis_rid(struct hn_softc *sc) @@ -121,371 +110,22 @@ again: return ((rid & 0xffff) << 16); } -void * -hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize, - size_t pi_dlen, uint32_t pi_type) -{ - const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen); - struct rndis_pktinfo *pi; - - KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0, - ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen)); - - /* - * Per-packet-info does not move; it only grows. - * - * NOTE: - * rm_pktinfooffset in this phase counts from the beginning - * of rndis_packet_msg. - */ - KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize, - ("%u pktinfo overflows RNDIS packet msg", pi_type)); - pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset + - pkt->rm_pktinfolen); - pkt->rm_pktinfolen += pi_size; - - pi->rm_size = pi_size; - pi->rm_type = pi_type; - pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET; - - /* Data immediately follow per-packet-info. */ - pkt->rm_dataoffset += pi_size; - - /* Update RNDIS packet msg length */ - pkt->rm_len += pi_size; - - return (pi->rm_data); -} - -/* - * RNDIS filter receive indicate status - */ -static void -hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) -{ - const struct rndis_status_msg *msg; - int ofs; - - if (dlen < sizeof(*msg)) { - if_printf(sc->hn_ifp, "invalid RNDIS status\n"); - return; - } - msg = data; - - switch (msg->rm_status) { - case RNDIS_STATUS_MEDIA_CONNECT: - case RNDIS_STATUS_MEDIA_DISCONNECT: - hn_link_status_update(sc); - break; - - case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: - /* Not really useful; ignore. */ - break; - - case RNDIS_STATUS_NETWORK_CHANGE: - ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset); - if (dlen < ofs + msg->rm_stbuflen || - msg->rm_stbuflen < sizeof(uint32_t)) { - if_printf(sc->hn_ifp, "network changed\n"); - } else { - uint32_t change; - - memcpy(&change, ((const uint8_t *)msg) + ofs, - sizeof(change)); - if_printf(sc->hn_ifp, "network changed, change %u\n", - change); - } - hn_network_change(sc); - break; - - default: - /* TODO: */ - if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n", - msg->rm_status); - break; - } -} - -static int -hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info) -{ - const struct rndis_pktinfo *pi = info_data; - uint32_t mask = 0; - - while (info_dlen != 0) { - const void *data; - uint32_t dlen; - - if (__predict_false(info_dlen < sizeof(*pi))) - return (EINVAL); - if (__predict_false(info_dlen < pi->rm_size)) - return (EINVAL); - info_dlen -= pi->rm_size; - - if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) - return (EINVAL); - if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) - return (EINVAL); - dlen = pi->rm_size - pi->rm_pktinfooffset; - data = pi->rm_data; - - switch (pi->rm_type) { - case NDIS_PKTINFO_TYPE_VLAN: - if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) - return (EINVAL); - info->vlan_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_VLAN; - break; - - case NDIS_PKTINFO_TYPE_CSUM: - if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) - return (EINVAL); - info->csum_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_CSUM; - break; - - case HN_NDIS_PKTINFO_TYPE_HASHVAL: - if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) - return (EINVAL); - info->hash_value = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_HASHVAL; - break; - - case HN_NDIS_PKTINFO_TYPE_HASHINF: - if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) - return (EINVAL); - info->hash_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_HASHINF; - break; - - default: - goto next; - } - - if (mask == HV_RF_RECVINFO_ALL) { - /* All found; done */ - break; - } -next: - pi = (const struct rndis_pktinfo *) - ((const uint8_t *)pi + pi->rm_size); - } - - /* - * Final fixup. - * - If there is no hash value, invalidate the hash info. - */ - if ((mask & HV_RF_RECVINFO_HASHVAL) == 0) - info->hash_info = HN_NDIS_HASH_INFO_INVALID; - return (0); -} - -static __inline bool -hn_rndis_check_overlap(int off, int len, int check_off, int check_len) -{ - - if (off < check_off) { - if (__predict_true(off + len <= check_off)) - return (false); - } else if (off > check_off) { - if (__predict_true(check_off + check_len <= off)) - return (false); - } - return (true); -} - -/* - * RNDIS filter receive data - */ -static void -hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen) -{ - const struct rndis_packet_msg *pkt; - struct hn_recvinfo info; - int data_off, pktinfo_off, data_len, pktinfo_len; - - /* - * Check length. - */ - if (__predict_false(dlen < sizeof(*pkt))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); - return; - } - pkt = data; - - if (__predict_false(dlen < pkt->rm_len)) { - if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, " - "dlen %d, msglen %u\n", dlen, pkt->rm_len); - return; - } - if (__predict_false(pkt->rm_len < - pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, " - "msglen %u, data %u, oob %u, pktinfo %u\n", - pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen, - pkt->rm_pktinfolen); - return; - } - if (__predict_false(pkt->rm_datalen == 0)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n"); - return; - } - - /* - * Check offests. - */ -#define IS_OFFSET_INVALID(ofs) \ - ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \ - ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK)) - - /* XXX Hyper-V does not meet data offset alignment requirement */ - if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "data offset %u\n", pkt->rm_dataoffset); - return; - } - if (__predict_false(pkt->rm_oobdataoffset > 0 && - IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob offset %u\n", pkt->rm_oobdataoffset); - return; - } - if (__predict_true(pkt->rm_pktinfooffset > 0) && - __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo offset %u\n", pkt->rm_pktinfooffset); - return; - } - -#undef IS_OFFSET_INVALID - - data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset); - data_len = pkt->rm_datalen; - pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset); - pktinfo_len = pkt->rm_pktinfolen; - - /* - * Check OOB coverage. - */ - if (__predict_false(pkt->rm_oobdatalen != 0)) { - int oob_off, oob_len; - - if_printf(rxr->hn_ifp, "got oobdata\n"); - oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset); - oob_len = pkt->rm_oobdatalen; - - if (__predict_false(oob_off + oob_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overflow, msglen %u, oob abs %d len %d\n", - pkt->rm_len, oob_off, oob_len); - return; - } - - /* - * Check against data. - */ - if (hn_rndis_check_overlap(oob_off, oob_len, - data_off, data_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overlaps data, oob abs %d len %d, " - "data abs %d len %d\n", - oob_off, oob_len, data_off, data_len); - return; - } - - /* - * Check against pktinfo. - */ - if (pktinfo_len != 0 && - hn_rndis_check_overlap(oob_off, oob_len, - pktinfo_off, pktinfo_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overlaps pktinfo, oob abs %d len %d, " - "pktinfo abs %d len %d\n", - oob_off, oob_len, pktinfo_off, pktinfo_len); - return; - } - } - - /* - * Check per-packet-info coverage and find useful per-packet-info. - */ - info.vlan_info = HN_NDIS_VLAN_INFO_INVALID; - info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID; - info.hash_info = HN_NDIS_HASH_INFO_INVALID; - if (__predict_true(pktinfo_len != 0)) { - bool overlap; - int error; - - if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo overflow, msglen %u, " - "pktinfo abs %d len %d\n", - pkt->rm_len, pktinfo_off, pktinfo_len); - return; - } - - /* - * Check packet info coverage. - */ - overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len, - data_off, data_len); - if (__predict_false(overlap)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo overlap data, pktinfo abs %d len %d, " - "data abs %d len %d\n", - pktinfo_off, pktinfo_len, data_off, data_len); - return; - } - - /* - * Find useful per-packet-info. - */ - error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off, - pktinfo_len, &info); - if (__predict_false(error)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg " - "pktinfo\n"); - return; - } - } - - if (__predict_false(data_off + data_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "data overflow, msglen %u, data abs %d len %d\n", - pkt->rm_len, data_off, data_len); - return; - } - hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info); -} - -/* - * RNDIS filter on receive - */ void -hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, - const void *data, int dlen) +hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen) { const struct rndis_comp_hdr *comp; const struct rndis_msghdr *hdr; - if (__predict_false(dlen < sizeof(*hdr))) { - if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); - return; - } + KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n")); hdr = data; switch (hdr->rm_type) { - case REMOTE_NDIS_PACKET_MSG: - hv_rf_receive_data(rxr, data, dlen); - break; - case REMOTE_NDIS_INITIALIZE_CMPLT: case REMOTE_NDIS_QUERY_CMPLT: case REMOTE_NDIS_SET_CMPLT: case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */ if (dlen < sizeof(*comp)) { - if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n"); + if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n"); return; } comp = data; @@ -495,10 +135,6 @@ hv_rf_on_receive(struct hn_softc *sc, st vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen); break; - case REMOTE_NDIS_INDICATE_STATUS_MSG: - hv_rf_receive_indicate_status(sc, data, dlen); - break; - case REMOTE_NDIS_RESET_CMPLT: /* * Reset completed, no rid. @@ -507,11 +143,11 @@ hv_rf_on_receive(struct hn_softc *sc, st * RESET is not issued by hn(4), so this message should * _not_ be observed. */ - if_printf(rxr->hn_ifp, "RESET cmplt received\n"); + if_printf(sc->hn_ifp, "RESET cmplt received\n"); break; default: - if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n", + if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n", hdr->rm_type); break; } @@ -1354,10 +990,3 @@ hn_rndis_detach(struct hn_softc *sc) /* Halt the RNDIS. */ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201611110623.uAB6N1aI062429>